Akawa

ETY001的博客

<urlopen error [Errno -2] Name or service not known>报错,
原因是域名解析有问题。

注意:

该文转载出处已不可访问,找到一篇相似的文章,可以参考:
https://blog.csdn.net/liuchonge/article/details/50061331

– 2019-11-28 更新


转自:http://www.devdiv.com/chunked_http_c_-article-2473-1.html

今天的主要内容还是不会偏离帖子的标题,关于HTTP采用chunked方式传输数据的解码问题。相信“在座”的各位有不少是搞过web系统的哈,但考虑到其他没接触过的XDJMs,这里还是简要的介绍一下:

chunked编码的基本方法是将大块数据分解成多块小数据,每块都可以自指定长度,其具体格式RFC文档中是这样描述的(http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html):

1
2
3
4
5
6
7
8
9
10
11
12
13
Chunked-Body   = *chunk
last-chunk
trailer
CRLF
chunk = chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
chunk-size = 1*HEX
last-chunk = 1*("0") [ chunk-extension ] CRLF
chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)
trailer = *(entity-header CRLF)

这个据说是BNF文法,我本人有点陌生,于是又去维基百科里面找了下,里面有报文举例,这样就一目了然了(http://en.wikipedia.org/wiki/Chunked_transfer_encoding),我摘一段报文如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con
8
sequence
0

总而言之呢,就是说,这种方式的HTTP响应,头字段中不会带上我们常见的Content-Length字段,而是带上了Transfer-Encoding: chunked的字样。这种响应是未知长度的,有很多段自定义长度的块组合而成,每个块以一个十六进制数开头,该数字表示这块chunk数据的长度(包括数据段末尾的CRLF,但不包括十六进制数后面的CRLF)。

于是,众多Coders在发现了这个真相以后就开始在互联网上共享各种语言的解码代码。我看了C、PHP和Python那几个版本的代码,发现了一个问题就是,他们解析的数据是完整的,也就是说,他们所操纵的数据是假定已经在解码前在外部完成了拼装的,但是这完全不符合我的使用场景,Why?因为我的数据都是直接从Socket里面拿出来的,Socket里面的数据绝对不会有如此漂亮的格式,它们在那个时候都是散装的,当然我也可以选择将他们组装好然后再去解,但是以我粗浅的认识认为,那样子无论是从时间还是从空间的效率上来讲都是极为低下的(当你开发了一个kext程序,就明白我的苦衷了)。于是我又继续搜索,以期待能有高手已经提前帮我解决了这些问题,不过很遗憾,我没能找到。

没办法,自己做吧,比较重要的地方无非就是一个结尾的判断、一个chunk长度的读取、chunk之间的分段问题。看起来貌似比较轻松,不过代码写起来还是花费了不少时间的,今天又单独从项目中提取了这部分功能用C重写了一下。接下来就结合部分代码来说明一下整个过程。

  1. 先看dechunk.h这个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
#define DCE_OK              0
#define DCE_ISRUNNING 1
#define DCE_FORMAT 2
#define DCE_ARGUMENT 3
#define DCE_MEM 4
#define DCE_LOCK 5

int dechunk_init();
int dechunk(void *input, size_t inlen);
int dechunk_getbuff(void **buff, size_t *buf_size);
int dechunk_free();

void *memstr(void *src, size_t src_len, char *sub);

宏定义就不用说了,都是一些错误码定义。函数一共有5个。dechunk_init、dechunk、dechunk_free这三个是解码的主要流程,dechunk_getbuff则是获取数据的接口。接下来看memstr,这是个很奇怪的名字,也是代码中唯一值得重点提醒一下的地方,其主要功能是在一块内存中寻找能匹配sub表示的字符串的地址。有人肯定要问了,不是有strstr么?对,我也这样想过,并且对于一些chunked网站也是实用的,但是,它不是通用的。主要是因为还有一些网站不仅使用了chunked传输方式,还采用了gzip的内容编码方式。当你碰到这种网站的时候,你再想使用strstr就等着郁闷吧,因为strstr会以字符串中的’\0’字符作为结尾标识,而恰巧经过gzip编码后的数据会有大量的这种字符。

  1. 接下来看dechunk.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    int dechunk(void *input, size_t inlen)
    {
    if (!g_is_running)
    {
    return DCE_LOCK;
    }

    if (NULL == input || inlen <= 0)
    {
    return DCE_ARGUMENT;
    }

    void *data_start = input;
    size_t data_len = inlen;

    if (g_is_first)
    {
    data_start = memstr(data_start, data_len, "\r\n\r\n");
    if (NULL == data_start)
    {
    return DCE_FORMAT;
    }

    data_start += 4;
    data_len -= (data_start - input);

    g_is_first = 0;
    }

    if (!g_is_chunkbegin)
    {
    char *stmp = data_start;
    int itmp = 0;

    sscanf(stmp, "%x", &itmp;);
    itmp = (itmp > 0 ? itmp - 2 : itmp); // exclude the terminate "\r\n"

    data_start = memstr(stmp, data_len, "\r\n");
    data_start += 2; // strlen("\r\n")

    data_len -= (data_start - (void *)stmp);
    g_chunk_len = itmp;
    g_buff_outlen += g_chunk_len;
    g_is_chunkbegin = 1;
    g_chunk_read = 0;

    if (g_chunk_len > 0 && 0 != g_buff_outlen)
    {
    if (NULL == g_buff_out)
    {
    g_buff_out = (char *)malloc(g_buff_outlen);
    g_buff_pt = g_buff_out;
    }
    else
    {
    g_buff_out = realloc(g_buff_out, g_buff_outlen);
    }

    if (NULL == g_buff_out)
    {
    return DCE_MEM;
    }
    }
    }

    #define CHUNK_INIT() \
    do \
    { \
    g_is_chunkbegin = 0; \
    g_chunk_len = 0; \
    g_chunk_read = 0; \
    } while (0)

    if (g_chunk_read < g_chunk_len)
    {
    size_t cpsize = DC_MIN(g_chunk_len - g_chunk_read, data_len);
    memcpy(g_buff_pt, data_start, cpsize);

    g_buff_pt += cpsize;
    g_chunk_read += cpsize;
    data_len -= (cpsize + 2);
    data_start += (cpsize + 2);

    if (g_chunk_read >= g_chunk_len)
    {
    CHUNK_INIT();

    if (data_len > 0)
    {
    return dechunk(data_start, data_len);
    }
    }
    }
    else
    {
    CHUNK_INIT();
    }

    #undef CHUNK_INIT()

    return DCE_OK;
    }

其他函数没什么好说的,主要就只是把dechunk这个函数的流程讲一下(本来要是写了注释,我就不啰嗦这么多了,没办法,我们还是要对自己写的代码负责的不是)。

首先判断是否初始化过,全局变量g_is_running的唯一用途就只是用来防止多线程的调用,这只是一种很低级的保护,大家可以在实际使用中仁者见仁,智者见智。接下来,判断是否是http响应的第一个包,因为第一个包中包含有http的相应头,我们必须把这部分内容给过滤掉,判断的依据就是寻找两个连续的CRLF,也就是”\r\n\r\n”。
响应body的第一行,毫无疑问是第一个chunk的size字段,读取出来,设置状态,设置计数器,分配内存(如果不是第一个chunk的时候,通过realloc方法动态改变我们所分配的内存)。紧接着,就是一个对数据完整性的判断,如果input中的剩余数据的大小比我们还未读取到缓冲区中的chunk的size要小,这很明显说明了这个chunk分成了好几次被收到,那么我们直接按顺序拷贝到我们的chunk缓冲区中即可。反之,如果input中的剩余数据比未读取的size大,则说明了当前这个input数据包含了不止一个chunk,此时,使用了一个递归来保证把数据读取完。这里值得注意的一点是读取数据的时候要把表示数据结束的CRLF字符忽略掉。
总的流程基本就是这个样子,外部调用者通过循环把socket获取到的数据丢进来dechunk,外部循环结束条件就是socket接受完数据或者判断到表示chunk结束的0数据chunk。

  1. 最后看一下main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
int main (int argc, const char * argv[])
{
const char *server_ipstr = "59.151.32.20"; // the host address of "www.mtime.com"
// you can also use function gethostbyname
// to get the address
const unsigned short server_port = 80; // default port of http protocol

int sock_http_get = -1;

do
{
sock_http_get = socket(PF_INET, SOCK_STREAM, 0);

if (-1 == sock_http_get)
{
printf("socket() error %d.\n", errno);
break;
}

struct sockaddr_in addr_server;
bzero(&addr;_server, sizeof(addr_server));

addr_server.sin_family = AF_INET;
addr_server.sin_addr.s_addr = inet_addr(server_ipstr);
addr_server.sin_port = htons(server_port);

if (-1 == connect(sock_http_get, (struct sockaddr *)&addr;_server, sizeof(addr_server)))
{
printf("connect() error %d.\n", errno);
break;
}

printf("connected...\n");

char *request =
"GET / HTTP/1.1\r\n"
"Host: www.mtime.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Encoding: gzip,deflate,sdch\r\n"
"Accept-Language: zh-CN,zh;q=0.8\r\n"
"Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3\r\n"
"Cookie: DefaultCity-CookieKey=561; DefaultDistrict-CookieKey=0; _userCode_=2011725182104293; _userIdentity_=2011725182109188; _movies_=105799.87871.91873.68867; __utma=196937584.1484842614.1299113024.1314613017.1315205344.4; __utmz=196937584.1299113024.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _urm=0%7C0%7C0%7C0%7C0; _urmt=Mon%2C%2005%20Sep%202011%2006%3A49%3A04%20GMT\r\n"
"\r\n";

if (-1 == send(sock_http_get, request, strlen(request), 0))
{
printf("send() error %d.\n", errno);
break;
}

#define MY_BUF_SIZE 1024
char buff[MY_BUF_SIZE] = {0};
int recv_bytes = 0;
int chunkret = DCE_OK;

if (DCE_OK == dechunk_init())
{
while (-1 != (recv_bytes = recv(sock_http_get, buff, MY_BUF_SIZE, 0))
&& 0 != recv_bytes)
{
printf("%s", buff);
if (DCE_OK != (chunkret = dechunk(buff, recv_bytes)))
{
printf("\nchunkret = %d\n", chunkret);
break;
}

if (NULL != memstr(buff, recv_bytes, "\r\n0\r\n"))
{
break;
}

bzero(buff, MY_BUF_SIZE);
}

printf("\n*********************************\n");
printf("receive finished.\n");
printf("*********************************\n");

void *zipbuf = NULL;
size_t zipsize = 0;
dechunk_getbuff(&zipbuf;, &zipsize;);

printf("\n%s\n", (char *)zipbuf);

z_stream strm;
bzero(&strm;, sizeof(strm));

printf("BEGIN:decompress...\n");
printf("*********************************\n");

if (Z_OK == inflateInit2(&strm;, 31)) // 31:decompress gzip
{
strm.next_in = zipbuf;
strm.avail_in = zipsize;

char zbuff[MY_BUF_SIZE] = {0};

do
{
bzero(zbuff, MY_BUF_SIZE);
strm.next_out = (Bytef *)zbuff;
strm.avail_out = MY_BUF_SIZE;

int zlibret = inflate(&strm;, Z_NO_FLUSH);

if (zlibret != Z_OK && zlibret != Z_STREAM_END)
{
printf("\ninflate ret = %d\n", zlibret);
break;
}

printf("%s", zbuff);

} while (strm.avail_out == 0);
}

printf("\n");
printf("*********************************\n");

dechunk_free();
}
#undef MY_BUF_SIZE

} while (0);

close(sock_http_get);

return 0;
}

按照惯例,main函数是我们用来测试的函数。

这个函数中,我们首先使用socket创建了跟服务器之间的连接,紧接着我手动构造了一个请求报文,通过socket发送出去,然后循环获取数据。然后通过使用zlib库来对dechunk出来的数据进行解码以确定数据是否正确。关于zlib的使用跟本次讨论的话题不太沾边,这里就不详述,有兴趣的我们可以另行讨论。

解码前和解码后的数据都会被打印到控制台中去,日志比较庞大,这里就不给出具体信息了,大家可以自行调试观察。
关于这个文件我说明一下,网站选的是www.mtime.com,因为它采用的就是chunked + gzip的方式,是一种相对难处理的数据。该网站的IP地址信息,相信各位有不下于100种方法去找到,所以我就没有使用gethostbyname那个方法了,因为那个方法返回的结构体使用起来实在不怎么方便。另外就是关于手动拼装的请求报文哪里来,千万不要告诉我你去用各种专业抓包工具去抓。没那么麻烦,打开你的Chrome,右键选择审查元素,然后访问你要访问的网站,OK,所有请求都会被记录在案。

/**分割一下吧/

关于代码就说这么多,不过我还是声明一下,因为没多少时间,所以我只能尽力把代码写的不那么难看,注释不多,但我在这里也有所弥补,各个函数功能的实现也许有效率方面的问题抑或是各种bug,这个属于我的编程能力不足,大家可以提出宝贵的修改意见,我一定虚心接受。如果有不明白的地方(我这只是以防万一哈^_^)也可以跟帖一起讨论。根据设计的原则,每个函数的功能应该尽量的单一和完整,调用者应该确保传递的数据符合函数的要求,但是我的main函数中却没有一些必要的判断(比如http响应是否是chunked的,是否是gzip的),因为我准备的测试数据已经确定好了的。

写了那么多,也该结尾了,不过还是要送上源码工程以祝大家天天晚上睡好觉!
源码在这里 —>
Vincent,如果您要查看本帖隐藏内容请回复

doors.du说:“我觉得明天起床这个帖子会火的,不管你们信不信,我反正信了……”

http_chunk_demo.zip (19.35 KB, 下载次数: 2742)

首先生成必要的密钥文件,需要四步:

1、openssl genrsa -des3 -out server.key 1024
2、openssl req -new -key server.key -out server.csr
3、openssl rsa -in server.key -out server_nopass.key
4、openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

执行以上四步的时候,把目录切到nginx的配置文件目录下面,然后再新建一个ssl的目录,便于管理。比如我的目录是/etc/nginx/ssl。

关于nginx的配置,贴一份我自己的:

server {
    listen          443 ssl;
    server_name     localhost;
    ### SSL cert files ###
    ssl on;
    ssl_certificate      ssl/server.crt;
    ssl_certificate_key  ssl/server_nopass.key;
    keepalive_timeout    60;
    index index.html index.htm index.php;
    root /home/ety001/wwwroot/localhost;
    location ~ \.php$ {
        fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        include        fastcgi.conf;
    }
}

1
2
3
4
5
6
7
def _importCls(self,model,action,params):
obj = __import__(model)
entrance = getattr(obj,action)
if params == '':
entrance()
else:
entrance(params)

今天在调一个命令的时候遇到的问题,有一个capture程序,有一个处理程序(就叫做abc吧),执行命令差不多类似于下面这样:
capture -r 1 -f pipe:1 | abc -v 5 -r 3 > /dev/null 2> /dev/null &
但是执行的时候,有时候成功有时候失败,失败的时候,abc报错内容大致就是没有收到capture程序的标准输出的数据。
网上查了下重定向和管道,发现了一篇文章,讲两者的区别,里面有个例子跟我这个类似,但是例子中多加了个括号,于是模仿一下,执行就成功了。
(capture -r 1 -f pipe:1 | abc -v 5 -r 3) > /dev/null 2> /dev/null &
于是猜测可能原因是重定向导致abc拿不到capture的数据了。

参考文章:http://www.cnblogs.com/chengmo/archive/2010/10/21/1856577.html

MAC地址规则

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/herren0167-logs/221441607.html

MAC 地址第二位数字必须是偶数。以下是详细介绍。

MAC 地 址通常表示为12 个16 进制数,每2 个16 进制数之间用冒号隔开,如:08:00:20:0A:8C:6D 就是一个MAC 地址,其中前6 位16 进制数 08:00:20 代表网络硬件制造商的编号,它由IEEE 分配,而后3 位16 进制数0A:8C:6D 代表该制造商所制造的某个网络产品(如网卡)的系列 号。每个网络制造商必须确保它所制造的每个以太网设备都具有相同的前三字节以及不同的后三个字节。这样就可保证世界上每个以太网设备都具有唯一的MAC 地 址。

阅读全文 »

一、字节序
来自:http://ayazh.gjjblog.com/archives/1058846/

谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢?

 其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。

 用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Big Endian

低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&,见鬼去吧 `-_-|||`

 为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

 无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。

big endian:最高字节在地址最低位,最低字节在地址最高位,依次排列。
little endian:最低字节在最低位,最高字节在最高位,反序排列。

endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。咱们接触到的物理单元最小都是byte,在通信领域中,这里往往是bit,不过原理也是类似的。

一个例子:
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为

1
2
3
4
5
                big-endian     little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12

目前应该little endian是主流,因为在数据类型转换的时候(尤其是指针转换)不用考虑地址问题。

二、Big Endian 和 Little Endian名词的由来
这两个术语来自于 Jonathan Swift 的《《格利佛游记》其中交战的两个派别无法就应该从哪一端--小端还是大端--打开一个半熟的鸡蛋达成一致。:)
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。

在那个时代,Swift是在讽刺英国和法国之间的持续冲突,Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了

三、Big Endian 和 Little Endian优劣
来自:Dr. William T. Verts, April 19, 1996
Big Endian
判别一个数的正负很容易,只要取offset0处的一个字节就能确认。
Little Endian
长度为1,2,4字节的数,排列方式都是一样的,数据类型转换非常方便。

四、一些常见文件的字节序
来自:Dr. William T. Verts, April 19, 1996

Common file formats and their endian order are as follows:
Adobe Photoshop – Big Endian
BMP (Windows and OS/2 Bitmaps) – Little Endian
DXF (AutoCad) – Variable
GIF – Little Endian
IMG (GEM Raster) – Big Endian
JPEG – Big Endian
FLI (Autodesk Animator) – Little Endian
MacPaint – Big Endian
PCX (PC Paintbrush) – Little Endian
PostScript – Not Applicable (text!)
POV (Persistence of Vision ray-tracer) – Not Applicable (text!)
QTM (Quicktime Movies) – Little Endian (on a Mac!) (PeterLee注Big Endian in my opinion)
Microsoft RIFF (.WAV & .AVI) – Both
Microsoft RTF (Rich Text Format) – Little Endian
SGI (Silicon Graphics) – Big Endian
Sun Raster – Big Endian
TGA (Targa) – Little Endian
TIFF – Both, Endian identifier encoded into file
WPG (WordPerfect Graphics Metafile) – Big Endian (on a PC!)
XWD (X Window Dump) – Both, Endian identifier encoded into file

五、比特序
来自:http://ayazh.gjjblog.com/archives/1058846/

我在8月9号的《Big Endian和Little Endian》一文中谈了字节序的问题。可是有朋友仍然会问,CPU存储一个字节的数据时其字节内的8个比特之间的顺序是否也有big endian和little endian之分?或者说是否有比特序的不同?

 实际上,这个比特序是同样存在的。下面以数字0xB4(10110100)用图加以说明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Big Endian

msb lsb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

lsb msb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 实际上,由于CPU存储数据操作的最小单位是一个字节,其内部的比特序是什么样对我们的程序来说是一个黑盒子。也就是说,你给我一个指向0xB4这个数的指针,对于big endian方式的CPU来说,它是从左往右依次读取这个数的8个比特;而对于little endian方式的CPU来说,则正好相反,是从右往左依次读取这个数的8个比特。而我们的程序通过这个指针访问后得到的数就是0xB4,字节内部的比特序对于程序来说是不可见的,其实这点对于单机上的字节序来说也是一样的。

 那可能有人又会问,如果是网络传输呢?会不会出问题?是不是也要通过什么函数转换一下比特序?嗯,这个问题提得很好。假设little endian方式的CPU要传给big endian方式CPU一个字节的话,其本身在传输之前会在本地就读出这个8比特的数,然后再按照网络字节序的顺序来传输这8个比特,这样的话到了接收端不会出现任何问题。而假如要传输一个32比特的数的话,由于这个数在littel endian方存储时占了4个字节,而网络传输是以字节为单位进行的,little endian方的CPU读出第一个字节后发送,实际上这个字节是原数的LSB,到了接收方反倒成了MSB从而发生混乱。

转自:http://www.cnblogs.com/chanruida/archive/2011/04/19/invocation.html

调用一个函数将会挂起当前函数的执行,并传递控制权与参数给新的函数。 除了声明的参数,每个函数会接收到两个新的附加参数:this和arguments。 this是个很重要的参数,并且它的值是由调用模式决定的。

以下是JavaScript中很重要的4个调用模式:

a. 方法调用模式the method invocation pattern

b. 函数调用模式the function invocation pattern

c. 构造器调用模式the constructor invocation pattern

d. Apply调用模式the apply invocation pattern

阅读全文 »

今天,在所里跟同事同时登陆vpn,测试能不能互访,结果成功了,但是晚上,都回家后,再测试却不成功,tracert一下后,发现第一跳路由是WAN口的IP,显然就是这里的问题了,在PPP项目下,修改profiles配置中的localAddress为LAN口IP,问题即可解决。

近期做cobbler服务器,由于就一块网卡,前几天折腾了一个usb的网卡的驱动,也算是折腾好了,再后来忘记什么原因了,就又回到了单网卡,做虚拟ip的方法,但是出现的情况是虚拟网卡做完后,上不了网,因为虚拟出来的ip段是没法上网的,估计是电脑走的网关错了,route -n看了下,果然,把虚拟网卡配置文件中的GATEWAY去掉,重启网络就可以了,另外附上在解决这个问题时找到的关于做NAT的方法。

方法:

提示: 以下方法只适用于红帽企业版Linux 3 以上。

1、打开包转发功能:

echo “1” > /proc/sys/net/ipv4/ip_forward

2、修改/etc/sysctl.conf文件,让包转发功能在系统启动时自动生效:

Controls IP packet forwarding

net.ipv4.ip_forward = 1

3、打开iptables的NAT功能:

/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

说明:上面的语句中eth0是连接外网或者连接Internet的网卡. 执行下面的命令,保存iptables的规则: service iptables save

4、查看路由表:

netstat -rn 或 route -n

5、查看iptables规则:

iptables -L

查看nat表

iptables -t nat -vnL POSTROUTING –line-number

0%