linux网络编程手记

上传人:博****1 文档编号:498255819 上传时间:2022-11-13 格式:DOC 页数:10 大小:128.50KB
返回 下载 相关 举报
linux网络编程手记_第1页
第1页 / 共10页
linux网络编程手记_第2页
第2页 / 共10页
linux网络编程手记_第3页
第3页 / 共10页
linux网络编程手记_第4页
第4页 / 共10页
linux网络编程手记_第5页
第5页 / 共10页
点击查看更多>>
资源描述

《linux网络编程手记》由会员分享,可在线阅读,更多相关《linux网络编程手记(10页珍藏版)》请在金锄头文库上搜索。

1、word做linux下的网络编程有一段时间了,中间遇到过很多问题,其中不少是因为自己对网络编程和网络协议的一些根本概念搞不清楚,趁着今天没心情干活就把自己在网络编程方面的理解和一些经验总结一下,Request For ments。在诸多的网络协议中接触的最多也最严密的无疑是TCP和UDP,SCTP之前因为项目原因也研究过,不过最终由于方案修改给抛弃了,TCP年代已经很久远,在网上的资料也非常多,而且我感觉它是一种非常复杂的协议,感觉要把编好基于TCP的程序光简单地了解几个socket API是不够的,刚开始接触网络编程的时候自己确实也吃了不少苦头,后来我还专门拿时间出来阅读了一下RFC,再加上

2、长时间的实践总算也对TCP有所了解,把自己的一些经验和教训都总结一下。首先说一下TCP的状态转移图,这个应该是很重要的,了解TCP运行周期的各种状态才能更好地运用netstat之类的应用程序去对程序进展调试,我这里收藏了一X图,是TCP的状态图,记不清是从哪里找来的,也不知道直接该给谁,但这X图应该最终是出自于UNP第一卷的,那copyright就是UNP了吧。连接建立的几个状态没什么可说的,TCP的三次握手众所周知,更重要的是TCP连接中止的几个状态,应该可以说是连接中止需要四次握手吧。当Client调用close函数主动关闭socket时,连接状态被标记为FIN_WAIT_1,Server

3、在收到FIN之后read函数会返回0,这里server知道Client已经关闭连接,回复ACK,这里client连接状态被标记为FIN_WAIT_2,接下来Server调用close函数关闭连接,这时候Server向client发送FIN,Client收到之后将状态标记为TIME_WAIT,并回复ACK。TIME_WAIT这个状态存在的意义在于Client回复的ACK未必会被Server收到,可能在传输过程中导致包的丢失,而这里Server未收到ACK之后会重新向Client发送FIN,如果client未将状态标记为TIME_WAIT而是直接标记为CLOSED,如此Server发送的FIN会直

4、接收到RST,导致Server端的发送错误,因此Client需要保证有一个TIME_WAIT状态,而这个状态会持续两位的MSL(最大段生命周期),从而保证Server成功发送FIN并发送ACK,为了保证两个数据段传输的最大时间,因此TIME_WAIT持续的时间为两倍的MSL。Server在收到第一个FIN之后会将状态标记为CLOSE_WAIT,此时是client主动关闭连接,这里Server也需要调用Close给Client发送FIN如上所述,之后Server的状态标记为LAST_ACK,表示Server正在等待Client发送的最后一个ACK,当Server收到最后一个ACK便会将连接标记为

5、CLOSED,这时连接完毕。TIME_WAIT这个状态和套接字的SO_REUSEADDR选项是有关系的,这个留做后面讨论。TCP连接异常分为很多种情况,无论是客户端程序还是服务器端程序都需要考虑周全的。Server在连接的过程中程序崩溃或者CTRL+C中止程序,或者kill接Server进程。这时会导致Server立即发送一个FIN数据包给Client,Client如果此时正在调用recv函数,如此recv函数返回0,表示服务器已关闭连接,如果Client调用send函数继续向Server发送数据,Server在收到后会回复RST,而此时send方法会触发SIGPIPE信号,表示通信管道已断开

6、,在程序中如果对该信号不做处理如此会导致程序的崩溃,一般在程序开始时会忽略此信号,如此在这种情况下send函数会返回-1,表示发送失败,处理SIGPIPE的代码如下:前几天实验室这个破项目非要加上什么流媒体的功能,简单起见使用了VLC来实现,客户端这边就得需要把相关的播放界面整合到现有的界面里面来,之前的客户端UI我都是用GTK实现的,没方法,GTK用得比拟多,相对熟练一些就用GTK来做了,没想到要把VLC整到GTK里面来那么麻烦,原生的libvlc是不支持GTK的,需要加一层libvlc-gtk,从网上好不容易下载到了libvlc-gtk的源码,从哪里下的也记不清了,反正就是零散地几个文件,

7、没有README甚至连Makefile都没有,没方法首先得先写个Makefile把它编译一下,libvlc-gtk一共有八个文件,Makefile如下:struct sigaction sa;sa.sa_handler= SIG_IGN;sigaction(SIGPIPE,&sa,0);另外在这种情况下select函数也会立即返回,socket描述符会被设置,而试图从该socket中recv数据,如此会返回-1。另外一种情况是Server系统崩溃或者网络直接异常或断开,这时候Server不可能再给Client发送FIN包,而Client调用send函数后会导致数据包一直重传直接超时后返回-1,

8、而recv函数也会一直阻塞直接超时后返回-1。这种情况就很难判断是Server端进程关闭还是网络异常,这种情况一般会用TCP的KEEP ALIVE机制,每隔一定的时间向对方发送一个只有一字节数据内容的数据包,对端收到后会返回一个ACK,以此来确保连接正常,如果未收到ACK,会尝试重传,直到重试规定次数后可以将与对端的连接标记为断开,send和recv将会返回-1。KEEP ALIVE的使用方法如下:int tcp_keep_alive(int socketfd)int keepAlive =1;int keepIdle =10;/* 开始发送KEEP ALIVE数据包之前经历的时间 */int

9、 keepInterval =10;/* KEEP ALIVE数据包之前间隔的时间 */int keepCount =10;/* 重试的最大次数 */if(setsockopt(socketfd , SOL_SOCKET , SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)=-1)debug_info(set SO_KEEPALIVE failedn);return-1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPIDLE,(void*)&keepIdle,sizeof(keepIdle)=-1)debu

10、g_info(set TCP_KEEPIDEL failedn);return-1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPINTVL,(void*)&keepInterval,sizeof(keepInterval)=-1)debug_info(set TCP_KEEPINTVL failedn);return-1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPT,(void*)&keepCount,sizeof(keepCount)=-1)debug_info(set TCP_KEEPT failedn)

11、;return-1;return1;上面这个函数只针对Linux,昨天有网友告知在Mac OS上TCP_KEEPIDLE ,TCP_KEEPINTVL, TCP_KEEPT这些宏将未定义。另外对于这些参数的设置也是需要注意的,很多系统中它们的设置并不是对单个socket描述符起作用的,而是该机器上的所有socket描述符起作用的,所以这个需要注意这个是从UNP里面看到的。Linux的主机字节顺序是采用little-endian字节顺序,而网络字节顺序是采用big-endian字节顺序,字节顺序转换是必需的。写了一个小程序来检测字节顺序,不知道对不对,Request For ment.#incl

12、ude int main(int argc,char*argv)short s =0x0102;if(*(unsignedchar*)&s)=2)printf(little endiann);elseif(*(unsignedchar*)&s)=1)printf(big endiann);elseprintf(unknown endiann);return0;写过socket程序的人肯定都会知道send和recv函数并不会总是返回要求发送或读取的字节数,如:int ret = recv(sk, buf,2096,0);这句话并不总是读取到完整地2096个字节,相反地,大多数情况下都不能将buf

13、读满,recv只能返回当前可以读取到的字节数,如果协议规定本次读取肯定会读取到N个字节,那我一般的做法会写一个这样的函数来确保读取到固定的字节数:int buf_recv(int sock,void*buf, size_t len,int flags)int n, ret;if(len =0)return0;for(n=0;n!=len &(ret = recv(sock, buf+n, len-n, flags)!=-1&ret; n += ret);return(n!=len)?-1:n;关于这两个函数还有很重要的一点是应该尽可能大地一次发送或接收更多地数据,当然前提是缓冲区中有这些数据的

14、话,原因很简单,当通信链路很好的时候数据可能会填满系统缓冲区,而recv便是从缓冲区中读取数据,这时候一次读取更多地字节就意味着可以少调用几次recv函数,而这些函数通常都是调用了系统调用,需要进展内核态和用户态上下文的切换,也就意味着多调用几次recv会带来额外的开销,之前写的一个代理服务器的程序数据传输速度一直很低,后来修改了recv和send的缓冲区大小后速率提高了近一倍。一般应用的时候都是使用阻塞式IO,至少我在大多数情况下都用的阻塞式IO,非阻塞很少应用,但存在便我价值,我用到的非阻塞IO的情况一般是用来进展超时connect,首先将socket设为非阻塞模式,connect立即返回

15、-1,此时已向对端发送FIN,而并未来得与收到任何ACK,于是直接返回-1,但并不代表连接失败,errno会被置为EINPROGRESS ,表示连接正在进展中,然后通过select来设置socket可写的超时时间,如果规定时间内可写,且socket并无出错,如此表示连接成功,socket出错如此表示连接失败,或规定时间内不可写如此表示连接超时,简单地写了如下代码:#include#include#include#include#include#include#include int main(int argc,char*argv)int sk;int flags;int err =0;int ret;socklen_t len;struct sockaddr_in addr;fd_set fd_write;struct timeval tv;if(sk = socket(AF_INET, SOCK_STREAM,0)=-1)perror(socket);retur

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 工作计划

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号