资源预览内容
第1页 / 共30页
第2页 / 共30页
第3页 / 共30页
第4页 / 共30页
第5页 / 共30页
第6页 / 共30页
第7页 / 共30页
第8页 / 共30页
第9页 / 共30页
第10页 / 共30页
亲,该文档总共30页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
Unix网络编程 3rd vol1 读书笔记学习笔记目的此文档记录本人学习Unix Network Programming 3rdverion volumn I的一些笔记,我只将觉得重要或经过一番功夫才理解的内容记录下来,方便以后回顾。第二章 传输层TCP,UDP和SCTP2.10 TCP端口号和并行服务器tcp是通过一对socket(socket pair)来区分socket通讯的,可以这么理解,socket = ip + port,socket pair = client socket + server socket。 所以,当使用一个监听器监听时,一旦accept到一个connect后,可以,将这个socket交给一个线程或进程执行任务,在线程中执行这个任务的同时,主线程仍然可以accept其他client socket的链接。因为,每个tcp connection的socket pair中的client socket是不一样的,所以tcp是可以区分这些连接。下面这个举个列子,如下图所示,客户socket ,简写为CS1= 206.168.112.219:1500,发起了链接,链接到服务socket ,简写为SS = 12.106.32.254:21。连接后,服务器fork一个进程,该进程处理CS1的相关操作。接下来,如图:客户socket,简写为CS2= 206.168.112.219:1501,发起链接,仍然链接SS,此时服务器在fork一个进程处理该链接,此时CS1和SS的链接仍然存在,并且SS还可以监听其他链接。通过以上的描述,你可以知道socket只有在accept的时候会block,accept后只要fork或采用多线程,处理请求,就可以继续监听其他请求,也就是说,监听socket和accept之后的socket是可以同时存在,互相不影响的。我以前误以为监听socket在accept的socket销毁后,才能继续监听,否则会出现混乱,因为他们会同时使用一个端口和一个IP(上面的列子,都是12.106.32.254:21)。出现这个误区的原因是因为我没有理解TCP是根据socket pair区分连接,而不是根据单独的socket。2.11 缓存大小和限制对于同步的socket(blocking socket),write函数的内部如何操作?write函数会将应用程序buffer中的数据写到Tcp buffer,这里会发生一次数据copy,因为tcp需要得到这份数据进行重传。TCP buffer是有大小的,可以通过SO_SNDBUF来设置,如果满了却没有装满应用程序的数据,那么应用程序进程就会sleep,也就是堵塞了,直到应用程序的数据全部copy到tcp buffer中,write程序才会返回(这里针对默认的blocking socket)。所以,可以想一下,如果应用程序每次都传超过SO_SNDBUF的数据,那么每次至少会sleep一次,效率会大打折扣。而且,write返回后,并不意味者客户端接收到了所有数据,他只表明应用程序中的数据全部copy到tcp buffer中,仅此而已,接下来由tcp负责将数据发送给客户端。下图是tcp buffer和应用程序buffer的示意图:第三章 套接字介绍3.2 Socket地址结构每一种协议都有地址结构,这些地址结构都是以“sockaddr_XXX”的形式命名,比如IP v4的地址结构为sockaddr_in,IP v6为sockaddr_in6,链路协议sockaddr_dl等等。IP v4结构定义struct in_addrin_addr_t s_addr; / 32比特的IP v4地址,网络字节序/需要函数将点分十进制的地址转化为该值;struct sockaddr_inuint8_t sin_len; /结构长度,应用程序开发人员无需关系sa_family_t sin_family; /恒为AF_INETin_port_t sin_port; / 16比特TCP或UDP端口,网络字节序struct in_addr sin_addr; / 32比特的IP v4地址,网络字节序char sin_zero8; /没有使用;总结一下:l都是网络字节序l只需要关心16位的port和32位的IP addresslsin_family恒为AF_INET3.3 “值-结果”参数(value-result arguments)值-结果参数的意思就是in-out参数,即作为函数的参数,有作为函数的一部分结果,线面的代码就是值结果参数1. struct sockaddr_un cli; /* Unix domain */2. socklen_t len;3.4. len = sizeof(cli); /* len is a value */5. getpeername(unixfd, (SA *) &cli, &len);6. /* len may have changed */如果cli是sockaddr_in(或sockaddr_in6),那么len恒为为16(或28),而sockadr_un的长度是会变化的,地址结构的可以参见下图:3.4 字节序函数字节序对于单字节数据类型如char来说,无所谓字节数序的问题,但是对于两个或两个以上的数据而言,字节序就很重要,如果字节序不统一,将会出现严重的数据兼容问题。比如short,是由两个字节组成,究竟是高位字节存在内存低位,还是低位字节存在内存低位(前者称为big-endian byte order,后者称为little-endian byte order)。答案是,不确定。不同操作系统自己定义。我们可以做的是保持我们的字节序与环境一致,而不需要关心到底是big-endian还是little-endian。下图表示不同字节序在内存中的结构:网络字节序网络字节序规定数据在网络中传输的字节数序,是统一的,每一个协议实现的厂家必须遵守该字节序,否则无法与其他协议通讯。由于历史原因,网络字节序使用big-endian byte order(高位数据存在内存低位,与手写整数的顺序类似)。本机字节序本机字节序没有统一的规定,不同操作系统厂家会只有自己的本机字节序,下面的代码可以判断当前OS本机字节序。#includeusingnamespacestd;intmain()unionshorts;charbytes2;un;shortsSample = 0x0102;un.s = sSample;if(sizeof(un.s) = 2)if(un.bytes0 = 1 & un.bytes1 = 2)cout big-endian endl;elseif(un.bytes0 = 2 & un.bytes1 = 1)cout little-endian endl;elsecout unknow endl;elsecout size of short is not 2, but sizeof(short) endl;return0;字节序函数在进行网络编程时,我们不需要关心机器字节序和网络字节序到底是little-endian还是big-endian,我们只需要知道数据在当前机器上的进程处理时,需要使用本机字节序,当数据在网络上传递时,需要使用网络字节序。通过下面这四个函数,可以方便的进行本机与网络字节序之间的转换:#include uint16_t htons(uint16_t host_16_bit_value);uint32_t htonl(uint32 _t host_32_bit_value);both return: value in network byte orderuint16_t ntohs(uint16_t net_16_bit_value);uint32_t ntohl(uint32_t net_32_bit_value);both return: value in host byte order记这些函数的技巧:l h代表host,即本机;l n代表net,即网络;l l代表long,即32位;l s代表short,即16位。3.5 字节操作函数Unix下有两组字节操作函数,一组以b(byte)开头,是socket库提供的自己操作函数,一种以mem(memory)开头,由ANSI C提供。#include / 注意这里不是,多了一个svoid bzero(void* dest, size_t nbyte);void bcopy(const void* src, void* dest, size_t nbyte);int bcmp(const void* ptr1,void* ptr2, size_t nbyte); / 返回第一个不想等的byte比较结果/*总结:l 无法修改的都是用const修饰,l 长度都是size_t,指的的src的长度l bzero比较好用,因为只有两个参数*/#include void* memset(void* dest, int c, size_t len);void* memcpy(void* dest, const void* src, size_t nbyte);void* memcmp(const void* ptr1, const void* ptr2, size_t nbytes);/*总结:l memcpy的记忆方式, dest = srcl memset的记忆方式, 长度在最后面l 据说bcopy会正确处理越界(overlap)情况,而memcpy不行,不知如何正确处理*/3.6 函数inet_aton,inet_addr和inet_ntoa这几个函数都是用于点分十进制IP和网络字节序IP相互转换。#include /* 注意:* 1 下面一对函数,相互转换,a代表字符串,n代表网络字节序* 2 没有长度参数,因为点分十进制额字符串长度比较固定,程序可以分析* 3 inet_ntoa接收的参数是值,而不是指针,比较少见,而且返回的值是在静态内容中,* 且运行修改,因为不是const* 4 inet_aton 返回1代表成功转换,0代表失败,可以理解为true和false,* 与一般的0标识成功有点不同*/int inet_aton(const char* strptr, struct in_addr* addrptr);char* inet_ntoa(struct in_addr inaddr);/* 注意:* 此方法过时,因为文档不全,更主要的是对于“255.255.255.255”转换得到结果与错误码相同,有缺陷,所以不建议使用。虽然使用起来,比inet_aton方便,但是隐患较多,所以最好不要使用。*/in_addr_t inet_addr(const
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号