我正在嘗試實作一個單元測驗,以查看使用“WaitForMultipleObjects”函式是否適用于套接字。為此,我實作了一個簡單的 UDP 服務器-客戶端協議。服務器系結他的套接字并等待使用函式“WaitForMultipleObjects”從接收者接收資料。一旦服務器從客戶端接收到資料,它就會顯示它,然后等待 5 秒。這里的問題是,如果客戶端在這五秒鐘內嘗試發送 2 條訊息,則顯示第一個訊息,而第二個訊息將永遠阻止函式“WaitForMultipleObjects”。我知道我可以使用“選擇”功能來做到這一點,因為套接字是我正在等待的唯一物件,但這只是一個單元測驗。在我的真實專案中,我需要等待一個套接字和另一個物件' s 型別,它同時是一個 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,'\0', 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:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
printf("Data: %s\n" , 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,'\0', 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”)
uj5u.com熱心網友回復:
這里的問題是,如果客戶端在這五秒鐘內嘗試發送 2 條訊息,則顯示第一個訊息,而第二個訊息將永遠為函式“WaitForMultipleObjects”計時。
您的服務器代碼有競爭條件。
您應該在呼叫WSAResetEvent/ResetEvent之前呼叫recvfrom,而不是之后呼叫。否則,新資料可能會在呼叫recvfrom和之間到達WSAResetEvent,將事件物件設定為已發出信號。在這種情況下,WSAResetEvent會將事件設定回非信號狀態,從而導致您丟失新資料可用的通知。
此外,根據 的檔案WSAEventSelect,從套接字讀取資料后,如果有更多資料可供讀取,則該事件將自動設定為再次發出信號,以指示有更多資料可用。如果您WSAResetEvent之后呼叫,那么您會將事件設定回非信號狀態,從而導致您丟失新資料可用的通知。這可能是您在問題中描述的行為的原因。
您應該在/之后立即呼叫WSAResetEvent/ 。請參閱該函式的檔案以獲取代碼示例(盡管該示例使用重疊 I/O 而不是)。ResetEventWSAWaitForMultipleEventsWaitForMultipleObjectsExWSAWaitForMultipleEventsWSAEventSelect
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/485244.html
