multi-threaded chess using winsock - c++

I've been working on a small network based chess application. I managed to create a server that can handle multiple connections, however I don't know how to send data from one client to another.
Here is the partial Server implementation
//function to handle our Socket on its own thread.
//param- SOCKET* that is connected to a client
DWORD WINAPI HandleSocket(void* param)
{
string test;
SOCKET s = (SOCKET)param;
User temp;
temp._socket = (SOCKET)param;
temp._inGame = false;
userlist.add(&temp);
std::cout<<"connection"<<endl;
int bytesread = 0;
int byteswrite=0;
while(true)
{
//receive
bytesread = recv(s, reinterpret_cast<char*>(test.c_str()), BUF_LEN, 0);
//error check
if(bytesread == SOCKET_ERROR)
{
std::cout << WSAGetLastError();
//shutdown and close on error
shutdown(s, SD_BOTH);
closesocket(s);
return 0;
}
//check for socket being closed by the client
if(bytesread == 0)
{
//shutdown our socket, it closed
shutdown(s, SD_BOTH);
closesocket(s);
return 0;
}
byteswrite = send(s, "test" , 255 , 0);
if(byteswrite == SOCKET_ERROR)
{
std::cout << WSAGetLastError();
//shutdown and close on error
shutdown(s, SD_BOTH);
closesocket(s);
return 0;
}
test.clear();
}
}

Maybe you should start a thread for a new game when both players of the game are connected to the server. In that case you can deliver both sockets to the thread by the following way:
DWORD WINAPI HandleGame(void* param)
{
GameState* game = (GameState*)param;
SOCKET s1 = game->getSocketOfPlayer(1);
SOCKET s2 = game->getSocketOfPlayer(2);
...
// TODO: Forward game messages between clients (players).
...
delete game;
return 0;
}
Alternative solutions: Implement the server program in sigle thread.
In both cases you propably need select() function for waiting messages from several players simultaneously.

Related

IOCP Threads and GetQueuedCompletionStatus

Been trying to wrap my head around windows IOCP and have run into a few issues as many other people seem to be doing so.
So far what I'm trying to do is run a TCP IOCP server, which creates 3 accept sockets and has a thread for each waiting on a GetQueuedCompletionStatus. Below is the main thread loop for said threads.
The problem I've run into currently - I believe - is as follow.
Spawn 3 "worker" threads
Create 3 accept sockets in a loop and call AcceptEx() for each
Client 1 connects with no issue on Thread 1
GetQueuedCompletionStatus releases Thread 1 to continue on
Thread 1 now waits for data on WSARecv().
!!PROBLEM!! Client one sends data, Thread 1's WSARecv() seems to now send a completion packet
which is picked up BY ANOTHER THREAD OUT OF THE 3
Code hangs because of obvious reason (Thread 1 is waiting on the GQCS after its WSARecv(), but never receives it because it went to Thread 2 and Thread 2 is told to continue even though it hasn't actually received a client)
I say I believe this to be the issue because I've run it through debugger and outputted thread id's to console and have seen that right after starting a single client I get 2 outputs of "Accepted client on thread ..." with 2 different thread ids.
Also, when I was stepping through the code in the debugger, as soon as I went passed the WSARecv() in Thread 1, the code hit a breakpoint in Thread 2 at the GQCS line.
I'm clearly misunderstanding something about the use/function of GetQueuedCompletionStatus or something related to it. Is my fundamental methodology here just wrong or is this a easy fix I'm just not getting?
How can I get it such that, the WSARecv() sends its completion packet/signal thing to the write thread?
// Initialize Winsock...
// Create a handle for the completion port
hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long)0, 0);
if (hCompPort == NULL) {
wprintf(L"CreateIoCompletionPort failed with error: %u\n",
GetLastError());
WSACleanup();
return 1;
}
//Create worker threads
for (int i = 0; i < 3; i++)
{
threads.push_back(std::thread(WorkerThreadFun));
}
// Create a listening socket
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Associate the listening socket with the completion port
u_long ck = 111;
CreateIoCompletionPort((HANDLE)ListenSocket, hCompPort, (u_long)0, 0);
//----------------------------------------
// Bind the listening socket to the local IP address
// and port 27015
if (bind(ListenSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) {
wprintf(L"bind failed with error: %u\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//----------------------------------------
// Start listening on the listening socket
iResult = listen(ListenSocket, 100);
printf("Listening on address: %s:%d\n", ip, port);
iResult = WSAIoctl(ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx, sizeof(GuidAcceptEx),
&lpfnAcceptEx, sizeof(lpfnAcceptEx),
&dwBytes, NULL, NULL);
if (iResult == SOCKET_ERROR) {
wprintf(L"WSAIoctl failed with error: %u\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//pre-emptively create sockets for connections
for (int i = 0; i < 3; i++)
{
// Create an accepting socket
AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
//create Overlapped struct for AcceptEx and following that GetQueCompStatus
LPWSAOVERLAPPEDPLUS pOl = new WSAOVERLAPPEDPLUS();
//Initialise the "extended" overlapped struct with appropriate data
memset(pOl, 0, sizeof(WSAOVERLAPPEDPLUS));
pOl->operation = OP_ACCEPTEX;
pOl->client = AcceptSocket;
pOl->listenSocket = ListenSocket;
DWORD expected = sizeof(struct sockaddr_in) + 16;
int buflen = (sizeof(SOCKADDR_IN) + 16) * 2;
char* pBuf = new char[buflen];
memset(pBuf, 0, buflen);
// Empty our overlapped structure and accept connections.
//memset(&olOverlap, 0, sizeof(olOverlap));
bRetVal = lpfnAcceptEx(ListenSocket, AcceptSocket, pBuf,
0, //0 to avoid waiting to read data from a send()
expected, expected,
&pOl->dwBytes,
&pOl->ProviderOverlapped);
if (bRetVal == FALSE)
{
int err = WSAGetLastError();
if (err != ERROR_IO_PENDING)
{
wprintf(L"AcceptEx failed with error: %u\n", WSAGetLastError());
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
}
}//for
WorkerThreadFunc()
while (TRUE)
{
bOk = GetQueuedCompletionStatus(hCompPort, &bytes_transferred, (PULONG_PTR)&completion_key, &pOverlapped, INFINITE);
std::thread::id this_id = std::this_thread::get_id();
std::cout << "Accepted client on thread" << this_id << "Going to sleep for 5 seconds" << std::endl;
//std::this_thread::sleep_for(std::chrono::seconds(5));
if (bOk) {
// Process a successfully completed I/O request
if (completion_key == 0) {
// Safe way to extract the customized structure from pointer
// is to use 'CONTAINING_RECORD'. Read more on 'CONTAINING_RECORD'.
WSAOVERLAPPEDPLUS* pOl = CONTAINING_RECORD(pOverlapped, WSAOVERLAPPEDPLUS, ProviderOverlapped);
if (pOl->operation == OP_ACCEPTEX)
{
if (hCompPort2 == NULL)
{
hCompPort2 = CreateIoCompletionPort((HANDLE)pOl->client, hCompPort, (u_long)&this_id, 0);
// hCompPort2 should be hCompPort if this succeeds
if (hCompPort2 == NULL)
{
wprintf(L"CreateIoCompletionPort associate failed with error: %u\n",
GetLastError());
closesocket(pOl->client);
closesocket(pOl->listenSocket);
WSACleanup();
break;
}//if: CreateIOCP error
}//if: accept socket already IOCP
// Before doing any WSASend/WSARecv, inherit the
// listen socket properties by calling 'setsockopt()'
setsockopt(pOl->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char*)&pOl->listenSocket, sizeof(pOl->listenSocket));
DWORD& recvbytes = pOl->dwBytes;
char buf[MAX_BUF];
WSABUF wsabuf = { MAX_BUF, buf };
DWORD flags = 0;
if (WSARecv(pOl->client, &wsabuf, 1, &pOl->dwBytes, &flags, &pOl->ProviderOverlapped, NULL) == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
printf("WSARecv failed with error: %d\n", WSAGetLastError());
//return 1;
}
}
bool qresult = GetQueuedCompletionStatus(hCompPort, &pOl->dwBytes,
(PULONG_PTR)&completion_key, &pOverlapped, INFINITE);
if (!qresult)
{
DWORD err = GetLastError();
printf("* error %d getting completion port status!!!\n", err);
}
int iResult = WSAGetOverlappedResult(pOl->client, &pOl->ProviderOverlapped, &pOl->dwBytes, FALSE, &flags);
if (iResult == FALSE) {
wprintf(L"WSARecv operation failed with error: %d\n", WSAGetLastError());
//return 1;
break;
}
wsabuf.buf[recvbytes] = '\0';
std::cout << "Bytes received: " << recvbytes << std::endl;
std::wcout << "Bytes create: " << wsabuf.buf << std::endl;
}
delete pOl;
}
}
else {
// Handle error ...
}
}
}

How to connect multiple clients to a single server in c++ on Windows - Visual Studio? [duplicate]

This question already has answers here:
Single TCP/IP server that handles multiple clients (in C++)?
(2 answers)
Closed 5 years ago.
I have written a server program in C++ and also a client program in C++.
Both are working fine but when if one client is communicating with the server then other client can't get connected to the same server. If suppose when I close the client 1 then also my 2nd client can't connect to the server. I have started my Server with multiple threads to connect to multiple clients but only one client is connecting.
MY server Program:
#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib")
static const int num_of_threads = 2;
void client_disconnected(SOCKET Socket);
void start_server()
{
WSADATA WsaDat;
if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
{
std::cout<<"WSA Initialization failed!\r\n";
WSACleanup();
system("PAUSE");
}
SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
std::cout<<"Socket creation failed.\r\n";
WSACleanup();
system("PAUSE");
}
SOCKADDR_IN serverInf;
serverInf.sin_family=AF_INET;
serverInf.sin_addr.s_addr=INADDR_ANY;
serverInf.sin_port=htons(8888);
if(bind(Socket,(SOCKADDR*)(&serverInf),sizeof(serverInf))==SOCKET_ERROR)
{
std::cout<<"Unable to bind socket!\r\n";
WSACleanup();
system("PAUSE");
}
listen(Socket,3);
SOCKET TempSock=SOCKET_ERROR;
while(TempSock==SOCKET_ERROR)
{
std::cout<<"Waiting for incoming connections...\r\n";
Sleep(5000);
TempSock=accept(Socket,NULL,NULL);
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode=1;
ioctlsocket(Socket,FIONBIO,&iMode);
Socket=TempSock;
std::cout<<"Client connected!\r\n\r\n";
// Main loop
for(;;)
{
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0)
{
client_disconnected(Socket);
break;
}
char *szMessage="Welcome to the server!\r\n";
send(Socket,szMessage,strlen(szMessage),0);
Sleep(2000);
}
}
void client_disconnected(SOCKET Socket)
{
std::cout<<"Client disconnected!\r\n";
// Shutdown our socket
shutdown(Socket,SD_SEND);
// Close our socket entirely
closesocket(Socket);
WSACleanup();
}
int main()
{
//starting multiple threads for invoking server
std::thread threads[num_of_threads];
//This statement will launch multiple threads in loop
for (int i = 0; i < num_of_threads; ++i) {
threads[i] = std::thread(start_server);
Sleep(2000);
}
for (int i = 0; i < num_of_threads; ++i) {
threads[i].join();
}
return 0;
}
My Client Program:
#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include "client.h"
#pragma comment(lib,"ws2_32.lib")
void MultipleClient :: receiveToClient(void*data)
{
WSADATA WsaDat;
if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
{
std::cout<<"Winsock error - Winsock initialization failed\r\n";
WSACleanup();
system("PAUSE");
}
// Create our socket
SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
std::cout<<"Winsock error - Socket creation Failed!\r\n";
WSACleanup();
system("PAUSE");
}
// Resolve IP address for hostname
struct hostent *host;
if((host=gethostbyname("localhost"))==NULL)
{
std::cout<<"Failed to resolve hostname.\r\n";
WSACleanup();
system("PAUSE");
}
// Setup our socket address structure
SOCKADDR_IN SockAddr;
SockAddr.sin_port=htons(8888);
SockAddr.sin_family=AF_INET;
SockAddr.sin_addr.s_addr=*((unsigned long*)host->h_addr);
// Attempt to connect to server
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr))!=0)
{
std::cout<<"Failed to establish connection with server\r\n";
WSACleanup();
system("PAUSE");
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode=1;
ioctlsocket(Socket,FIONBIO,&iMode);
// Main loop
for(;;)
{
// Display message from server
char buffer[1000];
memset(buffer,0,999);
int inDataLength=recv(Socket,buffer,1000,0);
std::cout<<buffer;
//end client when server is disconnected
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0)
{
std::cout<<"Winsock error code: "<<nError<<"\r\n";
std::cout<<"Server disconnected!\r\n";
// Shutdown our socket
shutdown(Socket,SD_SEND);
// Close our socket entirely
closesocket(Socket);
break;
}
Sleep(2000);
}
WSACleanup();
system("PAUSE");
}
class Client{
public:
static unsigned int __stdcall receiveMessageThread(void *p_this)
{
MultipleClient* mc = static_cast<MultipleClient*>(p_this);
mc-> receiveToClient(p_this); // Non-static member function!
return 0;
}
void startThread()
{
HANDLE myhandleA;
myhandleA = (HANDLE)_beginthreadex(0,0,&Client::receiveMessageThread,this,0, 0);
WaitForSingleObject(myhandleA, INFINITE);
}
};
int main(void)
{
Client *c = new Client;
c->startThread();
return 0;
}
Please help me how to make multiple clients to connect with a single server. A sample code will be very useful(sorry for asking).
Your way of distributing work among threads is wrong.
You want one thread to open the listening socket and wait for incoming connections there. Note that you cannot have more than one listening socket per port, so you definitely don't want multiple threads trying to listen on the same port simultaneously.
If a connection comes in, accept will give you a new socket object. You still have the original listening socket, which waits for new connections, but you also have a second socket now with an already established connection to a client.
Now you can split the work: Have one thread go back to calling listen on the original socket and await new connections, while the other thread grabs the new socket and performs the necessary I/O to interact with the client.
In this simple scheme, you will always have one thread per client connection plus an additional thread for the listening socket. Since all of these threads will spent a large amount of time just waiting for network I/O to complete, you can use asynchronous I/O to share the workload between fewer (or even a single) threads, but that is slightly more complex to pull off, so I'd suggest you leave that for a second draft.
Using threads. Use one thread for socket server to accept all the Request Connections that coming from clients.
Each thread will have the socket that was assigned by method accept something like this:
Server. This method (run) is running under a new thread. I don't show the method connectionRequest to be practical but keep in mind that this method create the new thread that will attend the Remote End Point.
void SocketServer::run()
{
int err = 0;
err = initSocketServer();
//Fallo el inicio de socket server.
if (err != NO_ERROR)
{
stringstream out;
out << "There was a problem to bind Native Port: " << _port;
log->writeError(out.str());
bKeepRunning = false;
}
else
bKeepRunning = true;
stringstream out;
out << "Native Port stablished on port: " << _port;
log->writeInfo(out.str());
while (bKeepRunning)
{
stringstream out;
SOCKET socket;
socket = accept(server, NULL,NULL);
if (bKeepRunning)
{
bool reuseadd = true;
bool keepAlive = true;
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseadd, sizeof(bool));
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&keepAlive, sizeof(bool));
//This method will create a new Thread wihc recives the socket.
connectionRequest(socket);
// It is ready to accept the nexto connection...
}
}
serverStoped();
}
Attending the Remote End Point. The new thread that has been created by Connection request, must be attendig the Remote End Point to recive a send. Here is a sample on how the execute the other
It isn't my intention to show how to send and recive data by sockets.
void SocketClient::run()
{
stringstream out;
bKeepRunning = true;
bool wasClosed = false;
int error = 0;
error = 0;
do
{
if(bSocketSet)
log->writeDebug(_dlevel,"Socket Previamente asignado, No hay conexion");
else
log->writeDebug(_dlevel, "SIN Socket Previamente asignado, Se conectara");
if(!bSocketSet) //If the socket has not been set
bConnected = openConnection();
if (bConnected)
{
if (!bSocketSet) //If the socket has not been set
{
out.str("");
out.clear();
out << "Connected to Server [" << _host << ":" << _port << "]";
log->writeInfo(out.str());
}
//The readMessage will performed a loop to read data an report.
error = readMessage(&wasClosed);
if ((error != 0 && bKeepRunning) || wasClosed)
{
out.str("");
out.clear();
out << "There was an error on reading data. The connection colud be closed.";
log->writeError(out.str());
//if(!wasClosed)
//closeConnection();
bConnected = false;
}
} // if (bConnected)
else
{
if (!bSocketSet)
{
out.str("");
out.clear();
out << "It was not possible to connect to Server [" << _host << ":" << _port << "]";
log->writeError(out.str());
waitForResume(15000); //Wait for 15 second to try to reconect
}
} //Else Not Connected
} while (bKeepRunning && !bSocketSet); //do while The socket is not a Socket set by SocketServer
if (bConnected)
closeConnection();
}//SocketClient::run()
By each Thread that attend each Remote End Point you can send a recive many data as the protocol sets it but at the end you must close the connection to release the socket to be used as soon as possible by OS.

C++ Socket server application control

I am working on a application which works by received commands (API calls) over a socket connection. The application has two socket server with two different port numbers. As said the application is acting on the received commands (string). What is the best way to setup the socket server(s)? The socket server object is been build in a class file and contains two threads (listen for connections, handling the new connected client). The question is, what will be the best way to use the received commands/data so that the application will respond to it. Should I buildin a buffer (vector of string) to store the received data, read this buffer in the main loop of the application and after the reading clear the vector? Or is there a better/ other way?
The code of the socket server class is:
void SocketServer::Startup()
{
WSADATA wsa;
WSAStartup(0x0202, &wsa);
this->WorkerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
this->EndPnt.sin_addr.s_addr = INADDR_ANY;
this->EndPnt.sin_family = AF_INET;
this->EndPnt.sin_port = htons(this->m_port);
this->Enabled = true;
this->ID = 0;
bind(this->WorkerSocket, (SOCKADDR*)&this->EndPnt, sizeof(this->EndPnt));
printf("[SocketServer]Bound on %d..\n", this->m_port);
listen(this->WorkerSocket, this->Backlog);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerMainThread, this, NULL, NULL);
}
void SocketServer::WaitForConnections(SocketServer * Ser)
{
while(Ser->Enabled)
{
SOCKET accptsock = accept(Ser->WorkerSocket, NULL, NULL);
printf("[SocketServer]Client connected.\n");
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerDataThread, (LPVOID)accptsock, NULL, NULL);
}
}
void SocketServer::WaitForData(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
char *message = "ECHO Daemon v1.0 \r\n";
send(sock, message, strlen(message), 0);
int res;
char buffer[255];
while(true)
{
res = recv(sock, buffer, sizeof(buffer), 0);
if( res == SOCKET_ERROR)
{
int error_code = WSAGetLastError();
if(error_code == WSAECONNRESET)
{
//Somebody disconnected , get his details and print
cout << "[SocketServer]Client disconnected" << endl;
//Close the socket and mark as 0 in list for reuse
closesocket(sock);
ExitThread(0);
}
else
{
closesocket(sock);
ExitThread(0);
}
}
if(res == 0)
{
closesocket(sock);
ExitThread(0);
}
else
{
cout << buffer << endl;
// insert data to vector here
send(sock , buffer , sizeof(buffer) , 0 );
}
}
}
DWORD WINAPI SocketServer::ServerMainThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForConnections((SocketServer*)lParam);
return 0;
}
DWORD WINAPI SocketServer::ServerDataThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForData((LPVOID)lParam);
return 0;
}
The vector it needs to push the data to is called "Buffer", "Buffer.push_back(buffer)";

Problems with my winsock application

I'm having some problems with my Winsock application.
As it currently stands, when my client first connects to the server, the welcome message is sent fine but thats when it screws up. After that initial message its like the program goes into turbo mode.
Each client has a vector that stores messages that need to be sent, to debug I have it output how many messages are left to send and heres what the latest one says:
Successfully sent message. 1 messages left.
Successfully sent message. 1574803 messages left
................... (Counts down)
Successfully sent message. 1574647 messages left
Client connection closed or broken
EDIT: Managed to place some output code in the right place, for some reason when it starts sending update messages to clients, it starts sending the same message over and over.
Heres some code, am only posting the cpp's, 'Network::Update' is run by a thread in 'Game'
-- Server --
Main.cpp
while(true)
{
m_Game -> Update();
Sleep(500);
}
Network.cpp
#include "Network.h"
Network::Network()
{
}
Network::~Network()
{
closesocket(m_Socket);
}
Network::Network(char* ServerIP, int ServerPort)
{
Initialise(ServerIP, ServerPort);
}
void Network::Initialise(char* ServerIP, int ServerPort)
{
//
// Initialise the winsock library to 2.2
//
WSADATA w;
int error = WSAStartup(0x0202, &w);
if ((error != 0) || (w.wVersion != 0x0202))
{
MessageBox(NULL, L"Winsock error. Shutting down.", L"Notification", MB_OK);
exit(1);
}
//
// Create the TCP socket
//
m_Socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_Socket == SOCKET_ERROR)
{
MessageBox(NULL, L"Failed to create socket.", L"Notification", MB_OK);
exit(1);
}
//
// Fill out the address structure
//
m_Address.sin_family = AF_INET;
m_Address.sin_addr.s_addr = inet_addr(ServerIP);
m_Address.sin_port = htons(ServerPort);
//
// Bind the server socket to that address.
//
if (bind(m_Socket, (const sockaddr *) &m_Address, sizeof(m_Address)) != 0)
{
MessageBox(NULL, L"Failed to bind the socket to the address.", L"Notification", MB_OK);
exit(1);
}
//
// Make the socket listen for connections.
//
if (listen(m_Socket, 1) != 0)
{
MessageBox(NULL, L"Failed to listen on the socket.", L"Notification", MB_OK);
exit(1);
}
m_ID = 1;
}
void Network::Update()
{
//
// Structures for the sockets
//
fd_set readable, writeable;
FD_ZERO(&readable);
FD_ZERO(&writeable);
//
// Let the socket accept connections
//
FD_SET(m_Socket, &readable);
//
// Cycle through clients allowing them to read and write
//
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
{
Client *client = *it;
if (client->wantRead())
{
FD_SET(client->sock(), &readable);
}
if (client->wantWrite())
{
FD_SET(client->sock(), &writeable);
}
}
//
// Structure defining the connection time out
//
timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 500000;
//
// Check if a socket is readable
//
int count = select(0, &readable, &writeable, NULL, &timeout);
if (count == SOCKET_ERROR)
{
ExitProgram(L"Unable to check if the socket can be read.\nREASON: Select failed");
}
//
// Check if a theres an incoming connection
//
if (FD_ISSET(m_Socket, &readable))
{
//
// Accept the incoming connection
//
sockaddr_in clientAddr;
int addrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(m_Socket, (sockaddr *) &clientAddr, &addrSize);
if (clientSocket > 0)
{
// Create a new Client object, and add it to the collection.
Client *client = new Client(clientSocket);
m_Clients.push_back(client);
// Send the new client a welcome message.
NetworkMessage message;
message.m_Type = MT_Welcome;
client->sendMessage(message);
m_ID++;
}
}
//
// Loop through clients
//
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ) // note no ++it here
{
Client *client = *it;
bool dead = false;
//
// Read data
//
if (FD_ISSET(client->sock(), &readable))
{
dead = client->doRead();
}
//
// Write data
//
if (FD_ISSET(client->sock(), &writeable))
{
dead = client->doWrite();
}
//
// Check if the client is dead (Was unable to write/read packets)
//
if (dead)
{
delete client;
it = m_Clients.erase(it);
}
else
{
++it;
}
}
}
void Network::sendMessage(NetworkMessage message)
{
//Loop through clients and send them the message
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ) // note no ++it here
{
Client *client = *it;
client->sendMessage(message);
}
}
Client.cpp
#include "Client.h"
Client::Client(SOCKET sock)
{
m_Socket = sock;
send_count_ = 0;
}
// Destructor.
Client::~Client()
{
closesocket(m_Socket);
}
// Process an incoming message.
void Client::processMessage(NetworkMessage message)
{
// PROCESSING NEEDS TO GO HERE
}
// Return the client's socket.
SOCKET Client::sock()
{
return m_Socket;
}
// Return whether this connection is in a state where we want to try
// reading from the socket.
bool Client::wantRead()
{
// At present, we always do.
return true;
}
// Return whether this connection is in a state where we want to try-
// writing to the socket.
bool Client::wantWrite()
{
// Only if we've got data to send.
//return send_count_ > 0;
return Messages.size() > 0;
}
// Call this when the socket is ready to read.
// Returns true if the socket should be closed.
bool Client::doRead()
{
return false;
}
// Call this when the socket is ready to write.
// Returns true if the socket should be closed.
bool Client::doWrite()
{
/*int count = send(m_Socket, send_buf_, send_count_, 0);
if (count <= 0)
{
printf("Client connection closed or broken\n");
return true;
}
send_count_ -= count;
// Remove the sent data from the start of the buffer.
memmove(send_buf_, &send_buf_[count], send_count_);
return false;*/
int count = send(m_Socket, (char*)&Messages[0], sizeof(NetworkMessage), 0);
if( count <= 0 )
{
printf("Client connection closed or broken\n");
return true;
}
Messages.pop_back();
printf(" Successfully sent message. %d messages left.\n", Messages.size() );
return false;
}
void Client::sendMessage(NetworkMessage message)
{
/*if (send_count_ + sizeof(NetworkMessage) > sizeof(send_buf_))
{
ExitProgram(L"Unable to send message.\nREASON: send_buf_ full");
}
else
{
memcpy(&send_buf_[send_count_], message, sizeof(NetworkMessage));
send_count_ += sizeof(NetworkMessage);
printf(" Size of send_count_ : %d \n", send_count_);
}*/
Messages.push_back(message);
}
Game.cpp
void Game::Update()
{
tempPlayer -> ChangePosition(1, 1); //Increase X and Y pos by 1
Dispatch();
printf("Update\n");
}
void Game::Dispatch()
{
NetworkMessage temp;
temp.m_Type = MT_Update;
temp.m_ID = tempPlayer->getID();
temp.m_positionX = tempPlayer->getX();
temp.m_positionY = tempPlayer->getY();
m_Network->sendMessage(temp);
}
There are a few problems with your code.
The main problem is that Network::sendMessage() runs an infinite loop. Your it iterator is never incremented (there is a comment saying as much - the comment is doing the wrong thing!), so the loop sends the same NetworkMessage to the same Client over and over without ever stopping. Do this instead:
void Network::sendMessage(NetworkMessage message)
{
//Loop through clients and send them the message
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
{
...
}
}
Client::doWrite() is using pop_back() when it should be using pop_front() instead. If there are multiple pending messages in the vector, you will lose messages. Rather than using the [] operator to pass a NetworkMessage directly to send(), you should pop_front() the first pending message and then send() it:
bool Client::doWrite()
{
NetworkMessage message = Messages.pop_front();
int count = send(m_Socket, (char*)&message, sizeof(NetworkMessage), 0);
if( count <= 0 )
{
printf("Client connection closed or broken\n");
return true;
}
printf(" Successfully sent message. %d messages left.\n", Messages.size() );
return false;
}
Your dead check in Network::Update() has a small logic hole in it that can prevent you from deleting dead clients correctly if doRead() returns true and then doWrite() return false. Do this instead:
bool dead = false;
//
// Read data
//
if (FD_ISSET(client->sock(), &readable))
{
if (client->doRead())
dead = true;
}
//
// Write data
//
if (FD_ISSET(client->sock(), &writeable))
{
if (client->doWrite())
dead = true;
}

Nonblocking sockets even if not explicitly setting them as nonblocking

I have a TCP application written in C++, where a client and a server exchange data. I've istantiated a socket, believing that it would have been blocking by default; on the contrary, after server waits for a client, I have that client calls the recv function without waiting for data. This is the code in which I inizialize the socket fr the client.
int TCPreceiver::initialize(char* address, int port)
{
sock = socket (AF_INET, SOCK_STREAM, 0);
cout << "Socket: " << sock << endl;
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons (port);
target.sin_addr.s_addr = inet_addr(address);
int fails=0;
while (connect(sock, (sockaddr*) &target, sizeof(target)) == -1)
{
fails++;
if (fails==10)
{
close(sock);
cout << "Error with connection to the server, try again"<< endl;
exit(-1);
}
}
cout << "Client connected (control channel)" << endl;
unsigned char text[10]; //Request message
//fill text[]
if(send(sock, (char*)text, 10, 0)==-1)
{
printf("send() failed with error code : %d" , -1);
exit(EXIT_FAILURE);
}
return 0;
}
I've tried adding this code:
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0) {
perror("fcntl(F_GETFL)");
exit(0);
}
opts = (opts & (~O_NONBLOCK));
if (fcntl(sock,F_SETFL,opts) < 0) {
perror("fcntl(F_SETFL)");
exit(0);
}
but it still doesn't work, and if I call the recv(), the application doesn't block (and recv() always returns 0). Here is the function where I call the recv():
void TCPreceiver::receive(char* text, int& dim)
{
int ret;
ret = recv(sock, text, dim, 0);
dim=ret;
if(ret == -1){
printf("recv() failed with error (%d)\n", ret);
//system("PAUSE");
exit(1);
}
}
Where am I wrong?
recv() returning zero indicates either (1) you passed a zero length, which is just a programming error which I won't discuss further here, or (2) end of stream. The peer has close the connection. This isn't a non-blocking situation, this is the end of the connection. You must close the socket and stop using it. It will never return anything. It zero ever again.
See the man pages.