主页 > IT业界  > 

UNIX网络编程学习记录4-第三章

UNIX网络编程学习记录4-第三章
3.2 套接字地址结构 3.2.1 IPv4 套接字地址结构 struct in_addr { in_addr_t s_addr; /* 32位的IPv4地址,需要转换为网络字节的 */ }; struct sock_addr_in { uint8_t sin_len; /* 结构长度 */ sa_family_t sin_family; /* AF_INET 无符号短整型,16位*/ in_port_t sin_port; /* 端口号 16位,需要转化为网络字节的 */ struct in_addr sin_addr; /* 32位的IPv4地址,需要转换为网络字节的 */ char sin_zero[8]; /* unused 长度为8的字符数组,8个字节*/ }; sin_len字段简化了长度可变的套接字地址结构的处理;无需检查和设置该字段,除非涉及路由套接字;地址和端口号在套接字地址结构中总是以网络字节存储的;sin_zero字段总是被设置为0,但前提是设置该字段前,需要把整个结构设置为0;套接字地址结构仅在本机上使用,不在主机之间传递; 3.2.1 套接字地址结构作为参数

当套接字地址结构需要作为参数进行传递时,怎么处理?ANSIC后有一个办法是void *,但套接字的定义是在其之前,所以需要定义一个通用的套接字地址结构:

struct sockaddr { uint8_t sa_len; sa_family_t sa_family; /* 地址族, AF_XXX */ char sa_data[14]; /* 指定协议地址 */ };

为了容纳系统所支持的任何套接字地址结构,新的通用套接字地址结构:

struct sockaddr_storage { uint8_t ss_len; sa_family_t ss_family; }

在套接字地址结构需要作为参数进行传递时,必须要进行类型转换。如:

bind(sockfd, (struct sockaddr *)&serv, sizeof(serv));

否则就会报警告信息:warning :passing arg 2 of "bind" incompatible pointer type

3.3 值-结果参数

参数传递的方向有两个,进程到内核,内核到进程。

进程到内核:这样的函数有3个:bind、connect 、sendto,这些函数的参数一个是指向套接字地址结构的指针,一个是该结构的大小。如: struct sockaddr_in serv; ... connect(sockfd, (SA*)&serv, sizeof(serv));

把指针和指针大小都传递给内核,内核就知道需要从进程复制多少数据进来;

        2.内核到进程:这样的函数有4个:accept、recvfrom、getsockname、getpeername,这些函数的参数一个是指向套接字地址结构的指针,一个是指向该结构大小的变量的指针。如:

struct sockaddr_t cli; socklen_t len; /*套接字地址结构大小的类型实际是socklen_t,不是int,只是POSIX规范建议将其改成uint32_t*/ len = sizeof(cli); getpeername(unixfd, (SA*)&cli, &len);

为什么这里要把套接字地址结构的大小由一个整数值改为指针呢????

因为:当函数被调用时,结构大小是一个value,告诉内核该结构的大小,这样内核在读数据的时候不至于越界;当函数返回结果时,结构大小是一个指针,告诉内核在该结构中存储了多少信息。

3.4 字节排序函数

考虑一个16位整数,由2个字节组成。内存中有两种存储该整数的方法:一种是低序字节存储在起始地址,另一种是高序字节存储在起始地址。

这两种存储方式都有系统在用,那怎么查看当前系统采用的哪种存储方式呢???

#include "unp.h" int main(int argc, char** argv) { union { short s; char c[sizeof(short)]; } un; un.s = 0x0102; /* 在一个短整型变量中存放2字节的值0x0102 */ printf("%s:", CPU_VENDOR_OS); /* 标识CPU类型、厂家、操作系统版本 */ if (ssizeof(short) == 2) { if (un.c[0] == 1 && un.c[1] == 2) { /* 查看它的连续两个字节的 */ printf("big-endian\n") } else if (un.c[0] == 2 && un.c[1] == 1) { printf("little-endian\n") } else { printf("unknown\n"); } } else { printf("sizeof(short) = %d\n", sizeof(short)) } exit(0); }

网际协议使用大端字节序存储。

主机字节序和网络字节序的转换,涉及的函数:

#include <netinet/in.h> /* host to net */ uint16_t htons(uint16_t host16bitvalue); uint32_t htonl(uint32_t host32bitvalue); /* net to host */ uint16_t ntohs(uint16_t net16bitvlaue); uint32_t ntohl(uint32_t net32bitvalue); 3.5 字节操纵函数

以b开头的函数、以mem开头的函数。

如用bzero函数将套接字结构初始化为0;bcopy、bcmp(两个字符串相同则返回0,否则返回非0)。

#include <string.h> void bzero(void *dest, size_t nbytes); void bcopy(const void *src, void *dest, size_t nbytes); int bcmp(const void *src, const *dest, size_t nbytes); #include <string.h> /* 把目标字符串指定数目的字节值设置为c */ void *memset(void *dest, int c, size_t len); /* 与bcopy函数类似,但参数相反 */ /* 当源字节串与目标字节串重叠时,bcopy可以正确处理,但memcpy返回结果不可知 */ /* 这种情况下建议使用memmove函数 */ void *memcpy(void *dest, const void *src, size_t len); int memcmp(const void *ptr1, const void *ptr2, size_t len); 3.6 地址转换函数

点分十进制IPv4地址和32位的网络字节序二进制地址的相互转换

#include <arpa/inet.h> /* 点分十进制转网络字节序二进制,转换成功返回1,否则返回0 */ /* 如果addrptr指针为空,依旧会进行转换,只是不存储结果 */ int inet_aton(const char *strptr, struct in_addr *addrptr); /* 已废弃 */ in_addr_t inet_addr(const char *strptr); /* 网络字节序二进制转点分十进制 */ char *int_ntoa(strcut in_addr *inaddr);

以下两个新的函数对于IPv4和IPv6地址都适用。

#include <arpa/inet.h> /* 地址有效返回1,地址无效返回0,出错返回-1 */ int inet_pton(int family, const char *strptr, void *addrptr); /* 转换成功返回指针,出错返回NULL */ const char *innet_ntop(int family, const *addrptr, char *strptr, size_t len); /* 函数中的p是地址的表达presentation,n是二进制值numeric */ 3.9 读写函数

使用read或者write函数输入或输出套接字数据的时候,可能数据比请求的数据量少,这不是出错,而是内核中用于套接字的缓冲区以达到极限,你可以循环多次调用read/write函数,也可以使用readn/writen代替

ssize_t readn(int filedes, void *buff, size_t nbytes); ssize_t writen(int filedes, const void *buff, size_t nbytes); ssize_t readline(int fileds, void *buff, size_t maxlen);

标签:

UNIX网络编程学习记录4-第三章由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“UNIX网络编程学习记录4-第三章