有关UDP非阻塞recvfrom超时设置的问题

lingducool 2013-05-21 08:55:57
我现在想编这么一个程序,向目标端口发送UDP的一个包后,用recvfrom等待接受回应,等待5秒后未接到回应就继续向下执行。
我的思路是设置为非阻塞式套接字,然后设置超时5秒,但是这样我没有成功,代码如下:
#include <winsock2.h>
#include <iostream.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void initClient();
int main()
{
initClient();
return 0;
}
void initClient()
{
WSADATA wsaData;
int error=WSAStartup(MAKEWORD(2,2),&wsaData);
if(error!=0)
{
cout<<"初始化DLL失败"<<endl;
return;
}
if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
cout<<"版本出错"<<endl;
return;
}
SOCKET s=socket(AF_INET,SOCK_DGRAM,0);
SOCKADDR_IN sockSend;
sockSend.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
sockSend.sin_port=htons(4000);
sockSend.sin_family=AF_INET;
char buff[1024];
strcpy(buff,"hello,it's the first!");


int iMode = 1;//block
ioctlsocket(s, FIONBIO, (u_long FAR*) &iMode);
DWORD TimeOut=1000*5; //设置发送超时10秒
if(::setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
cout<<"设置失败"<<endl;
}

int lenword;
lenword=sendto(s,buff,strlen(buff)+1,0,(sockaddr *)&sockSend,sizeof(sockaddr));
cout<<lenword<<","<<sockSend.sin_port<<":"<<sockSend.sin_addr.S_un.S_addr<<endl;




char recBuff[1024];
memset(recBuff,0,1024);
SOCKADDR_IN sockRec;
int len=sizeof(SOCKADDR);

recvfrom(s,recBuff,sizeof(recBuff),0,(sockaddr *)&sockRec,&len);

printf("the receive is\n");

closesocket(s);
WSACleanup();
}


大家注意这句:int iMode = 1;不管我这一句是0还是1,都不会阻塞,程序执行后一下子就出现了打印的最后一句,根本不会等待5秒。我的这是windows平台的程序
...全文
11462 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
swing07 2015-06-09
  • 打赏
  • 举报
回复
UDP 非阻塞 int iMode = 0;//0 == block, 1 == non-block ioctlsocket(s, FIONBIO, (u_long FAR*) &iMode);可以用。 但是 我按照你们两个讨论的 设置超时还是不成功。不知道到底怎么用,能否请教一下。
nossiac 2013-05-22
  • 打赏
  • 举报
回复
想了一下,最后一点说的不全面。bind也不能防住sendto的错误。 这是使用bind的改法。sendto注释掉了,如果要启用它,得保证对面有人接收数据。不然会出错,导致socket error。

#include <winsock2.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

void initClient();
int main()
{
	initClient();
	return 0;
}
void initClient()
{
	WSADATA wsaData; 
	int error=WSAStartup(MAKEWORD(2,2),&wsaData);
	if(error!=0)
	{
		cout<<"初始化DLL失败"<<endl;
		return;
	}
	if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
	{
		WSACleanup();
		cout<<"版本出错"<<endl;
		return;
	}
	SOCKET s=socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN sockSend;
	sockSend.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	sockSend.sin_port=htons(6788);
	sockSend.sin_family=AF_INET;
	char * buff = "hello,it's the first!";
	int lenword;
	SOCKADDR_IN sockme;
	char recBuff[1024];
	DWORD TimeOut=1000*5;   //设置发送超时10秒

	memset(recBuff,0,1024);

	sockme.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	sockme.sin_port=htons(9686);
	sockme.sin_family=AF_INET;

#if 1
	lenword = bind(s, (sockaddr *)&sockme,sizeof(sockme)); 
    if (lenword != 0) {
        printf("bind failed with error %d\n", WSAGetLastError());
        return;
    }
#endif
	if(::setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
	{
		cout<<"设置失败"<<endl;
	}

	//lenword=sendto(s,buff,strlen(buff),0,(sockaddr *)&sockSend,sizeof(sockaddr));
	//cout<<lenword<<","<<sockSend.sin_port<<":"<<sockSend.sin_addr.S_un.S_addr<<endl;

	lenword = recvfrom(s,recBuff,sizeof(recBuff),0, NULL, NULL); 
	printf("recvfrom data %d:%s\n", lenword, recBuff); 
    if (lenword < 0) {
        printf("recvfrom failed with error %d\n", WSAGetLastError());
        return;
    }

	closesocket(s);
	WSACleanup();
}
如果能保证对面有人接收,那也可以用sendto让主机自动分析本地地址,不须bind。

#include <winsock2.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

void initClient();
int main()
{
	initClient();
	return 0;
}
void initClient()
{
	WSADATA wsaData; 
	int error=WSAStartup(MAKEWORD(2,2),&wsaData);
	if(error!=0)
	{
		cout<<"初始化DLL失败"<<endl;
		return;
	}
	if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
	{
		WSACleanup();
		cout<<"版本出错"<<endl;
		return;
	}
	SOCKET s=socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN sockSend;
	sockSend.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	sockSend.sin_port=htons(6788);
	sockSend.sin_family=AF_INET;
	char * buff = "hello,it's the first!";
	int lenword;
	SOCKADDR_IN sockme;
	char recBuff[1024];
	DWORD TimeOut=1000*5;   //设置发送超时10秒

	memset(recBuff,0,1024);

	sockme.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	sockme.sin_port=htons(9686);
	sockme.sin_family=AF_INET;

#if 0
	lenword = bind(s, (sockaddr *)&sockme,sizeof(sockme)); 
    if (lenword != 0) {
        printf("bind failed with error %d\n", WSAGetLastError());
        return;
    }
#endif
	if(::setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
	{
		cout<<"设置失败"<<endl;
	}

	lenword=sendto(s,buff,strlen(buff),0,(sockaddr *)&sockSend,sizeof(sockaddr));
	cout<<lenword<<","<<sockSend.sin_port<<":"<<sockSend.sin_addr.S_un.S_addr<<endl;

	lenword = recvfrom(s,recBuff,sizeof(recBuff),0, NULL, NULL); 
	printf("recvfrom data %d:%s\n", lenword, recBuff); 
    if (lenword < 0) {
        printf("recvfrom failed with error %d\n", WSAGetLastError());
        return;
    }

	closesocket(s);
	WSACleanup();
}
nossiac 2013-05-22
  • 打赏
  • 举报
回复
有几处问题: 1、“非阻塞式套接字”和“超时”是矛盾的。 非阻塞式套接字表示如果没有数据就直接返回,不会有等待超时的机制。 看你的需求,应该是阻塞式+超时。 2、windows 套接字默认是阻塞式的,也就是默认效果就是: int iMode = 0;//0 == block, 1 == non-block ioctlsocket(s, FIONBIO, (u_long FAR*) &iMode); 3、为什么你的socket没有阻塞,原因有点复杂。 一是因为udp是无连接的,socket没有指定本地地址的时候,IP层是没办法知道哪个数据该送达给它。 4、要想在recvfrom上实现阻塞,前提是socket已经具有一个本地地址。 一般有两个办法,一是做一次成功的send动作,自然本地地址就由主机自行分配了,二是手动给socket bind一个。 另外要注意,sendto的失败信息是通过ICMP异步返回的,所以sendto不会阻塞,但接下来的recvfrom就可以撞上这个icmp错误,然后就终止了…… 所以比较可靠的办法就是bind。

4,358

社区成员

发帖
与我相关
我的任务
社区描述
通信技术相关讨论
社区管理员
  • 网络通信
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧