Test Winsock options

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;
}