预览加载中,请您耐心等待几秒...
1/10
2/10
3/10
4/10
5/10
6/10
7/10
8/10
9/10
10/10

亲,该文档总共15页,到这已经超出免费预览范围,如果喜欢就直接下载吧~

如果您无法下载资料,请参考说明:

1、部分资料下载需要金币,请确保您的账户上有足够的金币

2、已购买过的文档,再次下载不重复扣费

3、资料包下载后请先用软件解压,在使用对应软件打开

TCPIP网络编程中socket的行为 TCP/IP网络编程中socket的行为 想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1.TCP/IP协议(如连接的建立和终止、重传和确认、滑动窗口和拥塞控制等等) 2.SocketI/O系统调用(重点如read/write),这是TCP/IP协议在应用层表现出来的行为。 3.编写Performant,Scalable的服务器程序。包括多线程、IOMultiplexing、非阻塞、异步等各种技术。 关于TCP/IP协议,建议参考RichardStevens的《TCP/IPIllustrated,vol1》(TCP/IP详解卷1)。 关于第二层面,依然建议RichardStevens的《Unixnetworkproggramming,vol1》(Unix网络编程卷1),这两本书公认是Unix网络编程的圣经。 至于第三个层面,UNP的书中有所提及,也有著名的C10K问题,业界也有各种各样的框架和解决方案,本人才疏学浅,在这里就不一一敷述。 本文的重点在于第二个层面,主要总结一下Linux下TCP/IP网络编程中的read/write系统调用的行为,知识来源于自己网络编程的粗浅经验和对《Unix网络编程卷1》相关章节的总结。由于本人接触Linux下网络编程时间不长,错误和疏漏再所难免,望看官不吝赐教。 一.read/write的语义:为什么会阻塞? 先从write说起: #include<unistd.h> ssize_twrite(intfd,constvoid*buf,size_tcount); 首先,write成功返回,只是buf中的数据被复制到了kernel中的TCP发送缓冲区。至于数据什么时候被发往网络,什么时候被对方主机接收,什么时候被对方进程读取,系统调用层面不会给予任何保证和通知。 write在什么情况下会阻塞?当kernel的该socket的发送缓冲区已满时。对于每个socket,拥有自己的sendbuffer和receivebuffer。从Linux2.6开始,两个缓冲区大小都由系统来自动调节(autotuning),但一般在default和max之间浮动。 #获取socket的发送/接受缓冲区的大小:(后面的值是在我在Linux2.6.38x86_64上测试的结果) sysctlnet.core.wmem_default   #126976 sysctlnet.core.wmem_max  #131071 sysctlnet.core.wmem_default   #126976 sysctlnet.core.wmem_max     #131071 已经发送到网络的数据依然需要暂存在sendbuffer中,只有收到对方的ack后,kernel才从buffer中清除这一部分数据,为后续发送数据腾出空间。接收端将收到的数据暂存在receivebuffer中,自动进行确认。但如果socket所在的进程不及时将数据从receivebuffer中取出,最终导致receivebuffer填满,由于TCP的滑动窗口和拥塞控制,接收端会阻止发送端向其发送数据。这些控制皆发生在TCP/IP栈中,对应用程序是透明的,应用程序继续发送数据,最终导致sendbuffer填满,write调用阻塞。 一般来说,由于接收端进程从socket读数据的速度跟不上发送端进程向socket写数据的速度,最终导致发送端write调用阻塞。 而read调用的行为相对容易理解,从socket的receivebuffer中拷贝数据到应用程序的buffer中。read调用阻塞,通常是发送端的数据没有到达。 二.blocking(默认)和nonblock模式下read/write行为的区别: 将socketfd设置为nonblock(非阻塞)是在服务器编程中常见的做法,采用blockingIO并为每一个client创建一个线程的模式开销巨大且可扩展性不佳(带来大量的切换开销),更为通用的做法是采用线程池+NonblockI/O+Multiplexing(select/poll,以及Linux上特有的epoll)。 //设置一个文件描述符为nonblock intset_nonblocking(intfd) { intflags; if((flags=fcntl(fd,F_GETFL,0))==-1) flags=0; returnfcntl(fd,F_SETFL,flags|O_NONBLOCK); } 几个重要的结论: 1.read总是在接