正在使用函数WaitForMultipleObjects(msdn)等待套接字



我正在尝试实现一个单元测试,看看是否使用函数"WaitForMultipleObjects";插座工作正常。为此,我实现了一个简单的UDP服务器客户端协议。服务器绑定他的套接字并等待使用函数"0"从接收器接收数据;等待多个对象";。一旦服务器从客户端接收到数据,它就会显示数据,然后等待5秒。这里的问题是,如果客户端试图在这五秒钟内发送两条消息,则显示第一条消息,而第二条消息将阻止功能"0";WaitForMultipleObjects";永远我知道我可以使用";选择";函数,因为套接字是我正在等待的唯一对象,但这只是一个单元测试。在我的真实项目中,我需要同时等待一个套接字和另一个对象的类型,即windows事件(类型为HANDLE)。这就是为什么我尝试使用";WaitForMultipleObjects";在插座上。

这是服务器代码:

/*
Simple UDP Server
*/
#include<winsock2.h>
#include<windows.h>
#include <ws2tcpip.h>
#include<stdio.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data


int main()
{
SOCKET s;
struct sockaddr_in server, si_other;
int slen , recv_len;
char buf[BUFLEN];
WSADATA wsa;
HANDLE SEvent;

slen = sizeof(si_other) ;

//Initialise winsock
printf("nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Initialised.n");

//Create a socket
if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
printf("Socket created.n");

//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( PORT );

//Bind
if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
{
printf("Bind failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}

puts("Bind done");

SEvent = WSACreateEvent();
WSAEventSelect( s, SEvent, FD_READ);
//keep listening for data
while(1)
{
printf("Waiting for data...");
fflush(stdout);

//clear the buffer by filling null, it might have previously received data
memset(buf,'', BUFLEN);
INT r = WaitForMultipleObjectsEx(1,&SEvent,FALSE,INFINITE,TRUE);
if( r == WAIT_OBJECT_0)
{
if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == SOCKET_ERROR)
{
printf("recvfrom() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
//print details of the client/peer and the data received
printf("Received packet from %s:%dn", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
printf("Data: %sn" , buf);
//now reply the client with the same data
if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == SOCKET_ERROR)
{
printf("sendto() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
ResetEvent(SEvent);
Sleep(5000);
}
else
{
std::cerr<<"WaitForMultipleObject() Error ( "<<GetLastError()<<" )"<<std::endl;
exit(0);
}

}
closesocket(s);
WSACleanup();

return 0;
}

这是客户端代码:


/*
Simple udp client
*/
#include<stdio.h>
#include<winsock2.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#define SERVER "127.0.0.1"  //ip address of udp server
#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data
int main(void)
{
struct sockaddr_in si_other;
int s, slen=sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];
WSADATA wsa;
//Initialise winsock
printf("nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Initialised.n");

//create socket
if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR)
{
printf("socket() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}

//setup address structure
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
si_other.sin_addr.S_un.S_addr = inet_addr(SERVER);

//start communication
while(1)
{
printf("Enter message : ");
gets(message);

//send the message
if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen) == SOCKET_ERROR)
{
printf("sendto() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}

//receive a reply and print it
//clear the buffer by filling null, it might have previously received data
/*memset(buf,'', BUFLEN);
//try to receive some data, this is a blocking call
if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == SOCKET_ERROR)
{
printf("recvfrom() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}

puts(buf);*/
}
closesocket(s);
WSACleanup();
return 0;
}

注意:我没有写所有的代码。我使用了一个已经(从互联网上)编写的代码,但做了一些更改

有人知道如何解决这个问题吗?(在套接字上正确使用"WaitForMultipleObjects")

这里的问题是,如果客户端试图在这五秒钟内发送两条消息,则会显示第一条消息,而第二条消息将计时函数"WaitForMultipleObjects";永远

您的服务器代码具有竞争条件。

您应该在呼叫recvfrom之前呼叫WSAResetEvent/ResetEvent,而不是之后。否则,新数据可能会在对recvfromWSAResetEvent的调用之间到达,从而将事件对象设置为已发出信号。在这种情况下,WSAResetEvent会将事件设置回无信号状态,导致您丢失可用新数据的通知。

此外,根据WSAEventSelect的文档,在从套接字读取数据之后,如果有更多数据可供读取,则事件将自动设置为再次发出信号,以指示有更多数据可用。如果之后调用WSAResetEvent,则会将事件设置回无信号状态,导致丢失可用新数据的通知。这可能就是你在问题中描述的行为的原因。

您应该在WSAWaitForMultipleEvents/WaitForMultipleObjectsEx之后立即调用WSAResetEvent/ResetEvent。有关代码示例,请参阅函数WSAWaitForMultipleEvents的文档(不过,该示例使用重叠I/O而不是WSAEventSelect)。

最新更新