advise for safely using remote client without exhausting ports - c++

I am working on a remote client program for Windows that sends data from a sensor unit to a server application over ethernet / internet connection. The client logs into the server with a username and password. Per the protocol the client then sends data one way to the server without ever expecting a response from the server. All works fine untill the wireless internet connection at a client gets broken. In my first version of the client I retried making a connect attept each loop but very often would run out of ports as Windows standard timeout was 4 minutes. I tried changing one client the other day. I set Windows registry to "TcpTimedWaitDelay" to 1 and rewrote the client app to close its socket on error then wait 1 second before attempting a reconnect. This seemed to be working but I did loose that client for a while today. I can't be certain if the wireless went down for the whole time it was offline or not.
I'm looking for advise on a good method when using this type of client that can be used reliably. Is there another way to prevent port exhaustion within the application code or is modifing Windows registry almost always needed? I can add pieces of my code but I have gone over my code itself in a recent question here. I'm looking for a more broad suggestion here.
void checkConnect(NTRIP& server)
{
time_f functionTime = getTimePrecise();
//1st check for recv or gracefully closed socket
char databuf[SERIAL_BUFFERSIZE];
fd_set Reader, Writer, Err;
TIMEVAL Timeout;
Timeout.tv_sec = 1; // timeout after 1 seconds
Timeout.tv_usec = 0;
FD_ZERO(&Reader);
FD_ZERO(&Err);
FD_SET(server.socket, &Reader);
FD_SET(server.socket, &Err);
int iResult = select(0, &Reader, NULL, &Err, &Timeout);
if(iResult > 0)
{
if(FD_ISSET(server.socket, &Reader) )
{
int recvBytes = recv(server.socket, databuf, sizeof(databuf), 0);
if(recvBytes == SOCKET_ERROR)
{
cout << "socket error on receive call from server " << WSAGetLastError() << endl;
closesocket(server.socket);
server.connected_IP = false;
}
else if(recvBytes == 0)
{
cout << "server closed the connection gracefully" << endl;
closesocket(server.socket);
server.connected_IP = false;
}
else //>0 bytes were received so read data if needed
{
cout << "received " << recvBytes << " bytes of data" << endl;
}
}
if(FD_ISSET(server.socket, &Err))
{
cout << "ip thread select returned socket in error group" << endl;
closesocket(server.socket); //what if dont close this socket, leave open for another loop
server.connected_IP = false;
}
}
else if(iResult == SOCKET_ERROR)
{
cout << "ip thread select returned SOCKET_ERROR " << WSAGetLastError() << endl;
closesocket(server.socket);
server.connected_IP = false;
}
//2nd check hard disconnect
if(server.connected_IP == true && functionTime - timer_send_keepalive >= 15.0)
{
timer_send_keepalive = functionTime;
char buf1[] = "hello";
cout << "checking send for error" << endl;
iResult = send(server_main.socket, buf1, sizeof(buf1), 0);
if(iResult == SOCKET_ERROR)
{
int lasterror = WSAGetLastError();
if(lasterror == WSAEWOULDBLOCK)
{
cout << "server send WSAEWOULDBLOCK" << endl;
}
else if(lasterror != WSAEWOULDBLOCK)
{
cout << "server testing connection send function error " << lasterror << endl;
closesocket(server.socket);
server.connected_IP = false;
}
}
else
{
cout << "sent out keep alive " << iResult << " bytes" << endl;
}
}//end send keep alive
}
send function
bool sendData(CHAR_MESSAGE& data)
{
bool check = false;
if(data.flag_toSend == true)
{
if(WaitForSingleObject(data.mutex, 0) != WAIT_FAILED)
{
if(server_main.connected_IP == true)
{
int iResult = send(server_main.socket, data.buffer, data.bufferLength, 0);
if(iResult == SOCKET_ERROR)
{
int lasterror = WSAGetLastError();
if(lasterror == WSAEWOULDBLOCK)
{
cout << "main server send WSAEWOULDBLOCK" << endl;
check = false;
}
if(lasterror != WSAEWOULDBLOCK)
{
cout << "server main send error " << lasterror << endl;
closesocket(server_main.socket);
server_main.connected_IP = false;
check = false;
}
}
else
{
check = true;
server_main.lastDataSendTime = getTimePrecise();
//{cout << "sent data to main server" << endl;}
}
}
if(server_backup.connected_IP == true)
{
int iResult = send(server_backup.socket, data.buffer, data.bufferLength, 0);
if(iResult == SOCKET_ERROR)
{
int lasterror = WSAGetLastError();
if(lasterror == WSAEWOULDBLOCK)
{
cout << "backup server send WSAEWOULDBLOCK" << endl;
//check = false;
}
if(lasterror != WSAEWOULDBLOCK)
{
cout << "server backup send error " << lasterror << endl;
closesocket(server_backup.socket);
server_backup.connected_IP = false;
}
}
else
{
server_backup.lastDataSendTime = getTimePrecise();
//{cout << "sent data to backup server" << endl;}
}
}
data.flag_toSend = false;
ReleaseMutex(data.mutex);
}//end obtained mutex
}//end data flag to send is true
return check;
}
login function
bool loginServer(NTRIP& datasource )
{
std::string sLogin = buildLogin(datasource);
char databuf[1030];
int iResult = send(datasource.socket, sLogin.c_str(), sLogin.length(), 0);
if(iResult == SOCKET_ERROR)
{
cout << "Send error = " << WSAGetLastError() << endl;
closesocket(datasource.socket);
return false;
}
else //not socket error
{
int flags = 0;
fd_set Read, Err;
TIMEVAL Timeout;
FD_ZERO(&Read);
FD_ZERO(&Err);
FD_SET(datasource.socket, &Read);
FD_SET(datasource.socket, &Err);
Timeout.tv_sec = 1;
Timeout.tv_usec = 0;
iResult = select(0, &Read, NULL, &Err, &Timeout);
if(iResult == 0)
{
cout << "loginServer function, select timeout" << endl;
closesocket(datasource.socket);
return false;
}
else if(FD_ISSET(datasource.socket, &Read) )
{
int recvBytes = recv(datasource.socket, databuf, sizeof(databuf), flags);
if(recvBytes == SOCKET_ERROR)
{
cout << "loginServer function, Error recv call " << WSAGetLastError() << endl;
closesocket(datasource.socket);
return false;
}
else if(recvBytes == 0) //server closed connection
{
cout << "loginServer function, server reclosed connection" << endl;
closesocket(datasource.socket);
return false;
}
else if(recvBytes > 0) //process response
{
std::string tempString;
for(int n=0; n<recvBytes; n++)
{
tempString += databuf[n];
}
if(tempString.compare(CONNECT_OK) || tempString.compare(HTTP_OK) )
{
cout << "server connected" << endl;
return true;
}
else if(tempString.compare(ERROR_1))
{
MessageBox(NULL, ERROR_1, "NTRIP connect Error", MB_OK);
closesocket(datasource.socket);
return false;
}
else if(tempString.compare(ERROR_2))
{
MessageBox(NULL, ERROR_2, "NTRIP connect Error", MB_OK);
closesocket(datasource.socket);
return false;
}
else if(tempString.compare(ERROR_3))
{
MessageBox(NULL, ERROR_3, "NTRIP connect Error", MB_OK);
closesocket(datasource.socket);
return false;
}
else if(tempString.compare(ERROR_4))
{
MessageBox(NULL, ERROR_4, "NTRIP connect Error", MB_OK);
closesocket(datasource.socket);
return false;
}
}
}
else if(FD_ISSET(socket, &Err) )
{
cout << "loginServer function, select call error" << endl;
closesocket(datasource.socket);
return false;
}
}//end not socket error on send login
closesocket(datasource.socket);
return false;
}
open a new socket
bool clientOpenSocket_connectServer(SOCKET& Socket, const char* serverADDR, const char* serverPORT)
{
int check;
if(Socket != INVALID_SOCKET) //if not closed then close socket
{
closesocket(Socket);
Socket = INVALID_SOCKET;
}
time_f timer = getTimePrecise();
while(getTimePrecise() - timer < 5.0){} //wait 5 second before reconnect attempt
//set Windows reg to 1 second TcpTimedWaitDelay
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //use IPv4
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
//get socket memory address info
check = getaddrinfo(serverADDR , serverPORT, &hints, &result);
if ( check != 0)
{
printf("Winsock getaddrinfo failed. %d\n", WSAGetLastError());
return false;
}
//prepare socket, sets IP4 or IP6, sock type and protocol used
for(ptr=result; ptr!=NULL; ptr=ptr->ai_next)
{
Socket = socket( result->ai_family, result->ai_socktype, result->ai_protocol);
if (Socket == INVALID_SOCKET)
{
printf("Socket failed initializing %d %d.\n", Socket, WSAGetLastError());
freeaddrinfo (result);
return false;
}
//now have a valid socket
check = ioctlsocket(Socket, FIONBIO, &NonBlock);
if (check == SOCKET_ERROR)
{
printf("client socket could not set nonblocking, with error: %d\n", WSAGetLastError());
closesocket(Socket);
freeaddrinfo(result);
return false;
}
//set sockets to no-linger on close
char value = 0;
check = setsockopt( Socket, SOL_SOCKET, SO_DONTLINGER, &value, sizeof( value ) );
if (check == SOCKET_ERROR)
{
cout << "client socket could not set options no-linger " << WSAGetLastError() << endl;
}
//disable nagle algorithym
if(disableNagleSockets == true)
{
value = 1;
check = setsockopt( Socket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof( value ) );
if (check == SOCKET_ERROR)
{
cout << "client socket could not set options " << WSAGetLastError() << endl;
}
else{cout << "Nagle Sockets set disabled" << endl;}
value = 0;
check = setsockopt( Socket, IPPROTO_TCP, SO_SNDBUF, &value, sizeof( value ) );
if (check == SOCKET_ERROR)
{
cout << "client socket could not set options " << WSAGetLastError() << endl;
}
}
//attempt connect
cout << "attempting connect" << endl;
check = connect(Socket, ptr->ai_addr, ptr->ai_addrlen);
if(check == SOCKET_ERROR )
{
check = WSAGetLastError();
if(check == WSAEWOULDBLOCK) // then set a timeout
{
fd_set Write, Err;
TIMEVAL Timeout;
int TimeoutSec = 10; // timeout after 10 seconds
FD_ZERO(&Write);
FD_ZERO(&Err);
FD_SET(Socket, &Write);
FD_SET(Socket, &Err);
Timeout.tv_sec = TimeoutSec;
Timeout.tv_usec = 0;
check = select(0, NULL, &Write, &Err, &Timeout);
if(check == 0)
{
printf("connect call to server, select call timeout elapsed\r\n");
closesocket(Socket);
freeaddrinfo(result);
return false;
}
else
{
if(FD_ISSET(Socket, &Write) )
{
freeaddrinfo(result);
cout << "socket opened to server, after wait" << endl;
return true;
}
if(FD_ISSET(Socket, &Err) )
{
printf("connect call to server, select call error state\r\n");
closesocket(Socket);
freeaddrinfo(result);
return false;
}
}
}
else if(check == WSAECONNREFUSED)
{
cout << "no server program at requested address " << serverADDR << endl;
closesocket(Socket);
freeaddrinfo(result);
return false;
}
else if(check == WSAEHOSTDOWN || check == WSAETIMEDOUT)
{
cout << "no server present at requested address " << serverADDR << endl;
closesocket(Socket);
freeaddrinfo(result);
return false;
}
else
{
cout << "connect call WSA error code " << check << endl;
closesocket(Socket);
freeaddrinfo(result);
return false;
}
}//end socket error
//else instant connection is good
cout << "socket opened to server" << endl;
freeaddrinfo(result);
return true;
}//end search for a socket address to use
freeaddrinfo(result); //no socket opened here
return false;
}//end of setup TCP port
So as of now it waits 5 seconds snce the last failed connection before attempting another. My earlier version tried a new connection each loop untill it connected. It seems right now that it may be working with the 5 second delay and setting Windows registry to close a port after 1 second.

Use the connection until it breaks, and then create another one.
If it breaks so often that you encounter port exhaustion you have a network problem, not a programming problem.
If you get a connect error you should sleep for increasing amounts of time on each failure, e.g. 1,2,4,8,... seconds.

Related

How to enable TCP_NODELAY option in both Server and Client?

I am implementing a communication system (tx, rx) using TCP, in windows 10. My problem is when tx sends a message to rx, the message is received with delay. When tx sends several messages, the rx starts receiving only after several messages are sent. My guess is that tx waits until its buffer gets full and then starts sending messages altogether (in my platform buffer length is 512).
As shown in the bellow picture, before receiving is started, this error appears:
ERROR receiving TCP, error #: 10014
I tried to solve this problem by enabling the TCP_NODELAY option so that the messages are being sent immediately (which was not successful). However, my knowledge of TCP is shallow and I am not sure if I am doing it correctly.
Here are my questions:
Does the delayed sending (or receiving) is related to the TCP_NODELAY option of the TCP?
If yes, am I enabling the TCP_NODELAY correctly as follows:
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
Am I putting the TCP_NODELAY option in the right place in both server and client codes?
Here are my codes for client:
size_t IPTunnel::ipTunnelSend_tcp(size_t process) {
size_t remaining = process;
size_t result{ 0 };
size_t sent{ 0 };
while (remaining > 0) {
result = send(clientSocket[0], &(ip_tunnel_buffer[0]) + sent, (int) remaining, 0);
if (result >= 0) {
remaining -= result;
sent += result;
}
else {
if (getLogValue()) {
if ( result == 0) std::cerr << "No data send through TCP" << std::endl;
else std::cerr << "ERROR sending TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return sent;
}
and:
bool IPTunnel::client_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
int resultt = setsockopt(clientSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes,
sizeof(int));
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
clientSocket.push_back(s);
if (clientSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Connect to the server
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons((u_short)localMachinePort);
inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);
int connResult{ -2 };
while (connResult != 0 || numberOfTrials == 0) {
connResult = connect(clientSocket[0], (sockaddr*)& hint, sizeof(hint));
if (connResult == SOCKET_ERROR)
{
std::cerr << "Can't connect to server, Err #" << WSAGetLastError() << std::endl;
std::cerr << "Waiting " << timeIntervalSeconds << " seconds." << std::endl;
Sleep(timeIntervalSeconds * 1000);
}
if (--numberOfTrials == 0) {
std::cerr << "Reached maximum number of attempts." << std::endl;
}
}
std::cerr << "Connected!\n";
return true;
}
Here are my codes for server:
size_t IPTunnel::ipTunnelRecv_tcp(size_t space) {
char* recv_buffer = &ip_tunnel_buffer[0];
size_t remaining{ 0 };
if (outputSignals[0]->getValueType() == (signal_value_type::t_message))
{
remaining = ip_tunnel_buffer.size();
}
else
{
remaining = space * outputSignals[0]->valueTypeSizeOf();
}
size_t received{ 0 };
while (remaining > 0) {
auto aux{ 0 };
aux = recv(serverSocket[1], recv_buffer + received, (int) remaining, 0);
if (aux > 0) {
received += aux;
remaining -= received;
}
else {
if (getLogValue()) {
if (aux == 0) std::cerr << "No data received through TCP" << std::endl;
else std::cerr << "ERROR receiving TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return received;
}
and:
bool IPTunnel::server_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverSocket.push_back(s);
if (serverSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Bind the socket
sockaddr_in hint;
hint.sin_family = AF_INET; // AF_INET=2, IPv4
inet_pton(AF_INET, localMachineIpAddress.data(), &hint.sin_addr.S_un.S_addr);
hint.sin_port = ntohs((u_short)localMachinePort);
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &hint.sin_addr.S_un.S_addr, ipAddress, INET_ADDRSTRLEN);
std::cerr << "The TCP server is running on IP address: " << ipAddress;
std::cerr << " Port: " << htons(hint.sin_port) << std::endl;
if (bind(serverSocket[0], (sockaddr*)& hint, sizeof(hint)) < 0) {
std::cerr << "Bind failed with error code #" << WSAGetLastError() << std::endl;
return false;
}
// STEP 4 - Listen on the socket for a client
if (listen(serverSocket[0], SOMAXCONN) == -1) {
std::cerr << "Listen error!" << std::endl;
return false;
}
// STEP 5 - Accept a connection from a client
sockaddr_in client;
int clientSize = sizeof(client);
s = accept(serverSocket[0], (sockaddr*) &client, &clientSize);
serverSocket.push_back(s);
// Provides information
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
std::cerr << host << " connected on port " << service << std::endl;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cerr << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY_Server";
return true;
}
Here are the codes for sending the message (tx):
bool LoadFromCommandWindow::runBlock(void) {
int space = outputSignals[0]->space();
if (!space) return false;
if (flag && flag1)
{
std::getline(std::cin, plainData);
}
if (plainData.length() == 0)
{
flag = false;
return false;
}
else
{
data = plainData.substr(k, paddedDataLength);
if (data.length() != paddedDataLength) paddedData = padTo(data, paddedDataLength, '\0');
else paddedData = data;
outputSignals[0]->bufferPut((std::byte*) paddedData.c_str(), paddedDataLength); \\ This line puts the message into buffer
k += data.length();
if (k != plainData.length()) flag1 = false;
else
{
flag1 = true;
k = 0;
}
}
return flag;
}
std::string LoadFromCommandWindow::padTo(std::string str, const size_t num, const char paddingChar = '\0')
{
if (num > str.size())
str.insert(str.size(), num - str.size(), paddingChar);
return str;
}
Here are the codes for receiving the message (rx):
bool PrintData::runBlock(void) {
int ready = inputSignals[0]->ready();
int space = outputSignals[0]->space();
int process = std::min(ready, space);
if (process == 0) return false;
inputSignals[0]->bufferGet((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = unpad(decryptedDataLength, decryptedData, '\0');
for (size_t i = 0; i < decryptedDataLength; i++)
{
std::cout << decryptedData[i];
}
std::cout << std::endl;
outputSignals[0]->bufferPut((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = 496;
return true;
}
Thank you!
socket should be
int data_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
it should be enough , but it depends
bulletproof stuff is to use whole buffer transfers with buffer size on system maximum
https://stackoverflow.com/a/64726689/7172363

Cause of duplicate and missing messages in multithreaded C++ winsock socket?

void TCPConnectionV5::startServer()
{
/* Initialize Winsock */
int start;
sockaddr_in SERVER;
SERVER.sin_family = AF_INET;
SERVER.sin_addr.s_addr = INADDR_ANY;
//SERVER.sin_port = htons(stoi(DEFAULT_PORT));
SERVER.sin_port = htons(1787);
start = WSAStartup(MAKEWORD(2, 2), &_wsaData);
if (start != 0)
{
cout << "Error on WSAStartup: " << start << endl;
}
/* Create socket that will connect to server */
_listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
_socketCollection.push_back(_listener);
if (_listener == INVALID_SOCKET)
{
cout << "Error creating socket to connect to server: " << WSAGetLastError() << endl;
WSACleanup();
}
/* Bind the socket */
start = bind(_listener, (sockaddr*)&SERVER, sizeof(SERVER));
if (start == SOCKET_ERROR)
{
cout << "Error on bind:" << WSAGetLastError() << endl;
closesocket(_listener);
WSACleanup();
}
/* Create the listener socket */
start = listen(_listener, 16);
if (start == SOCKET_ERROR)
{
cout << "Error on entering the listening state: " << start << endl;
closesocket(_listener);
WSACleanup();
}
printTime();
cout << "Server entered listening state" << endl;
/* Create the thread */
sockaddr_in client;
int clientSize = sizeof(client);
while (true)
{
SOCKET messager = accept(_listener, (struct sockaddr*)&client, &clientSize);
_socketCollection.push_back(messager);
locker.lock();
printTime();
if (messager != SOCKET_ERROR)
{
cout << "Client Connection success!" << endl;
cout << "Messager: " << messager << endl;
locker.unlock();
std::thread newThread([&] {this->exchange(messager); });
newThread.detach();
}
else
{
locker.unlock();
}
}
}
DWORD TCPConnectionV5::exchange(SOCKET messager)
{
int bytesSent = sendMessage(messager, msg);
if (bytesSent <= 0)
{
closesocket(messager);
return -1;
}
int bytesReceived = receiveMessage(messager);
if (bytesReceived <= 0)
{
closesocket(messager);
return -1;
}
}
I noticed that when the server connects with multiple clients, that there sometimes appear to be duplicate messages that send to some clients, which is accompanied by missing messages to another client application. I have mutex lock/unlock in place for sending/receiving messages, but what's causing these duplicate/missing messages? Is there some underlying issue I have to address regarding the threads?

Exact order of actions when running a server with threads

I need to create a server in c++, where it can respond to more than 1 customer, thus use threads.
This is my class Socket, where the actual communication is done:
typedef struct addrinfo addinfo;
class Socket
{
private:
WSADATA _wsaData;
int _iResult;
SOCKET _ListenSocket;
addrinfo* _result;
addrinfo _hints;
int _iSendResult;
char _recvbuf[BUF_LEN];
int _recvbuflen;
std::vector<SOCKET> _ClientSockets;
public:
Socket();
bool Setup();
bool CreateSocket();
bool Bind();
bool Listen();
bool Accept();
bool Send();
bool Shutdown();
~Socket();
};
Socket::Socket()
{
_ListenSocket = INVALID_SOCKET;
_ClientSockets.push_back(INVALID_SOCKET);
_result = NULL;
_recvbuflen = BUF_LEN;
}
bool Socket::Setup() {
_iResult = WSAStartup(MAKEWORD(2, 2), &_wsaData);
if (_iResult != 0) {
std::cout << "WSAStartup failed with error: " << _iResult << std::endl;
return false;
}
ZeroMemory(&_hints, sizeof(_hints));
_hints.ai_family = AF_INET;
_hints.ai_socktype = SOCK_STREAM;
_hints.ai_protocol = IPPROTO_TCP;
_hints.ai_flags = AI_PASSIVE;
_iResult = getaddrinfo(NULL, DEFAULT_PORT, &_hints, &_result);
if (_iResult != 0) {
std::cout << "getaddrinfo failed with error: " << _iResult << std::endl;
WSACleanup();
return false;
}
return true;
}
bool Socket::CreateSocket() {
_ListenSocket = socket(_result->ai_family, _result->ai_socktype, _result->ai_protocol);
if (_ListenSocket == INVALID_SOCKET) {
std::cout << "socket failed with error: " << WSAGetLastError() << std::endl;
freeaddrinfo(_result);
WSACleanup();
return false;
}
return true;
}
bool Socket::Bind() {
_iResult = bind(_ListenSocket, _result->ai_addr, (int)_result->ai_addrlen);
if (_iResult == SOCKET_ERROR) {
std::cout << "bind failed with error: " << WSAGetLastError() << std::endl;
freeaddrinfo(_result);
closesocket(_ListenSocket);
WSACleanup();
return false;
}
freeaddrinfo(_result);
return true;
}
bool Socket::Listen() {
_iResult = listen(_ListenSocket, SOMAXCONN);
if (_iResult == SOCKET_ERROR) {
std::cout << "listen failed with error: " << WSAGetLastError() << std::endl;
closesocket(_ListenSocket);
WSACleanup();
return false;
}
return true;
}
bool Socket::Accept() {
_ClientSockets.push_back(accept(_ListenSocket, NULL, NULL));
if (_ClientSockets[_ClientSockets.size() - 1] == INVALID_SOCKET) {
std::cout << "accept failed with error: " << WSAGetLastError() << std::endl;
closesocket(_ListenSocket);
WSACleanup();
return false;
}
return true;
}
bool Socket::Send() {
closesocket(_ListenSocket);
_iResult = recv(_ClientSockets[_ClientSockets.size() - 1], _recvbuf, _recvbuflen, 0);
if (_iResult > 0) {
_iSendResult = send(_ClientSockets[_ClientSockets.size() - 1], "Accepted", strlen("Accepted"), 0);
if (_iSendResult == SOCKET_ERROR) {
std::cout << "send failed with error: " << WSAGetLastError() << std::endl;
closesocket(_ClientSockets[_ClientSockets.size() - 1]);
WSACleanup();
return false;
}
}
else if (_iResult == 0)
std::cout << "Connection closing..." << std::endl;
else {
std::cout << "recv failed with error: " << WSAGetLastError() << std::endl;
closesocket(_ClientSockets[_ClientSockets.size() - 1]);
WSACleanup();
return false;
}
return true;
}
bool Socket::Shutdown() {
_iResult = shutdown(_ClientSockets[_ClientSockets.size() - 1], SD_SEND);
if (_iResult == SOCKET_ERROR) {
std::cout << "shutdown failed with error: " << WSAGetLastError() << std::endl;
closesocket(_ClientSockets[_ClientSockets.size() - 1]);
WSACleanup();
return false;
}
closesocket(_ClientSockets[_ClientSockets.size() - 1]);
WSACleanup();
return true;
}
Socket::~Socket()
{
}
this is the main file:
void interact(Socket* socket);
int main() {
Socket* socket = new Socket();
socket->Setup();
socket->CreateSocket();
socket->Bind();
socket->Listen();
while (1) {
socket->Accept();
std::thread trd(interact, socket);
trd.join();
system("PAUSE");
}
socket->Shutdown();
system("PAUSE");
return 0;
}
void interact(Socket* socket) {
std::cout << "Accepted" << std::endl;
socket->Send();
}
Communicating with the first client is fine, but after I try to get back to listening and finish communication with the last client, it gives me error code 10038 for accept and 10093 for recv. I can't understand what the error codes actually mean in my code. I suspect it might be with the way I'm using Accept. Is it supposed to be in the interact function, or before i call the thread? Can I have some directing?
So your effective control flow is:
while (1) {
socket->Accept(); // uses socket->_ListenSocket
interact(socket); // calls socket->Send()
socket->Send(); // calls closesocket(_ListenSocket);
since you deliberately closed the listening socket, why would you expect a subsequent call to accept to work?
Other notes:
you're passing the entire server socket to each client thread, which is much more than it needs. Each client task should only need its own client socket, and would then be unable to damage your server socket as it does here.
you're currently serializing everything by joining the thread, but if you want parallelism, you need to detach this instead. This will further break the existing model of each thread taking the last client socket from the Socket's array (without synchronization).
It's often a better idea to have a thread pool for processing tasks, and make each task a client request with its own socket. This way you're not creating and destroying threads all the time, and it forces you to think about what constitutes a self-contained task

Winsock single client server send and receive simultaneously

I have a problem with simple Winsock chat application in C++. I've written a code that enables the user to choose if he wants to send data or receive the data and depending on users choice, the appropriate functions are executed. What I would like to achieve, is that the user can BOTH send and receive data. How could I approach it? Please note that I don't want to use multiple client, I just want to send and receive data by server and send and receive data by client simultaneously.
Edit: I added my code.
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <stdio.h>
#include <ctime>
int main()
{
string IP;
int userType;
// Ask the user if he is server or client
cout << "Hi, which type of user are you? 1 - Server, 2 - Client" <<endl;
cin >> userType;
// depending on the declared user type, execute the appropriate code
if (userType == 1)
{
//initialize winsock and create a socket
WSAData wsaData; // initialize
iResult = WSAStartup(MAKEWORD(2,1), &wsaData);
if (iResult != NO_ERROR) // check for errors
cout << "Error at WSAStartup()" <<endl;
else
cout << "WSAStartup() is OK." <<endl;
// create socket
sockSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // create a socket
if (sockSocket == INVALID_SOCKET) // check for errors
{
cout << "Error at socket(): " << WSAGetLastError();
WSACleanup();
return true;
}
else
cout << "Socket() is OK." <<endl;
return true;
// bind to socket
service.sin_addr.s_addr = inet_addr("0.0.0.0");
service.sin_family = AF_INET;
service.sin_port = htons(55555);
if (bind(sockSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) // cheking for errors
{
cout << "Bind() failed." << endl;
closesocket(sockSocket);
return true;
}
else
cout << "Bind() is OK." <<endl;
// listen
listen(sockSocket, SOMAXCONN);
if (listen(sockSocket, 10) == SOCKET_ERROR) // check for errors
{
cout << "Error listening on socket." << endl;
return true;
}
else
cout << "Listen() is OK." <<endl;
//accept connection
servlen = sizeof(service);
cout << "Waiting for user to connect..." << endl;
acceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(acceptSocket = accept(sockSocket, (SOCKADDR*)&service, &servlen))
{
cout << "A coonnection was found" <<endl<<endl;
}
sockSocket = acceptSocket;
// receive messages
do
{
std::string message;
char Buffer[512];
iResult = recv(sockSocket, Buffer, 512, 0);
Buffer[iResult] = '\0';
message = Buffer;
currentDate();
std::cout << Buffer <<endl<<endl;
}while(iResult>0);
closesocket(sockSocket);
WSACleanup();
}
else if (userType == 2)
{
// exactly the same code as for server part to initialize and create socket
// ask for the ip the user wants to connect to
cout << "Hi what's the IP that you want to connect to?" <<endl;
cin >> IP;
// connect to socket
conService.sin_addr.s_addr = inet_addr(IP); // connect to the ipnuted IP
conService.sin_family = AF_INET;
conService.sin_port = htons(55555); // should the port also be the argument?
if (connect(sockSocket, (SOCKADDR*)&conService, sizeof(conService)) == SOCKET_ERROR) // check for errors
{
cout << "Failed to connect: " << WSAGetLastError();
WSACleanup();
return true;
}
else
{
cout << "Connected." <<endl;
}
// send messages
for (;;)
{
std::string message;
std::getline(std::cin, message);
unsigned int Length = strlen(message.c_str());
if(Length>512)
Length = 512;
currentDate();
iResult = send(sockSocket, message.c_str(),Length,0);
}
closesocket(sockSocket);
WSACleanup();
}
WSACleanup();
return 0;
}
Edit2: As Lemy suggested in the comment, I need to have a second thread (as the first one is simply what's going on in the main function). From what I understood, I can use CreateThread function. My problem of how to both send and receive messages is solved (I should create another thread), but now I have problem with implementing the solution. As I wrote in the comment below Lemy's answer, I get such error referring to the line where I use CreateThread function (HANDLE hThread = CreateThread(0,0,&ReadingThread,acceptSocket,0,&dwThreadID);):
invalid conversion from 'SOCKET {aka unsigned int}' to 'PVOID {aka void*}' [-fpermissive]| and
invalid conversion from 'DWORD {aka long unsigned int}' to 'PDWORD {aka long unsigned int*}' [-fpermissive]
Any hints for that? Hope this makes my question more specific(if not pls tell me what should I specify more).
You need to have the client and server continuously reading in the background for the lifetime of the connection, and then they can send data in parallel whenever needed. Move your reading logic into a separate thread, eg:
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <stdio.h>
#include <ctime>
DWORD WINAPI ReadingThread(LPVOID param)
{
SOCKET s = (SOCKET) param;
char Buffer[512];
int iResult;
do
{
iResult = recv(s, Buffer, 512, 0);
if (iResult <= 0) break;
Buffer[iResult] = '\0';
std::cout << "Recv: " << message << std::endl;
}
while (true);
return 0;
}
int main()
{
int userType;
HANDLE hThread;
DWORD dwThreadID;
//initialize winsock and create a socket
WSAData wsaData; // initialize
iResult = WSAStartup(MAKEWORD(2,1), &wsaData);
if (iResult != NO_ERROR) // check for errors
{
std::cout << "Error at WSAStartup()" << std::endl;
return 0;
}
std::cout << "WSAStartup() is OK." << std::endl;
// Ask the user if he is server or client
std::cout << "Hi, which type of user are you? 1 - Server, 2 - Client" << std::endl;
std::cin >> userType;
// depending on the declared user type, execute the appropriate code
if (userType == 1)
{
// create socket
sockSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // create a socket
if (sockSocket == INVALID_SOCKET) // check for errors
{
std::cout << "Error at socket(): " << WSAGetLastError() << std::endl;
WSACleanup();
return 0;
}
std::cout << "Socket() is OK." << std::endl;
// bind to socket
service.sin_addr.s_addr = INADDR_ANY;
service.sin_family = AF_INET;
service.sin_port = htons(55555);
if (bind(sockSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) // cheking for errors
{
std::cout << "Error at bind(): " << WSAGetLastError() << std::endl;
closesocket(sockSocket);
WSACleanup();
return 0;
}
std::cout << "Bind() is OK." << std::endl;
// listen
if (listen(sockSocket, 10) == SOCKET_ERROR) // check for errors
{
std::cout << "Error at listen(): " << WSAGetLastError() << std::endl;
closesocket(sockSocket);
WSACleanup();
return 0;
}
std::cout << "Listen() is OK." << std::endl;
//accept connection
servlen = sizeof(service);
std::cout << "Waiting for user to connect..." << std::endl;
acceptSocket = accept(sockSocket, (SOCKADDR*)&service, &servlen);
if (acceptSocket != INVALID_SOCKET)
{
std::cout << "Error at accept(): " << WSAGetLastError() << std::endl;
closesocket(sockSocket);
WSACleanup();
return 0;
}
std::cout << "A client has connected" << std::endl << std::endl;
// receive messages
hThread = CreateThread(NULL, 0, &ReadingThread, (void*)acceptSocket, 0, &dwThreadID);
if (!hThread)
{
std::cout << "Error at CreateThread(): " << GetLastError() << std::endl;
closesocket(acceptSocket);
closesocket(sockSocket);
WSACleanup();
return 0;
}
// send messages
do
{
std::string message;
if (!std::getline(std::cin, message))
break;
if (send(acceptSocket, msg.c_str(), msg.length(), 0) == SOCKET_ERROR)
{
std::cout << "Error at send(): " << WSAGetLastError() << std::endl;
break;
}
}
while (true);
closesocket(acceptSocket);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
closesocket(sockSocket);
WSACleanup();
}
else if (userType == 2)
{
// create socket
sockSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // create a socket
if (sockSocket == INVALID_SOCKET) // check for errors
{
std::cout << "Error at socket(): " << WSAGetLastError() << std::endl;
WSACleanup();
return 0;
}
std::cout << "Socket() is OK." << std::endl;
// ask for the ip the user wants to connect to
std::string IP;
std::cout << "Hi what's the IP that you want to connect to?" << std::endl;
std::cin >> IP;
// connect to socket
conService.sin_addr.s_addr = inet_addr(IP.c_str()); // connect to the ipnuted IP
conService.sin_family = AF_INET;
conService.sin_port = htons(55555); // should the port also be the argument?
if (connect(sockSocket, (SOCKADDR*)&conService, sizeof(conService)) == SOCKET_ERROR) // check for errors
{
std::cout << "Failed to connect: " << WSAGetLastError() << std::endl;
closesocket(sockSocket);
WSACleanup();
return 0;
}
std::cout << "Connected." << std::endl;
// receive messages
hThread = CreateThread(NULL, 0, &ReadingThread, (void*)sockSocket, 0, &dwThreadID);
if (!hThread)
{
std::cout << "Error at CreateThread(): " << GetLastError() << std::endl;
closesocket(sockSocket);
WSACleanup();
return 0;
}
// send messages
do
{
std::string message;
if (!std::getline(std::cin, message))
break;
if (send(sockSocket, msg.c_str(), msg.length(), 0) == SOCKET_ERROR)
{
std::cout << "Error at send(): " << WSAGetLastError() << std::endl;
break;
}
}
while (true);
closesocket(sockSocket);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
WSACleanup();
}
else
{
std::cout << "Invalid type entered!" << std::endl;
WSACleanup();
return 0;
}
return 0;
}

Winsock C++ Proxy

I'm trying to get this code to work:
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <WinSock2.h>
#pragma comment( lib, "ws2_32.lib" )
#include <Windows.h>
using namespace std;
int port = 5012;
SOCKET listen_sock;
SOCKET client_sock;
char FR_recv_buf [1048576] = "";
char recv_buf [102400] = "";
int Receive();
int Listen();
//function to initialize winsock
bool InitializeWinsock()
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != 0)
{
cout << "WSAStartup failed with error: " << iResult << endl;
return false;
}
else
{
cout << "WSAStartup successfully initialized." << endl;
return true;
}
}
int ForwardResponse()
{
if (send(client_sock, FR_recv_buf, sizeof(FR_recv_buf), 0) == SOCKET_ERROR)
{
cout << "Forward Response: send() failed with error: " << WSAGetLastError() << endl;
closesocket(client_sock);
//WSACleanup();
return 0;
}
else
{
cout << "Forward Response: send() success.\n";
//go back to begginning again?
Receive();
//CreateThread(0,0,(LPTHREAD_START_ROUTINE)Receive, 0, 0 ,0);
}
}
//Function to parse hostname from http request
string ParseHostname(char * buf)
{
size_t pos;
//string to hold hostname substring
string hostname_t;
//copy request to string for easier parsing
string httpheader = buf;
pos = httpheader.find("Host: ");//find "Host: " line
hostname_t = httpheader.substr(pos + 6);//copy to substring, not including "Host: ", just the hostname
pos = hostname_t.find("\r\n");// find end of line
hostname_t.erase(pos);//erase the rest of the string which is unwanted
return hostname_t;
}
//Function to forward HTTP request from browser to webserver
int ForwardRequest()
{
int bytes_received;
SOCKADDR_IN Dest;
SOCKET frecv_sock;
hostent *Host;
//parse hostname from http request
string hostname = ParseHostname(recv_buf);
if((Host=gethostbyname(hostname.c_str()))==NULL)
{
DWORD dwError = WSAGetLastError();
if (dwError != 0)
{
if(dwError == WSAHOST_NOT_FOUND)
{
cout << "Host " << hostname.c_str() << " not found.\n";
WSACleanup();
return FALSE;
}
else if (dwError == WSANO_DATA)
{
cout << "No data record found.\n";;
WSACleanup();
return FALSE;
}
else
{
cout << "Function failed with error: " << dwError << endl;
WSACleanup();
return FALSE;
}
}
}
else
{
cout << "Successfully connected to host: " << hostname.c_str() << endl;
//privmsg(wsockdl.sock,sendbuf,curchan);
}
Dest.sin_family=AF_INET;
Dest.sin_port=htons(80);
memcpy(&Dest.sin_addr,Host->h_addr,Host->h_length);
// Create a SOCKET for connecting to server
if((frecv_sock = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
{
cout << "Forward Request: Error at socket(), error code: " << WSAGetLastError() << endl;
closesocket(frecv_sock);
WSACleanup();
return FALSE;
}
// Connect to server
if(connect( frecv_sock,(SOCKADDR*)&Dest,sizeof(Dest))==SOCKET_ERROR)
{
cout << "Forward Request: connect() failed, error code: " << WSAGetLastError() << endl;
closesocket( frecv_sock);
WSACleanup();
return FALSE;
}
//send intercepted request to server
if (send(frecv_sock, recv_buf, strlen(recv_buf), 0) == SOCKET_ERROR)
{
cout << "Forward Request: send() failed with error: " << WSAGetLastError() << endl;
closesocket(frecv_sock);
WSACleanup();
return 0;
}
else
{
cout << "Forward Request: send() success.\n";
}
//receive request from server
do{
bytes_received = recv(frecv_sock,FR_recv_buf,sizeof(FR_recv_buf),0);
if (bytes_received > 0){
strcat (FR_recv_buf, "\0");
cout << "Forward Request: recv() success. Bytes received: " << bytes_received << endl;
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ForwardResponse, 0 ,0 ,0);
//ForwardResponse();
}
else if ( bytes_received == 0 ){
cout << "Forward Request: Connection closed\n";
closesocket(frecv_sock);
}
else if ( bytes_received == SOCKET_ERROR){
cout << "Forward Request: recv() failed with error: " << WSAGetLastError() << endl;
closesocket(frecv_sock);
WSACleanup();
return 0;
}
}while (bytes_received > 0);
}
//Function to accept connection and receive data from browser
int Receive()
{
SOCKADDR_IN csin;
int csin_len = sizeof(csin);
int iResult;
//accept client connection
client_sock = accept(listen_sock , (LPSOCKADDR)&csin, &csin_len);//pauses here to wait for connection from client
if (client_sock == INVALID_SOCKET) {
cout << "accept failed with error: "<< WSAGetLastError() << endl;
closesocket(client_sock);
WSACleanup();
return 1;
}
else{
cout << "Client connection from IP: " << inet_ntoa(csin.sin_addr) << ":" << csin.sin_port << endl;
}
CreateThread(0, 0 , (LPTHREAD_START_ROUTINE)Receive, 0 , 0 ,0); //Start another thread to accept.
do {
iResult = recv(client_sock, recv_buf, sizeof(recv_buf), 0);
if (iResult == SOCKET_ERROR) {
closesocket(client_sock);
WSACleanup();
cout << "Receive: recv() failed with error: "<< WSAGetLastError() << endl;
}
else if (iResult > 0){
//null terminate receive buffer
//recv_buf[iResult] = '\0';
strcat(recv_buf, "\0");
cout <<"Receive: Bytes received: " << iResult << endl;
//forward HTTP request from browser to web server
cout << recv_buf << endl;
HANDLE pChildThread = CreateThread(0, 0 , (LPTHREAD_START_ROUTINE)ForwardRequest, 0 , 0 ,0);
WaitForSingleObject(pChildThread,60000); //Wait for connection between proxy and remote server
CloseHandle(pChildThread);
}
else if ( iResult == 0 ){
cout << "Receive: Connection closed\n";
}
}while ( iResult > 0 );
return 0;
}
//Function which listens for incoming connections to the proxy
int Listen()
{
SOCKADDR_IN local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
//create socket for listening to
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//bind function associates a local address with a socket.
if (bind(listen_sock, (LPSOCKADDR)&local, sizeof(local)) == 0)
{
if (listen(listen_sock, 10) == 0)
{
cout << "Listening on: " << port << endl;
}
else
{
cout << "Error listening on socket.\n";
}
}
else{
cout << "bind() failed with error: "<< WSAGetLastError() << endl;
}
//accept and start receiving data from broswer
CreateThread(0, 0 , (LPTHREAD_START_ROUTINE)Receive, 0 , 0 ,0);
return 0;
}
int CloseServer()
{
closesocket(client_sock);
WSACleanup();
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
InitializeWinsock();
Listen();
cin.get();
return 0;
}
But it seems like the connection ends too early, or the recv() or send() functions fail. Nothing is displayed on my browser except that it couldn't connect. Can anyone spot the problem?
One major problem is that you only have one client socket. Each thread you create share the same client socket so if two connections are made before the first one is done, the first socket will be over-written with the second connection. Remember that threads share all memory in the process, including things like global variables.
Edit: Since you are using C++, why don't you encapsulate variables and functions in a class? And instead of allocating memory for buffers statically like you do, create them on the heap with new.
Edit 2
Simple multi-threaded server:
class Connection
{
public:
Connection()
: buffer(0), buffer_size(0)
{ }
void run(SOCKET sock);
privat:
SOCKET input_socket; // Socket we read from
SOCKET output_socket; // Socket we write to
char *buffer; // Buffer we read data into, and write data from
size_t buffer_size; // Total size of buffer (allocated memory)
size_t read_size; // Number of bytes read
void connect();
void recv();
void send();
};
void Connection::run(SOCKET sock)
{
input_socket = sock;
if (buffer == 0)
{
// Allocate buffer
}
// Connect to the real server
connect();
for (;;)
{
try
{
recv();
send();
}
catch (exception &e)
{
std::cerr << "Error: " << e.what() << '\n';
break;
}
}
// Clean up
delete [] buffer;
closesocket(output_socket);
closesocket(input_socket);
}
void Connection::recv()
{
// Read data into the buffer, setting "read_size"
// Like: read_size = recv(input_socket, buffer_size, 0);
// Throw exception on error (includes connection closed)
// NOTE: If error is WSAEWOULDBLOCK, set read_size to 0, don't throw exception
}
void Connection::send()
{
if (read_size > 0)
{
// Send data from the buffer
// Like: send(output_socket, buffer, read_size, 0))
// Throw exception on error
}
}
void Connection::connect()
{
// Connect to the real server
// Set the output_socket member variable
}
DWORD client_thread(LPVOID param)
{
SOCKET socket = (SOCKET) param;
// Make socket nonblocking
int mode = 1;
ioctlsocket(socket, FIONBIO, &mode)
// Main thread stuff
Connection connection;
connection.run(socket);
}
int main()
{
// Create master socket, and other initialization
for (;;)
{
SOCKET client_socket = accept(...);
CreateThread(0, 0 , (LPTHREAD_START_ROUTINE) client_thread,
(LPVOID) client_socket , 0 ,0);
}
// Clean up
}
Another major problem is that you are appending '\0' to the received buffer in the apparent expectation that send() will recognize it and stop sending from there, even though the size parameter to send() is sizeof(FR_recv_buf), which is what it will really respect. Another problem is that data items such as FR_recv_buf are instance variables instead of local variables, so you can't handle multuple concurrent connections.