代码改变世界

Linux Socket学习--域和地址族

2012-08-16 11:53  Rollen Holt  阅读(4509)  评论(0编辑  收藏  举报

先来说说无名套接口吧:

       套接口不一定需要地址,比如函数socketpair就生成了一对相互连接但是没有地址的套接口,这就是所谓的无名套接口。

       有时候也会有这样的情况,在相互连接的两个太接口中有一个套接口不需要地址,例如当连接到一个远程的套接口的时候,虽然必须确定远程套接口的地址,但是发出调用的本地套接口却可以是匿名的。

        有时候虽然需要一个地址进行通信,但是并不关心这个地址具体是什么,这个本地地址仅仅在通信过程中保持有效。如果给他分配一个固定的地址,浪费资源也加重了网络管理的负担,一次地址仅仅在使用的时候才产生。

     

       在一般情况下protocol取值为0,这个使得操作系统能够选择适合所选的domain的正确的缺省协议,当然这条规则也有例外,但是不在我们的讨论之列。

       在上一篇文章中,我们说过AF_LOCAL和AF_UNIX是一样的。下面我们来解释一下:AF_LOCAL中的AF这个前缀表示地址族(address family),domain参数的意思就是在选择到底使用哪个地址族。地址族的作用就是指明使用哪一种地址类型AF_LOCAL表示使用本地地址规则来生成地址,而AF_INET表示使用IP地址规则来生成地址。下面我们来看看通用套接口地址:

 

 

#include <sys.socket.h>

struct sockaddr
{
	sa_family_t sa_family;
	char        sa_data[14];
};

其中sa_family_t是一个无符号短整数,2个字节,整个数据结构的长度为16个字节。

对应于AF_LOCAL(AF_UNIX)的地址的结构名称是sockaddr_un,具体内容如下:

#include <sys.un.h>
struct sockaddr_un
{
	sa_family_t sun_family;
	char        sun_path[108];
};

一些编程者在编写地址内容的时候之前,习惯将结构中所有的字节都清零。这个可以通过调用函数memset来完成:

struct sockaddr_un uaddr;
memset(&uaddr, 0 , sizeof(uaddr))

下面我们来展示一个例子吧:

---------UNDOWN(此处代码稍后添加)

下面我们来说说生成抽象本地地址:

传统的AF_UNIX有一个缺陷,总是有一个文件系统对象生成,这个没有必要也不方便,如果不删除这个文件系统对象,而在调用bind的时候使用同样的名字,就会发生错误。

在Linux内核2.2中,可以对本地套接口生成抽象地址,从而避免生成文件系统对象,这个实现很简单,只需要把路径名的第一个字节设置为空字节,而空字节后的其他部分就是抽象名。例子如下:

---------UNDOWN(此处代码稍后添加)

下面来说说生成Internet套接口地址吧

在Linux中,使用最普遍的地址族就是AF_INET了,具有IPv4套接口地址的套接口可以和TCP/IP上的其他主机进行通信。结构如下:

#include <netinet/in.h>
struct sockaddr_in
{
	sa_family_t sin_family;
	uint16_t sin_port;
	struct in_addr sin_addr;
	unsigned char sin_zero[8];
};

struct in_addr{
	uint32_t s_addr;
}

下面我们简要的介绍一下网络字节序:

对于多字节数据,不同的cpu有不同的组织方式,其中最基本的两种字节序为小端字节序和大端字节序。

小端字节序:是一种将低序字节存储在起始位置的方法。

大端字节序:是一种将高序字节存储在其实位置的方法。

我们可以使用一些函数来简化大端/小端字节序的转化工作,转化的方向有2个:

1.主机字节序到网络字节序

2.网络字节序到主机字节序

下面是一些转换函数:

#include <netinet/in.h>

unsigned long htonl(unsigned long hostlong);
unsigned long htons(unsigned short hostsgort);
unsigned long ntohl(unsigned long netlong);
unsigned short ntohl(unsigned short netshort);

      下面我们就准备生成一个统配的internet地址,之所以要生成统配地址,是因为有的主机安装了多个网卡,每个网卡有独立的ip地址,而且Linux容许每一个网卡指定不同的ip地址,这个时候,如果我们指定统配地址,那么系统就会自动选择一条通往远程服务的路由,当连接建立的时候,内核最终将决定使用哪一个本地的套接口地址。

      当想让内核自动分配本地端口号的时候,也可以使用统配的端口号,要实现这一点很简单,只需要将sinp_port的值设置为0.

下面的代码展示了如何初始化一个具有统配IP地址和统配端口号的AF_INET地址:

struct sockaddr_in addr_inet;
int adr_len;

memset(&addr_inet,0,sizeof(addr_inet));

addr_inet.sin_family=AF_INET;
addr_inet.sin_port=ntohs(0);
addr_inet.sin_addr.a_addr=ntohl(INADDR_ANY);
adr_len=sizeof(addr_inet); 

另外一个普遍使用的ip地址是127.0.0.1,这个是一个回送设备,通过这个地址,我们的进程就可以与同一台主机上的其他进程进行通信。现在们只需要注意这个地址是怎么分配的:

addr_inet.sin_addr.a_addr=ntohl(INADDR_LOOPBACK);

下面我们来看一个例子:初始化一个特定的Internet地址:

---------UNDOWN(此处代码稍后添加)

下面我们来看看生成X.25地址:

套接口界面容许编程者使用Linux支持的其他一些协议进行编程,X.25生成的地址和他非常的类似,他的结构如下:

#include <linux/x25.h>

struct sockaddr_x25{
	sa_familt_t sx25_addr[16];
	x25_address sx25_addr;
};

typedef struct{
	char x25_addr[16];
}x25_address;

下面的这个例子展示了如何使用X25:

---------UNDOWN(此处代码稍后添加)

 

本文章并不打算对于Linux支持的所有的地址族内容进行介绍,实际上Linux所支持的协议也在增长中,所以此处简要的介绍一下,感兴趣的朋友可以自行搜索先关文献。

除了IPv4之外,Linux至少还支持一下三类地址族协议:

AF_INET6-------------IPv6

AF_AX25--------------业余无线电X.25协议

AF_APPLETALK-----------Linux下AppleTalk协议的实现。

如果你想使用这些协议进行编程,那么在编译内核的时候一定要选中相应的协议,请编程者注意,有些协议并没有完全实现,而非完全实现或者实验性的协议是不健壮的,有时候设置会导致整个系统奔溃。

在本文的最后,我们简要的了解一下AF_UNSPEC地址族。

宏AF_UNSPEC代表不确定的地址族,当程序工作于不同的协议和地址族的时候,我们需要一种手段来达到整个目的,例如下面的代码表示的是一个具有不同地址族的联合:

union{
	sockaddr sa;
	sockaddr_un un;
	sockaddr_in in;
	sockaddr_in6 in6;
}u;

在对她进行赋值之前,我们需要把这个联合初始化为:

u.sa_family=AF_UNSPEC;

以后在程序中,如果我们使用地址族AF_INET,则可以再次赋值:

u。sa_family=AF_INET;

所以说AF_UNSPEC是一个安全的占位符。当还不确定具体的地址族之前,我们可以使用他。