当前位置: 首页 > news >正文

测试Winsock的select

说明

实现了一个回显一行字符串的服务器:客户端发送一行字符串,一’\n’结尾,服务器接受完一行后就原封不动地发回给客户端。
windows下对select的能监控的Socket数量是有限制的,若超过,一种方案是再开一个线程。

#ifndef FD_SETSIZE
#define FD_SETSIZE      64
#endif /* FD_SETSIZE */

代码

#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <vector>
#include <memory>#pragma comment(lib, "Ws2_32.lib")struct ClientSocketItem
{ClientSocketItem(){hSocket = NULL;memset(szRecv, 0, sizeof(szRecv));nRecvSize = 0;bNeedWrite = false;nWriteOffset = 0;}SOCKET hSocket;char szRecv[1024];unsigned int nRecvSize;std::string strIp;bool bNeedWrite;//接受完成,可以发送unsigned int nWriteOffset = 0;
};std::vector<std::shared_ptr<ClientSocketItem>> g_Clients;void do_accept(SOCKET hListenSocket)
{sockaddr_in mPeerAddr = { 0 };int nAddrLen = sizeof(sockaddr);SOCKET hClientSocket = accept(hListenSocket, (sockaddr*)(&mPeerAddr), &nAddrLen);if (INVALID_SOCKET == hClientSocket){std::cout << "accept failed with error "<< WSAGetLastError() << std::endl;}else{unsigned long nNoBlock = 0;ioctlsocket(hClientSocket, FIONBIO, &nNoBlock);std::string strIpAddr = inet_ntoa(mPeerAddr.sin_addr);std::cout << "accept success, peer ip is " << strIpAddr.c_str() << std::endl;auto pClient = std::make_shared<ClientSocketItem>();pClient->hSocket = hClientSocket;pClient->strIp = strIpAddr;g_Clients.push_back(pClient);}
}bool do_read(const std::shared_ptr<ClientSocketItem>& pClient )
{if (!pClient){return false;}char c = 0; //测试用,每次只读一个字符int nRecvValue = recv(pClient->hSocket, &c, 1, 0);if (nRecvValue > 0){pClient->szRecv[pClient->nRecvSize] = c;pClient->nRecvSize += 1;std::cout << "read one char: " << c << std::endl;if (c == '\n'){std::cout << "read finished" << std::endl;pClient->bNeedWrite = true;}return true;}else if (0 == nRecvValue){std::cout << "peer client closed" << std::endl;closesocket(pClient->hSocket);return false;}else{int nError = WSAGetLastError();if (WSAEWOULDBLOCK != nError){std::cerr << "recv failed with error " << nError << std::endl;closesocket(pClient->hSocket);return false;}std::cout << "next recv" << std::endl;return true;}
}bool do_write(const std::shared_ptr<ClientSocketItem>& pClient)
{if (!pClient){return false;}if (!pClient->bNeedWrite){return true;}//测试用,每次只发送一个字符int nRet = send(pClient->hSocket, pClient->szRecv + pClient->nWriteOffset, 1, 0);if (nRet >= 0){std::cout << "send one char: " << pClient->szRecv[pClient->nWriteOffset] << std::endl;pClient->nWriteOffset += 1;if (pClient->nWriteOffset == pClient->nRecvSize){std::cout << "send finished, close client(" << pClient->strIp.c_str()<< ")" << std::endl;pClient->bNeedWrite = false;closesocket(pClient->hSocket);return false;}return true;}else{int nError = WSAGetLastError();if (WSAEWOULDBLOCK != nError){std::cerr << "send failed with error " << nError << std::endl;closesocket(pClient->hSocket);return false;}std::cout << "next send" << std::endl;return true;}
}int main(int argc, char* argv)
{WORD wVersionRequested = MAKEWORD(2, 2);WSADATA wsaData = { 0 };int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){return -1;}if (LOBYTE(wsaData.wVersion) != 2 ||HIBYTE(wsaData.wVersion) != 2){WSACleanup();return -1;}SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == hListenSocket){std::cerr << "create socket failed with error " << WSAGetLastError()<< std::endl;return -1;}sockaddr_in mSockAddrIn = { 0 };mSockAddrIn.sin_family = AF_INET;mSockAddrIn.sin_port = htons((u_short)8878);mSockAddrIn.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");if (SOCKET_ERROR == bind(hListenSocket, (sockaddr*)(&mSockAddrIn),sizeof(sockaddr))){std::cerr << "bind failed with error " << WSAGetLastError() << std::endl;return -1;}if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN)){std::cerr << "listen failed with error " << WSAGetLastError() << std::endl;return -1;}std::vector<SOCKET> mAllClients;while (true){fd_set readfds;fd_set writefds;fd_set exceptfds;FD_ZERO(&readfds);FD_ZERO(&writefds);FD_ZERO(&exceptfds);FD_SET(hListenSocket, &readfds);//FD_SET(hListenSocket, &writefds);//FD_SET(hListenSocket, &exceptfds);for (auto it = g_Clients.begin(); it != g_Clients.end(); ++it){std::shared_ptr<ClientSocketItem> pClientItem = *it;if (!pClientItem) continue;FD_SET(pClientItem->hSocket, &readfds);if (pClientItem->bNeedWrite)//否则select会一直有事件{FD_SET(pClientItem->hSocket, &writefds);}FD_SET(pClientItem->hSocket, &exceptfds);}int nRet = select(0, &readfds, &writefds, &exceptfds, nullptr);std::cout << "select return with " << nRet << std::endl;if (nRet > 0){//readif (FD_ISSET(hListenSocket, &readfds)){do_accept(hListenSocket);}for (auto it = g_Clients.begin(); it != g_Clients.end();){std::shared_ptr<ClientSocketItem> pClientItem = *it;if (pClientItem && FD_ISSET(pClientItem->hSocket, &readfds)){if (!do_read(pClientItem)){it = g_Clients.erase(it);continue;}}++it;}//writefor (auto it = g_Clients.begin(); it != g_Clients.end();){std::shared_ptr<ClientSocketItem> pClientItem = *it;if (pClientItem && FD_ISSET(pClientItem->hSocket, &writefds)){if (!do_write(pClientItem)){it = g_Clients.erase(it);continue;}}++it;}//end of for//error for (auto it = g_Clients.begin(); it != g_Clients.end();){std::shared_ptr<ClientSocketItem> pClientItem = *it;if (pClientItem && FD_ISSET(pClientItem->hSocket, &exceptfds)){std::cerr << "client socket except, close and remove it" << std::endl;closesocket(pClientItem->hSocket);it = g_Clients.erase(it);continue;}++it;}//end of for}else{std::cerr << "select failed with error " << WSAGetLastError() << std::endl;}}return 0;
}
http://www.lryc.cn/news/214615.html

相关文章:

  • CentOS 搭建 Hadoop3 高可用集群
  • ModuleNotFoundError: No module named ‘paddle.fluid.incubate.fleet‘
  • 【Java】Java中的引用类型
  • File类、方法递归
  • MySQL - 系统库之 sys
  • GoLong的学习之路(十七)基础工具之Gin框架使用JWT(前后端分离)
  • 【代码数据】2023粤港澳大湾区金融数学建模B题分享
  • 大数据之LibrA数据库系统告警处理(ALM-12006 节点故障)
  • poi兴趣点推荐数据集介绍
  • 把两个4点的结构相加
  • windows内存取证-中等难度-下篇
  • 代码随想录算法训练营第7天|454 四数相加II 383. 赎金信 15.三数之和 18 四数之和
  • 负载均衡深度解析:算法、策略与Nginx实践
  • 7. 一文快速学懂常用工具——Makefile
  • [ACTF2023]复现
  • HNU-编译原理-讨论课1
  • 【Linux】关于Nginx的详细使用,部署项目
  • 编写 navigation2 控制器插件
  • 计算机网络 第六章应用层
  • 人工智能领域CCF推荐国际学术刊物最新目录(全)
  • 实现基于 Azure DevOps 的数据库 CI/CD 最佳实践
  • 上海实习小记
  • uniapp实现路线规划
  • 飞利浦双串口51单片机485网关
  • 生态扩展:Flink Doris Connector
  • HarmonyOS(二)—— 初识ArkTS开发语言(上)之TypeScript入门
  • 从零开始实现神经网络(一)_NN神经网络
  • C语言 每日一题 Day10
  • C++继承——矩形和长方体
  • 代码随想录打卡第五十八天|● 583. 两个字符串的删除操作 ● 72. 编辑距离