首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络协议 >

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

2013-07-09 
有关UDP非阻塞recvfrom超时设置的问题本帖最后由 lingducool 于 2013-05-21 21:04:20 编辑我现在想编这么

有关UDP非阻塞recvfrom超时设置的问题
本帖最后由 lingducool 于 2013-05-21 21:04:20 编辑 我现在想编这么一个程序,向目标端口发送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平台的程序 UDP socket 阻塞 超时 Windows
[解决办法]
有几处问题:
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。
[解决办法]
想了一下,最后一点说的不全面。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();
}

热点排行