WSAWaitForMultipleEvents

本文详细介绍了WSAWaitForMultipleEvents函数的功能与使用方法,包括参数解释、返回值说明及注意事项。此函数用于等待多个事件触发,适用于网络编程中的非阻塞IO操作。
WSAWaitForMultipleEvents函数
  熟悉WSAEventSelect模型的朋友对这个函数肯定不会陌生,不对,其实大家都不应该陌生,这个函数与线程中常用的WaitForMultipleObjects函数有些地方还是比较像的,因为都是在等待某个事件的触发嘛。
  因为我们需要事件来通知我们重叠操作的完成,所以自然需要这个等待事件的函数与之配套。
  DWORD WSAWaitForMultipleEvents(
  DWORD cEvents, // 等候事件的总数量
  const WSAEVENT* lphEvents, // 事件数组的指针
  BOOL fWaitAll, // 这个要多说两句:
  // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
  // FALSE则任何一个事件被传信函数都要返回
  // 我们这里肯定是要设置为FALSE的
  DWORD dwTimeout, // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
  // 如果设置为0,函数会立即返回
  // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回,
  //则WSAWaitForMultipleEvents 永远等待,不会出现超时现象。
  BOOL fAlertable
  //该值指定线程是否为alertable等待状态,此时系统能执行一些I/O 完成例程。如果值为真,当系统执行I/O //完成例程时线程被处于altertable 等待状态且WSAWaitForMultipleEvents 返回。在这种情况下,返回
  //WSA_WAIT_IO_COMPLETION ,并且等待的event 不会触发信号状态。程序必须重新调用
  //WSAWaitForMultipleEvents 函数。如果为false,线程不处于altertable 等待状态,
  //并且I/O 完成例程不会执行。 
  );
  返回值:
  WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
  WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
  如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
  具体的例子就先不在这里举了,后面还会讲到
  注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一个线程池,
#include <iostream> #include <string> #include <vector> #include <WinSock2.h> #include <WS2tcpip.h> #pragma comment(lib, "ws2_32.lib") using namespace std; // 全局套接字 SOCKET udpsocket, tcpsocket; // 用于存储已连接的 TCP 客户端套接字和对应的事件对象 vector<SOCKET> tcpClients; vector<WSAEVENT> tcpClientEvents; // 初始化 UDP 套接字 SOCKET udp_start_up() { WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { cout << "WSAStartup (UDP) failed!" << endl; return INVALID_SOCKET; } SOCKET clientsocket = socket(AF_INET, SOCK_DGRAM, 0); if (clientsocket == INVALID_SOCKET) { cout << "UDP Socket creation failed with error: " << WSAGetLastError() << endl; WSACleanup(); return INVALID_SOCKET; } struct sockaddr_in udp_addr; udp_addr.sin_family = AF_INET; udp_addr.sin_addr.s_addr = INADDR_ANY; udp_addr.sin_port = htons(7210); if (bind(clientsocket, (sockaddr*)&udp_addr, sizeof(udp_addr)) == SOCKET_ERROR) { cout << "UDP Bind failed with error: " << WSAGetLastError() << endl; closesocket(clientsocket); WSACleanup(); return INVALID_SOCKET; } cout << "UDP Socket created and ready on port 7210." << endl; return clientsocket; } // 初始化 TCP 套接字 SOCKET tcp_start_up() { WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { cout << "WSAStartup (TCP) failed!" << endl; return INVALID_SOCKET; } SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == INVALID_SOCKET) { cout << "TCP Socket creation failed with error: " << WSAGetLastError() << endl; WSACleanup(); return INVALID_SOCKET; } struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8888); inet_pton(AF_INET, "192.168.43.86", &serverAddr.sin_addr); if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { cout << "Bind failed with error: " << WSAGetLastError() << endl; closesocket(serverSocket); WSACleanup(); return INVALID_SOCKET; } if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { cout << "Listen failed with error: " << WSAGetLastError() << endl; closesocket(serverSocket); WSACleanup(); return INVALID_SOCKET; } cout << "TCP Server started on port 8888" << endl; return serverSocket; } int main() { // 初始化 UDP 和 TCP 套接字 udpsocket = udp_start_up(); if (udpsocket == INVALID_SOCKET) return -1; tcpsocket = tcp_start_up(); if (tcpsocket == INVALID_SOCKET) { closesocket(udpsocket); WSACleanup(); return -1; } // 创建事件对象 WSAEVENT udpEvent = WSACreateEvent(); WSAEVENT tcpEvent = WSACreateEvent(); // 将 UDP 套接字和事件对象绑定,监视 FD_READ 事件 WSAEventSelect(udpsocket, udpEvent, FD_READ); // 将 TCP 监听套接字和事件对象绑定,监视 FD_ACCEPT 事件 WSAEventSelect(tcpsocket, tcpEvent, FD_ACCEPT); // 存储所有事件对象(UDP 事件 + TCP 事件 + 每个 TCP 客户端事件) vector<WSAEVENT> events = { udpEvent, tcpEvent }; while (true) { // 等待任意一个事件对象变为有信号状态 DWORD index = WSAWaitForMultipleEvents(events.size(), events.data(), FALSE, WSA_INFINITE, FALSE); if (index == WSA_WAIT_FAILED) { cerr << "WSAWaitForMultipleEvents failed: " << WSAGetLastError() << endl; break; } index = index - WSA_WAIT_EVENT_0; // 计算实际的事件索引 if (index >= events.size()) continue; WSAEVENT triggeredEvent = events[index]; // 检查是否是 UDP 事件 if (triggeredEvent == udpEvent) { char buffer[1024]; sockaddr_in client_addr; int udp_len = sizeof(client_addr); int n = recvfrom(udpsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, (socklen_t*)&udp_len); if (n > 0) { buffer[n] = '\0'; char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int client_port = ntohs(client_addr.sin_port); cout << "UDP Socket " << udpsocket << " received from " << client_ip << ":" << client_port << ": " << buffer << endl; // 回显消息 if (sendto(udpsocket, buffer, n, 0, (struct sockaddr*)&client_addr, udp_len) != n) { cerr << "UDP Send failed: " << WSAGetLastError() << endl; } else { cout << "UDP Socket " << udpsocket << " sent back a message." << endl; } } else { int errorCode = WSAGetLastError(); if (errorCode == WSAEWOULDBLOCK) { // 没有数据可读,这是正常现象,不需要处理 cout << "UDP Recvfrom: No data available (WSAEWOULDBLOCK), ignoring..." << endl; } else { // 其他错误,记录错误信息 cerr << "UDP Recvfrom failed: " << errorCode << endl; } } // 重新绑定 UDP 事件(因为 WSAEventSelect 会重置事件状态) WSAEventSelect(udpsocket, udpEvent, FD_READ); } // 检查是否是 TCP 监听事件 else if (triggeredEvent == tcpEvent) { sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); SOCKET newsocket = accept(tcpsocket, (struct sockaddr*)&client_addr, &addr_len); if (newsocket == INVALID_SOCKET) { int errorCode = WSAGetLastError(); if (errorCode == WSAEWOULDBLOCK) { // 没有新的连接请求可接受,这是正常现象,不需要处理 cout << "Accept: No new connections available (WSAEWOULDBLOCK), ignoring..." << endl; } else { // 其他错误,记录错误信息 cerr << "Accept failed: " << errorCode << endl; } continue; } char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int client_port = ntohs(client_addr.sin_port); cout << "New TCP client connected: " << client_ip << ":" << client_port << ", socket: " << newsocket << endl; // 为新客户端创建事件对象并绑定 FD_READ | FD_CLOSE WSAEVENT clientEvent = WSACreateEvent(); WSAEventSelect(newsocket, clientEvent, FD_READ | FD_CLOSE); // 将新客户端套接字和事件对象加入列表 tcpClients.push_back(newsocket); tcpClientEvents.push_back(clientEvent); events.push_back(clientEvent); // 加入全局事件列表 } // 检查是否是 TCP 客户端事件 else { int clientIndex = index - 2; // 减去 UDP 和 TCP 监听事件 if (clientIndex >= 0 && clientIndex < tcpClients.size()) { SOCKET clientSocket = tcpClients[clientIndex]; WSAEVENT clientEvent = tcpClientEvents[clientIndex]; WSANETWORKEVENTS networkEvents; WSAEnumNetworkEvents(clientSocket, clientEvent, &networkEvents); if (networkEvents.lNetworkEvents & FD_READ) { char buffer[1024]; int valread = recv(clientSocket, buffer, sizeof(buffer), 0); if (valread > 0) { cout << "TCP Client " << clientSocket << " received: " << buffer << endl; if (send(clientSocket, buffer, valread, 0) != valread) { cerr << "TCP Send failed: " << WSAGetLastError() << endl; } else { cout << "TCP Client " << clientSocket << " sent back a message." << endl; } } else if (valread == 0) { cout << "TCP Client " << clientSocket << " disconnected." << endl; closesocket(clientSocket); WSACloseEvent(clientEvent); tcpClients.erase(tcpClients.begin() + clientIndex); tcpClientEvents.erase(tcpClientEvents.begin() + clientIndex); events.erase(events.begin() + 2 + clientIndex); // 移除事件 } } if (networkEvents.lNetworkEvents & FD_CLOSE) { cout << "TCP Client " << clientSocket << " closed." << endl; closesocket(clientSocket); WSACloseEvent(clientEvent); tcpClients.erase(tcpClients.begin() + clientIndex); tcpClientEvents.erase(tcpClientEvents.begin() + clientIndex); events.erase(events.begin() + 2 + clientIndex); } // 重新绑定事件 WSAEventSelect(clientSocket, clientEvent, FD_READ | FD_CLOSE); } } } // 清理 closesocket(udpsocket); closesocket(tcpsocket); WSACleanup(); return 0; }帮我看看这个代码有什么错误会导致这个问题
最新发布
10-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值