参考博客:
①setsockopt()函数使用详解:http://blog.csdn.net/tody_guo/article/details/5972588
②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 {//客户端和服务器都在本地,返回0 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)或
(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 */
};
有下列三种情况:
1、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
2、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态;
3、设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完成。
更具体的描述如下:
1、若设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零),并设置了零超时间隔,则closesocket()不被阻塞立即执行,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,因为套接口的虚电路立即被复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。
2、若设置了SO_LINGER并确定了非零的超时间隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅”或“从容”关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。
3、若在一个流类套接口上设置了SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零),则closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。请注意,在这种情况下WINDOWS套接口实现将在一段不确定的时间内保留套接口以及其他资源,这对于想用所以套接口的应用程序来说有一定影响。
SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。
SO_LINGER延迟关闭连接 struct linger上面这两个选项影响close行为;
选项 间隔 关闭方式 等待关闭与否
SO_DONTLINGER 不关心 优雅 否
SO_LINGER 零 强制 否
SO_LINGER 非零 优雅 是