参考博客:html
①setsockopt()函数使用详解:http://blog.csdn.net/tody_guo/article/details/5972588linux
②setsockopt :SO_LINGER 选项设置:http://blog.csdn.net/factor2000/article/details/3929816面试
③TIME_WAIT状态的做用:http://www.cnblogs.com/li-hao/archive/2011/12/08/2280678.html编程
在学习linux下c网络编程时,标准的C/S架构的网络体系模式时,没有注意connect的非阻塞模式,最近看项目代码时,发现原来connect非阻塞模式还有这么大的做用。但从程序客户端的角度,优化大量客户端链接服务器的性能。让我忽然想起曾经面试时的问题,TCP三次握手与四次挥手与你写的程序有什么关系。如今就知道了。服务器
关于:TCP三次握手与四次挥手,请看http://www.cnblogs.com/cz-blog/p/4431385.html。我以前的博客。网络
说明:因为程序用select等待链接完成,能够设置一个select等待时间限制,从而缩短connect超时时间。多数实现中,connect的超时时间在75秒到几分钟之间。有时程序但愿在等待必定时间内结束,使用非阻塞connect能够防止阻塞75秒,在多线程网络编程中,尤为必要。 例若有一个经过创建线程与其余主机进行socket通讯的应用程序,若是创建的线程使用阻塞connect与远程通讯,当有几百个线程并发的时候,因为网络延迟而所有阻塞,阻塞的线程不会释放系统的资源,同一时刻阻塞线程超过必定数量时候,系统就再也不容许创建新的线程(每一个进程因为进程空间的缘由能产生的线程有限),若是使用非阻塞的connect,链接失败使用select等待很短期,若是尚未链接后,线程马上结束释放资源,防止大量线程阻塞而使程序崩溃。多线程
int tcp_connect(char *host, int port) { int sock, flags; struct sockaddr_in rsock; struct hostent * hostinfo; struct in_addr * addp; struct linger stLinger = { 1, 2 }; memset ((char *)&rsock,0,sizeof(rsock)); if ((hostinfo = gethostbyname(host)) == NULL) { return -1; } //步骤一:socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { return -1; } //步骤二:填充 addp = (struct in_addr *)*(hostinfo->h_addr_list); rsock.sin_addr = *addp; rsock.sin_family = AF_INET; rsock.sin_port = htons(port); int ret = 0, error = -1, slen = sizeof(int); timeval tm; fd_set set; unsigned long ul = 1; ioctl(sock, FIONBIO, &ul); //设置为非阻塞模式 //步骤三:connect,此时socket设置为非阻塞,connect调用后,不管链接是否创建当即返回-1,
if (connect(sock, (struct sockaddr *)(&rsock), sizeof(rsock)) == -1) { //表示此时tcp三次握手仍旧进行,若是errno不是EINPROGRESS,则说明链接错误,程序结束 if (errno != EINPROGRESS) { ret = 0; } else { tm.tv_sec = 5; tm.tv_usec = 0; FD_ZERO(&set); FD_SET(sock, &set); //监听写集合 if (select(sock+1, NULL, &set, NULL, &tm) > 0) { //过调用 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len); 函数返回值来判断是否发生错误 //rror返回0则表示链接成功! getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&slen); if (error == 0) { ret = 1; } else { ret = 0; } } else { ret = 0; } } } else {//客户端和服务器已经创建链接 ret = 1; } ul = 0; ioctl(sock, FIONBIO, &ul); //设置为阻塞模式 //ret =1:表示正常创建链接 if (!ret) { close(sock); return -1; } //linger :徘徊的意思。SO_LINGER:表示经历time_wait阶段,且时间是stLinger中第二个参数指定的值。 flags = setsockopt(sock, SOL_SOCKET, SO_LINGER, &stLinger, sizeof(struct linger)); if (flags == -1) { close(sock); return -1; } return sock;
setsockopt 设置 SO_LINGER 选项:做用就是在close关闭时,保证发送数据发送到对方后,再完全关闭链接。架构
当套接口关闭时内核将拖延一段时间(由l_linger决定)。若是套接口缓冲区中仍残留数据,进程将处于睡眠状态,直到并发
(a)全部数据发送完且被对方确认,以后进行正常的终止序列(描述字访问计数为0)或socket
(b)延迟时间到。
此种状况下,应用程序检查close的返回值是很是重要的,由于要区分两种状态:
若是在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。
若是数据发送完并被确认后,指定的时间才到,close的成功返回仅告诉咱们发送的数据(和FIN)已由对方TCP确认,它并不能告诉咱们对方应用进程是否已读了数据。若是套接口设为非阻塞的,它将不等待close完成。
此选项指定函数close对面向链接的协议如何操做(如TCP)。内核缺省close操做是当即返回,若是有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
SO_LINGER选项用来改变此缺省设置。使用以下结构:
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三种状况:
一、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省状况,close调用会当即返回给调用者,若是可能将会传输任何未发送的数据;
二、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折链接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是一般的四分组终止序列,这避免了TIME_WAIT状态;
三、设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。若是套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)全部数据发送完且被对方确认,以后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种状况下,应用程序检查close的返回值是很是重要的,若是在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉咱们发送的数据(和FIN)已由对方TCP确认,它并不能告诉咱们对方应用进程是否已读了数据。若是套接口设为非阻塞的,它将不等待close完成。
更具体的描述以下:
一、若设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零),并设置了零超时间隔,则closesocket()不被阻塞当即执行,不管是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,由于套接口的虚电路当即被复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。
二、若设置了SO_LINGER并肯定了非零的超时间隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅”或“从容”关闭。请注意若是套接口置为非阻塞且SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。
三、若在一个流类套接口上设置了SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零),则closesocket()调用当即返回。可是,若是可能,排队的数据将在套接口关闭前发送。请注意,在这种状况下WINDOWS套接口实现将在一段不肯定的时间内保留套接口以及其余资源,这对于想用因此套接口的应用程序来讲有必定影响。
SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。
SO_LINGER延迟关闭链接 struct linger上面这两个选项影响close行为;
选项 间隔 关闭方式 等待关闭与否 SO_DONTLINGER 不关心 优雅 否 SO_LINGER 零 强制 否 SO_LINGER 非零 优雅 是