13. sockaddr_in清空方式
初始化可直接清空
sockaddr_in addr = {0};
1.TIME_WAIT和FIN_WAIT
问题:一方close(fd1),另一方没有close(fd2),导致调用方fd1处于TIME_WAIT和FIN_WAIT,socket不能使用。
解决方法:struct linger ling = {1,0} 跳过四步握手,直接强杀socket,socket直接CLOSED呈可用状态。
tcp连接的关闭经过FIN->ACK->FIN->ACK四步握手。
服务器先调用close(socket):
客户端先调用close(socket):
过程:
1.A调用close(socket)发送FIN表示A的数据传输完成、没有其他数据要传输给B
2.B发ACK对FIN进行应答
3.A进入FIN_WAIT_2、B进入CLOSE_WAIT
4.此时B仍旧可以发送数据给A(A的事儿处理完了不代表B的事儿也处理完了)
5.B把其余数据发送给A完成传输
6.B调用close(socket)发FIN
7.A发ACK予以响应
8.连接关闭
一个close()对应着一个FIN,一个FIN对应着一个close()调用。只有双方都使用close关闭socket后,连接才算关闭,以上就是以绅士的方式关闭TCP连接。但是不管是谁,率先调用close()的一方必回最终处于TIME_WAIT状态,在一段时间后方可变为CLOSED状态,此状态下的socket表可用。
在某些情况下,比如由服务器主动关闭连接的情况,如果client足够好调用了close(),则服务器端会有大量socket处于TIME_WAIT状态。如果client非善类,不曾调用close(),则服务器会有大量socket处于FIN_WAIT2状态下。这两种情况最终将会导致资源耗尽。
CentOS6.4 系统下为了解决这一问题,先后试着更改系统文件,close()前先调用shutdown()强制关闭,均不起作用。
最终解决办法如下:
server每接受一个新连接,即对生成的socket进行参数设置:
struct linger ling = {1,0};
if(setsockopt(fd,SOL_SOCKET,SO_LINGER,(void*)&ling,sizeof(ling)) !=0){
std::cout<<"set socket fail"<<std::endl;
return;
}
SO_LINGER显示需设置的选项,struct linger结构:
struct linger {
int l_onoff; //0即关闭此选项,1即打开此选项
int l_linger; //逗留时间
};
有下列三种情况:
a、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
b、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT和FIN_WAIT2状态;
c、设置 l_onoff 为非0