Description
A server that echoes a line of string is implemented: the client sends a line of string, ending with ‘\\
‘, and after the server receives the line, it sends it back to the client intact.
There is a limit to the number of Sockets that Select can monitor under Windows. If it exceeds, one solution is to open another thread.
#ifndef FD_SETSIZE #define FD_SETSIZE 64 #endif /* FD_SETSIZE */
Code
#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;//Acceptance completed, can be sent 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*)( & amp;mPeerAddr), & amp;nAddrLen); if (INVALID_SOCKET == hClientSocket) {<!-- --> std::cout << "accept failed with error " << WSAGetLastError() << std::endl; } else {<!-- --> unsigned long nNoBlock = 0; ioctlsocket(hClientSocket, FIONBIO, & amp;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> & amp; pClient ) {<!-- --> if (!pClient) {<!-- --> return false; } char c = 0; //For testing, only read one character at a time int nRecvValue = recv(pClient->hSocket, & amp;c, 1, 0); if (nRecvValue > 0) {<!-- --> pClient->szRecv[pClient->nRecvSize] = c; pClient->nRecvSize + = 1; std::cout << "read one char: " << c << std::endl; if (c == '\\ ') {<!-- --> 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> & amp; pClient) {<!-- --> if (!pClient) {<!-- --> return false; } if (!pClient->bNeedWrite) {<!-- --> return true; } //For testing, only send one character at a time 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, & amp;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*)( & amp;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( & amp;writefds); FD_ZERO( & amp;exceptfds); FD_SET(hListenSocket, &readfds); //FD_SET(hListenSocket, & amp;writefds); //FD_SET(hListenSocket, & amp;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, & amp;readfds); if (pClientItem->bNeedWrite)//Otherwise select will always have events {<!-- --> FD_SET(pClientItem->hSocket, & amp;writefds); } \t\t\t FD_SET(pClientItem->hSocket, & amp;exceptfds); } int nRet = select(0, & amp;readfds, & amp;writefds, & amp;exceptfds, nullptr); std::cout << "select return with " << nRet << std::endl; if (nRet > 0) {<!-- --> //read if (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 & amp; & amp; FD_ISSET(pClientItem->hSocket, & amp;readfds)) {<!-- --> if (!do_read(pClientItem)) {<!-- --> it = g_Clients.erase(it); continue; } } + + it; } //write for (auto it = g_Clients.begin(); it != g_Clients.end();) {<!-- --> std::shared_ptr<ClientSocketItem> pClientItem = *it; if (pClientItem & amp; & amp; FD_ISSET(pClientItem->hSocket, & amp;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 & amp; & amp; FD_ISSET(pClientItem->hSocket, & amp;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; }