工作中用到了TCP通讯,把遇到的问题和解决方法小结一下,方便以后查阅,有问题的地方也请各位大神指出~
问题一:非阻塞收发
按照最初的想法,我是想用setsockopt()设置下收发的阻塞时间间隔就OK了,可是通讯时状况百出,也没搞清楚为什么这样不对,如果有哪位大神能指点一下,小弟感激不尽,错误代码这里就不粘了,省得误人子弟~
最终我采用的方法是select判断收发缓冲区,已经过验证,下面付一小段代码:
接收端:
while(1)
{
<span style="white-space:pre"> </span>FD_ZERO(&serialread);
<span style="white-space:pre"> </span>FD_SET(sock,&serialread);
serialtimeout.tv_sec = 0;
serialtimeout.tv_usec = 500000;
switch(select(g_sock+1,&serialread,NULL,NULL,&serialtimeout))
{
case -1:
perror("select error----");
return;
break;
case 0:
{
continue;
}
break;
default:
{
if(FD_ISSET(g_sock,&serialread))
sign_read = 1;
}
break;
}
if(sign_read != 1)
continue;
sign_read = 0;
<span style="white-space:pre"> </span>buflen = recv(sock,recvbuf,MAX_DATA_LEN,0);
<span style="white-space:pre"> </span>if (buflen < 0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>perror("Net link error,close the link!---");
<span style="white-space:pre"> </span>close(g_sock);
<span style="white-space:pre"> </span>break;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else if(buflen==0 || errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN)
<span style="white-space:pre"> </span>continue;
<span style="white-space:pre"> </span>else if(buflen < IEC104_U_FRAME_LEN)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>tcflush(sock,TCIFLUSH);
<span style="white-space:pre"> </span>continue;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>/*数据处理*/
}
发送端是同样的道理。
问题二:对连接异常的判断和处理
开发时我的需求是满足网线的热插拔,就是设备不断电的情况下网线拔掉再插上还能继续通讯(前提:我做的是TCP server端,client端是第三方软件),对于linux底层对网络连接的状态判断,我在网上查了些资料,下面是链接:
http://blog.csdn.net/ctthuangcheng/article/details/8596818
http://bbs.csdn.net/topics/40417156
我的解决方法也就是上面第二个链接中一位大神介绍的方法,就是在创建socket后设置keepalive的时间间隔:
int param = 1;
struct linger lingerStruct;
<span style="white-space:pre"> </span>lingerStruct.l_onoff = 1;
<span style="white-space:pre"> </span>lingerStruct.l_linger = 0;
if(0 != setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(char*)¶m,sizeof(param)))//设置tcp心跳包的间隔时间
<span style="white-space:pre"> </span>perror("Set tcp keepalive time error---");
if(0 != setsockopt(transmit_pthread,SOL_SOCKET, SO_LINGER,(char *)&lingerStruct, sizeof(lingerStruct)))//设置close()函数立即生效
perror("Set tcp linger error---");
需要注意的是,linux系统把默认的tcp心跳包时间间隔设置为7200s是为了不占用网络资源,因此网络通讯数据量很大或者对实时性要求很高的时候建议根据实际情况进行调整
问题三:用CTRL+C停止程序后要等一段时间才能bind成功
之所以出现这种情况是因为程序停止后原来创建的socket没有立即释放,大概要等1~2min的时间,因此在此时间内再次bind()会失败,解决方法是在创建socket后执行:
int on = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))