在项目中需要检测UDP是否打开,刚开始实现是定时发送数据包,recvfrom设置超时时间是1s。结果发现当UDP没有开启时(比如电脑开机后直接运行)recvfrom设置的超时时间无效,而是立即返回SOCKET_ERROR,recvfrom函数返回0。
recvfrom返回值为0有两种情况:
1.socket已经"温和"关闭(使用shutdown或者设置linear属性)
2.对方发送一个空数据,也就是对方发送的数据长度为0。这时socket也就可以接收到的,并且recvfrom返回值为0。
只有服务端的recvfrom接收的是所有外部发向这个端口号的信息的,客户端的recvfrom是根据socket里的信息来收的,是绑定过服务器IP和端口号的。
最后解决方法: 定时发送数据包,用WSAGetLastError()判断错误号是否是10054。
调用WSAGetLastError()返回的结果为0,GetLastError()也是返回0。原因:int err=WSAGetLastError();的调用必须放到其他系统函数调用之前,也就是说,出现错误后,第一时间存储WSAGetLastError结果,而不能调用其他函数之后再调用此函数。因为调用系统函数会清除WSAGetLastError,致使结果返回0.
代码:
int TimeOut = 1000;
//设置接受等待时间为TimeOut
if(setsockopt(g_sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return -1;
}
for(i=0; i<5; i++){
memset(szBuff, 0, sizeof(szBuff));
strcpy(szBuff,"Test the UDP connect!\r\n");
ret = sendto(g_sock,szBuff,strlen(szBuff),0,(sockaddr *)&addrSend,sizeof(addrSend));
printf("%d send data: %s to %s:%d \n",ret,szBuff,inet_ntoa(addrSend.sin_addr),ntohs(addrSend.sin_port));
int nRecLen = sizeof(addrSend);
//等待并接受来自B的数据
memset(szBuff, 0, sizeof(szBuff));
ret = recvfrom(g_sock,szBuff,256,0,(sockaddr *)&addrSend,&nRecLen);
if(GetLastError() == 10054){
Sleep(1000);
}
printf("ret %d data: %s to %s:%d \n",ret, szBuff,inet_ntoa(addrSend.sin_addr),ntohs(addrSend.sin_port));
if (ret > 0 && strcmp(szBuff, "The Battery board is working!\r\n")==0) {
connectBoard = true;
break;
}
}
参考:
https://bbs.csdn.net/topics/391937100
https://blog.csdn.net/tianbaowen/article/details/8485567