问题背景:我们研制了一台设备,并开发了设备控制软件,设备内置主控板,运行平台为win7 64bit,本地控制软件采用MFC开发。上位机软件通过网络控制设备实现业务功能,采用的是TCP协议。设备本地控制软件用SCOKET搭建了服务器,监听本地5025端口,接收来自上位机socket客户端的连接,背景介绍完毕。
问题描述:上位机是24小时不停的运行,通过SOECKET完成初始化后,大部分时间是不与设备进行通信的,与设备收发数据时间紧紧几秒钟,间隔时间可能几个小时也可能几天。如果上位机在初始化完成后2小时内不与客户端进行任何数据交互,那么设备控制软件会主动断开连接,造成通信失败。
设备端socket代码:
InitTcpServer()
{
WORD wVersionRequested;//版本号
wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
WSADATA wsaData;
//初始化Winsock
int err;
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
return;
}//判断高低字节是不是1,如果不是1.1的版本则退出
//创建套接字
m_Server = socket(AF_INET, SOCK_STREAM, 0);
//Socket地址结构体的创建
struct sockaddr_in seraddr;
int serport=5025;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(serport);
seraddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//绑定套接字到服务器地址结构
err=bind(m_Server, (SOCKADDR*)&seraddr, sizeof(SOCKADDR));//If no error occurs, bind returns zero.
if(err!=0)
{
MessageBox("Bind Error!");
exit(-3);
}
//监听套接字
err=listen(m_Server,5);
if(err!=0)
{
MessageBox("Listen Error!");
exit(-2);
}
//套接字clisock上接收到数据时,向窗口发送自定义消息RECEIVE_EVENT
WSAAsyncSelect(m_Server,GetSafeHwnd(),ACCEPT_EVENT,FD_ACCEPT|FD_READ|FD_CLOSE);
}
尝试解决: 出现这个问题后,在网上查询了SOCKET相关内容,socket默认有7200秒的超时时间,如果7200秒内没有数据通信,服务器就会主动断开连接。网上找的解决办法是启动心跳包功能,socket默认keepalive是不开启的,需要用户主动开启。于是我在原基础上增加了启动心跳包的代码:
InitTCPServer()
{
WORD wVersionRequested;//版本号
wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
WSADATA wsaData;
//初始化Winsock
int err;
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
return;
}//判断高低字节是不是1,如果不是1.1的版本则退出
//创建套接字
m_Server = socket(AF_INET, SOCK_STREAM, 0);
//////////////////////////////////////////////////////////////////////////
tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每5秒发一次探测报文,发5次没有回应,就断开
live.keepalivetime=30000;//超过30s没有数据,就发送控测包
live.onoff=TRUE;
int Opt = 1;
int iRet = setsockopt(m_Server,SOL_SOCKET,SO_KEEPALIVE,(char *)&Opt,sizeof(int));
if(iRet == 0)
{
DWORD dw;
if(::WSAIoctl(m_Server,SIO_KEEPALIVE_VALS,
&live,sizeof(live),&liveout,sizeof(liveout),
&dw,NULL,NULL)== SOCKET_ERROR)
{
}
}
//////////////////////////////////////////////////////////////////////////
//Socket地址结构体的创建
struct sockaddr_in seraddr;
int serport=5025;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(serport);
seraddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//绑定套接字到服务器地址结构
err=bind(m_Server, (SOCKADDR*)&seraddr, sizeof(SOCKADDR));//If no error occurs, bind returns zero.
if(err!=0)
{
MessageBox("Bind Error!");
exit(-3);
}
//监听套接字
err=listen(m_Server,5);
if(err!=0)
{
MessageBox("Listen Error!");
exit(-2);
}
//套接字clisock上接收到数据时,向窗口发送自定义消息RECEIVE_EVENT
WSAAsyncSelect(m_Server,GetSafeHwnd(),ACCEPT_EVENT,FD_ACCEPT|FD_READ|FD_CLOSE);
}
测试了几天,仍然是每2小时服务器会主动断开客户端连接。
我们也想过不用长连接来实现设备控制,紧紧在需要控制的时候在连接设备,这是确实可以解业务层面的问题,但问题没有真正解决,确实如鲠在喉。