2024年2月20日发(作者:)

Linux网络数据结构

在网络实际传送的数据中,有两种字节排列顺序:重要的字节在前面,或者不重要的

字节在前面。 前一种叫网络字节顺序(Network Byte Order,NBO),有些机器在内部

是按照这个顺序储存数据的。当某数据必须按照NBO顺 序时,那么要调用函数(例如htons

())将它从本机字节顺序(Host Byte Order,HBO)转换过来,否则传送过去的数据将

使 对方机器不可读。这点对于网络数据传送来说是非常关键的。

在网络中第一个被创造的结构类型是sockaddr。这个数据结构是为许多类 型的套接口

储存地址信息。它的定义如下:

struct sockaddr{

unsigned short sa_family; /*这个是地址族,通常是AF-xxxx的形式*/

char sa_data[14]; /*14字节的地址信息*/

};

sa_family是地址家族,是“AF_xxx”的形式。常设为“AF_INET”,代表Internet(TCP/IP)地址族。

sa_data是协议地址,由sa_family决定。如果sa_family=AF_INET,则sa_data就是sockaddr_in的 sin_addr和sin_port,用于为套接口储存目标地址和端口信息。为了解决struct sockaddr,创造了一个并列的结构struct sockadd_in(“in”代表

“Internet”),换句话说,这时sockaddr可以当作sockaddr_in看。

如下所示:

struct sockaddr_in{

short int sin_family; /*地址族信息,通常是AF-xxxx的形式*/

unsigned short int sin_port; /*端口信息*/

struct in_addr sin_addr; /*网络地址*/

unsigned char sin_zero[8]; /*补位用的0,to make same size as struct sockaddr*/

}

struct in_addr {

unsigned long s_addr;

};

typedef struct in_addr {

union {

struct{

unsigned char s_b1,

s_b2,

s_b3,

s_b4;

} S_un_b;

struct {

unsigned short s_w1,

s_w2;

} S_un_w;

unsigned long S_addr;

} S_un;

} IN_ADDR;

sin_family意义与sa_family同。

sin_port存储端口号(使用网络字节顺序)

sin_addr存储IP地 址,使用in_addr这个数据结构

sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空 字节。

在 in_addr结构体中,s_addr按照网络字节顺序存储IP地址。考试大-全国最大 教育类网站(www.Examda。com)

sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in的指针和指向sockaddr的指针可 以相互转换,这意味着如果一个函数所需参数类型是sockaddr类型时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向 sockaddr的指针;或者相反。总之,sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体 的指针也可以指向sockadd的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,在最后用进行类型转换就可以了。

bzero((char*)&mysock,sizeof(mysock));//初始化

mysock结构体名

_family=AF_INET;

_addr.s_addr=inet_addr("192.168.0.1");

……

等到要做转换的时候用:

(struct sockaddr*)mysock

进行网络编程,使用socket, listen, bind等函数。

你只要记住,填值的时候使用sockaddr_in结构,而作为函数的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。

上 面这个数据结构可以轻松处理套接口地址的基本元素。需要解释的是,sin_zero被

加入到这个结构中主要是为了保证struct sockaddr的数据长度和struct sockaddr_in

的一样,这样在使用标准函数时,就可以使用统一的数据接口。需要注意的是, 应该

使用函数bzero()将sin_zero全部置零。最后,sin_port和sin_addb必须是网络字节

顺序 (Network Byte Order)。如果声明“inadd”是数据结构stmct sockaddr_in的实

例,那么 .s_addr就储存了4个字节的IP地址(网络字节顺序)。

另一个常用到的是unsigned类型。它比上 面介绍的struct sockaddr_in或struct sockaddr

用得更普遍。对于变量类型unsigned,可以使用的两种类 型是short(两个字节)和long

(四个字节)。假设想将short从本机字节顺序转换为网络字节顺序,需用“h”表示本

机 (host),用“to”表示进行转换,然后用“n”表示网络,用“s”表示short,那

么就是h-to-n-s或者htons() (“Host to Network Short”)。

考虑到对不同机器的可移值性,这样的转换是必需的。我们对“n”、“h”、 “s”

和“l”这几个字母进行组合,就可以得到Linux下的全部转换函数。

IP地址在Linux网络中的处理方法:

假设使用struct sockaddr_in ina,想将IP地址“164.112.175.124”储存到其中

, 那么所要做的是调用函数inet_addr(),转换上面“数字 + 句点”格式的IP地址到

unsigned long中。这个工作可以这样来做:

_addr.s_addr=inet_addr(”164.112.175.124”);

inet_addr()返回的地址已经是按照网络字节顺序的,不用调用htonl()。在发生错误

的时 候inet_addr()返回-1。调用后,需使用正确的错误检查,比如说当IP地址为255

.255.255.255的时候,返回的就是 (unsigned)-1。因为这是个广播地址,你的程

序必需能够将这类错误捕获出来。

你现在就可以转换字符串形式的IP地 址为1ong了。若有一个数据结构struct in_addr

,按照“数字+句点”格式打印时,你要用函数inet_ntoa()(ntoa 意思是network to

ascⅡ),如下所示:

printf(“%s”,inet_ntoa(_addr));

这样就可以打印IP地址。注意:函数inet_ntoa()的参数是struct in_addr,而不是

long, 它返回的是一个指向字符的指针。

在inet_ntoa内储存了字符数组,因此它每次调用inet_ntoa()的时候将覆盖以前的内

容。

例如:

Char a1, *a2;

......

a1=inet_ntoa(_addr); /*假设地址是;164.112.175.124*/

a2=inet_ntoa(_addr);/*假设地址 是:202.112.58.200*/

printf(“address 1:%sn”,a1);

printf(“address 2:%sn”,a2);

上面运行结果是:

address l:202.112.58.200

address 2:202.112.58.200