问题描述
在项目开发的时候,需要实现异常断线重连的功能。在客户端和服务器端建立连接后,拔掉客户端的网线,客户端这边能够正常返回错误,但是在网络恢复后,执行重连函数却无法再次连接上服务器。
分析
此处是我一开始执行的流程:在客户端连接成功后,在recv()函数等待接收服务器端发来的消息。若此时拔掉网线,recv()会返回错误,此时会在sleep(3000)后再次调用connec()函数,尝试与服务器连接。当网络恢复后,可以通过connec()函数重新连接上服务器。
但是经过测试后却发现,网络恢复后,connect始终无法连接上服务器,从日志中可以看到以下内容:
10056:TCP模式的socket在保持连接的状态下再次connect服务端会报错
10053:软件原因造成的连接中断
通过查阅资料得知:
当客户端与服务器建立起正常的TCP连接后,在二者都没有配置操作系统的SO_KEEPALIVE选项,或者进行应用层心跳检测的情况下,如果客户主机网线断开、电源掉电、或系统崩溃,服务器进程将永远不会知道(通过我们常用的select,epoll监测不到断开或错误事件),如果不主动处理或重启系统的话对于服务端来说会一直维持着这个连接,任凭服务端进程如何望穿秋水,也永远再等不到客户端的任何回应。
由此可以推断,虽然客户端虽然会返回错误,但是客户端socket未被销毁,服务器也会因此一直连接着socket,也就无法知道客户端的状态。
而当客户端连接恢复后,调用connect函数,但是因为服务器之前一直都是连接着客户端socket,所以connect就会一直返回错误,自然就不会重新连接上。
解决方法
此处提供一个我的方法:简单修改了之前的流程,使用的是接收阻塞的方式,在线程中处理。
在客户端因为异常原因,返回错误后,调用closesocket()关闭客户端socket。这样也会使服务器断开连接。此时在调用connect函数之前,对客户端套接字进行判断,若是无效套接字(INVALID_SOCKET),则重新创建socket连接。
以下为简单代码举例:
//定义的结构体
SOCKET s_client;
SOCKADDR s_addr_client;
int nResult;
char szRecBuf[2048]
//创建套接字
s_client= socket(AF_INET, SOCK_STREAM, 0);
while(1)
{
if (s_client == INVALID_SOCKET)
{
s_client= socket(AF_INET, SOCK_STREAM, 0);
}
//向服务器发送连接请求
if (connect(s_client ,(SOCKADDR*)&s_addr_client,sizeof(SOCKADDR)) == SOCKET_ERROR)
{
closesocket(s_client);
s_client = INVALID_SOCKET;
continue;
}
while(1)
{
nResult = recv(s_client, szRecBuf, sizeof(szRecBuf), 0);
if (nResult == SOCKET_ERROR)
{
//异常,关闭socket
closesocket(s_client);
s_client = INVALID_SOCKET;
break;
}
else
{
//接收数据处理
}
}
}
结果:
可以成功重连,并且也能正常接收到服务器发来的消息。
若有其他方法,以后也会再补充。
参考链接
链接: 网络编程释疑之:TCP半开连接的处理.