您的位置:首页 > 理论基础 > 计算机网络

网络编程学习——套接字编程(一)

2016-04-09 00:00 435 查看
摘要: 通过《UNIX网络编程卷1:套接字联网API》学习网络编程

1 套接字地址结构

大多数套接字函数需要一个指向套接字地址结构的指针作为参数。每个协议族都定义它自己的套接字地址结构。这些结构的名字均以是sockaddr_开头,并以对应每个协议族的唯一后缀结尾。

1.1 IPv4套接字地址结构

IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中。__SOCKADDR_COMMON宏定义在<bits/sockaddr.h>。sockaddr定义在/usr/include/bits/socket.h中。 当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递,然而以这样的指针作为参数之一的任何套接字函数必须处理来自所支持的任何协议族的套接字地址结构。

[code=plain]/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;

/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc.  */
#define    __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family

/* Structure describing a generic socket address.  */
struct sockaddr
{
__SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
char sa_data[14];        /* Address data.  */
};

typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr; //32位的IPv4地址
};

/* Type to represent a port.  */
typedef uint16_t in_port_t;
/* Structure describing an Internet socket address.  */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);     /* 协议族 */
in_port_t sin_port;            /* Port number. 端口号 */
struct in_addr sin_addr;        /* Internet address. IP地址 */

/* Pad to size of `struct sockaddr'. 用于填充0的字节 */
&nbs
7fe8
p;   unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};


二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别。

sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息。是一种通用的套接字地址。而 sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作。使用sockaddr_in来建立所需的信 息,最后使用类型转化就可以了。下面是一个完整的例子。

1.2 IPv6套接字地址结构

[code=plain]/* IPv6 address */
struct in6_addr
{
union
{
uint8_t    __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;

/* Ditto, for IPv6.  */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port;    /* Transport layer port # */
uint32_t sin6_flowinfo;    /* IPv6 flow information */
struct in6_addr sin6_addr;    /* IPv6 address */
uint32_t sin6_scope_id;    /* IPv6 scope-id */
};


1.3 套接字地址结构的比较

在图1-1中,我们对本书遇到的5种套接字地址结构进行了比较:IPv4、IPv6、Unix域、数据链路和存储。在该图中,我们假设所有套接字地址结构都包含一个单字节的长度字段,地址族字段也用一个字节,其他所有字段都站用确切的最短长度。

前两种套接字地址结构是固定长度的,而Unix域结构和数据结构是可变长的。为了处理长度可变的结构,当我们把指向某个套接字地址结构的指针作为一个参数传递给某个套接字函数时,也把该结构的长度作为另一个参数传递给这个函数。我们在每种长度固定的结构下方给出了这种结构的字节数长度。



图1-1 不同的套接字地址结构

1.4 值-结果参数

套接字结构长度页也作为一个参数来传递,不过其传递方式取决于该结构的传递方向:是从进程到内核,还是内核到进程。

(1)从进程到内核传递套接字地址结构的函数有3个:bind、connect和sendto。这些函数的一个参数是指向某个套接字地址结构的指针,另一个参数是该结构的整数大小。例如,

[code=plain]struct sockaddr_in addr;
connect( sockfd, (struct sockaddr*)&addr, sizeof(addr) );

既然指针和指针所指内容的大小都传递给了内核,于是内核直到到底需要从进程复制多少数据进来。图1-2展示了这个情形。



图1-2 从进程到内核传递套接字地址结构
(2)从内核到进程传递套接字地址结构的函数有4个:accept、recvfrom、getsockname和getpeername。这4个函数的其中两个参数是指向某个套接字地址结构的指针和指向表示该结构大小的整数变量的指针。例如,

[code=plain]struct sockaddr_in addr;
socklen_t len = sizeof(addr);

getpeername( sockfd, (struct sockaddr*)&addr, &len );

把套接字地址结构大小这个参数从一个整数改为指向某个整数变量的指针,其原因在于:当函数被调用时,结构大小是一个值(value),它告诉内核该结构的大小,这样内核在写该结构时不至于越界;当函数返回时,结构大小又是一个结果(result),它告诉进程内核在该结构中究竟存储了多少信息。这种类型的参数称为值-结果(value-result)参数。图1-3展示了这个情形。



图1-3 从内核到进程传递套接字地址结构

1.5 字节排序函数

考虑一个16位整数,它由2个字节组成。内存中存储这两个字节有两种方法:一种是将低字节存储在起始地址,这称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,这称为大端(big-endian)字节序。图1-4展示了这两种格式。



图1-4 16位整数的小端字节序和大端字节序
在该图中,我们在顶部表明内存地址增长的方向为从右到左,在底部标明内存地址增长的方向为从左到右。我们还表明最高有效位(most significant bit,MSB)是这个16位值最左边一位,最低有效位(least significant bit,LSB)是这个16位值最右边一位。

遗憾的是,这两种字节序之间没有标准可循,两种格式都有系统使用。我们把某个给定系统所用的字节序称为主机字节序(host byte order)。网络协议必须指定一个网络字节序(network byte order)。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: