0

Server stops working when clients tries to connect (reconnect) to server after disconnect.

I am making a TCP/IP server-client app. I want to have multiple clients that connect to one server by IP. Than server can print a list of all connected clients. Server can send messages(commands) to each client one at a time by it's socket number.

I have two console apps written in C++ in visual studio:

1. The server. It accepts all incoming connections and creates a new thread for each connection. It also stores information about all connections in a std::vector of structure Client (two fields: SOCKET Connection and std::string IP) (see Clients class). Server prints a message each time a new client connects. It also can print a list of all connections and send messages to each client by it socket number (not sure if this is a right term)

2. The client. It connects to the sever by IP and prints all incoming messages and handles commands from the server (also sends results of executing commands back to the server).

The problem The problem appeared when I tried to add handling for client disconnection from the server. Server checks the result of recv() in the same thread it send messages to a certain client. If recv() <= 0, it prints that this client has disconnected and then closesocket() socket of this client.

The problem is when I close the client window and then launch a new client from the same PC, client connects, but then server stops working and connection breaks.

Here's the code for both server and client: server.cpp

#include <iostream>
#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>

#pragma warning(disable: 4996)
#include <string>
#include <vector>

struct Client {
    SOCKET Connect;
    std::string IP;
};

class Clients {
private:
    std::vector<Client> v_Clients;
public:
    void addClient(Client client) {
        v_Clients.push_back(client);
    }
    void removeClient(int i) {
        v_Clients.erase(v_Clients.begin() + i);
    }
    Client getClientByIndex(int i) {
        return v_Clients[i];
    }
    Client getClientByIp(std::string IP) {
        for (int i = 0; i < v_Clients.size(); i++) {
            if (v_Clients[i].IP == IP) {
                return v_Clients[i];
            }
        }
    }
    Client getClientByConnect(int Connect) {
        for (int i = 0; i < v_Clients.size(); i++) {
            if (int(v_Clients[i].Connect) == Connect) {
                return v_Clients[i];
            }
        }
    }

   
};

Clients CLIENTS;
SOCKET Connections[100];
int connCounter = 0;

void Recieve(int i) {
    while (1) {
        char msg[1000];
        if (recv(CLIENTS.getClientByIndex(i).Connect, msg, sizeof(msg), NULL) > 0) {
            std::cout << msg << "\n>";
        }
        else {
            std::cout << "Client "<< CLIENTS.getClientByIndex(i).Connect <<"\t"<< CLIENTS.getClientByIndex(i).IP <<" disconnected"<<"\n>";
            shutdown(CLIENTS.getClientByIndex(i).Connect,SD_SEND);
            closesocket(CLIENTS.getClientByIndex(i).Connect);
            //CLIENTS.getClientByIndex(i).Connect = INVALID_SOCKET;
            CLIENTS.removeClient(i);
            connCounter--;
            break;
        }
    }
}

void commandHandler() {
    while (1) {
        char message[50];
        std::cout << ">";
        std::cin.getline(message, sizeof(message));
        std::string str = message;
        str.push_back('\n');
        std::string command;
        std::string argument;
        int command_iter = 0;
        int i = 0;
        while (str[i] != '\n') {
            if (str[i] != ' ') {
                command.push_back(str[i]);

            }
            else {
                i++;
                argument = str.erase(0, i);
                argument.pop_back();
                break;
            }
            i++;
        }
        //std::cout << command << std::endl;
        //std::cout << argument << std::endl;

        if (command == "clients") {
            std::cout << "#:\tSocket:\tIP Address:" << std::endl;
            for (int i = 0; i < connCounter; i++) {
                std::cout << i << "\t" << CLIENTS.getClientByIndex(i).Connect << "\t" << CLIENTS.getClientByIndex(i).IP << std::endl;
            }

        }
        else {
            //std::cout << "another command" << std::endl;
            if (argument != "") {
                //std::cout << "argument = " << argument << std::endl;
                for (int i = 0; i < connCounter; i++) {

                    //std::cout << Clients[i].IP << "\t" << argument << std::endl;

                    if (std::to_string(CLIENTS.getClientByIndex(i).Connect) == argument) {
                        //std::cout << Clients[i].Connect << "\t" << Clients[i].IP << std::endl;
                        char msg[50];
                        strcpy(msg, command.c_str());
                        send(CLIENTS.getClientByIndex(i).Connect, msg, sizeof(command), NULL);
                    }
                }
            }
        }
    }
}

//creating socket for incoming connections as a global variable
SOCKET newConnection;

int main(int argc, char* argv[])
{
    //WinSock 2.1
    WSAData wsaData;
    WORD DLLVersion = MAKEWORD(2, 1);
    //checking if WSA did initialise 
    if (WSAStartup(DLLVersion, &wsaData) != 0) {
        std::cout << "Error loading WSA lib" << std::endl;
        exit(1);
    }

    //configuring server socket
    SOCKADDR_IN addr;
    int sizeofaddr = sizeof(addr);
    addr.sin_addr.s_addr = inet_addr("0");
    addr.sin_port = htons(1111);
    addr.sin_family = AF_INET;

    //creating server socket
    SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL);
    //binding the socket
    bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
    //start to listen for incoming connections
    listen(sListen, SOMAXCONN);

    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)commandHandler, NULL, NULL, NULL);
    for (int i = 0; i < 100; i++) {
        newConnection = accept(sListen, (SOCKADDR*)&addr, &sizeofaddr);

        if (newConnection == 0) {
            std::cout << "Error: client didn't connect" << std::endl;
        }
        else {
            std::string clientIP = inet_ntoa(addr.sin_addr);
            std::cout << "Client connected. IP = " << clientIP << "\n>";
            //отправляем сообщение на клиент
            char msg[256] = "Welcome to the server!\n";
            send(newConnection, msg, sizeof(msg), NULL);
            Client newClient{newConnection, clientIP };
            CLIENTS.addClient(newClient);
            Connections[i] = newConnection;
            connCounter++;
            CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)Recieve, LPVOID(i), NULL, NULL);

        }
    }

    system("pause");

    return 0;
}

client.cpp

#include <iostream>
#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>
#include <filesystem>
#pragma warning(disable: 4996)

namespace fs = std::filesystem;

SOCKET ClientSocket;

void receiveMsg() {
    char msg[256];
    while (true) {
        int recvStatus = recv(ClientSocket, msg, sizeof(msg), NULL) > 0;
        if(recvStatus > 0) {
            std::cout << msg << std::endl;
            std::string message = msg;
            
            if (message == "pwd") {
                fs::path currentPath = fs::current_path();
                /*std::cout << "Current path is " << currentPath << '\n';*/

                std::string currentPathString = currentPath.string();
                currentPathString.push_back('\0');

                char tmp[256];
                for (int j = 0; j < currentPathString.length(); j++)
                {
                    tmp[j] = currentPathString[j];
                }

                send(ClientSocket, tmp, sizeof(tmp), NULL);
            }
        }
        else {
            std::cout << "server connection lost" << std::endl;
            closesocket(ClientSocket);
            break;
        }
    }
}

int main(int argc, char* argv[])
{
    //WinSock 2.1
    WSAData wsaData;
    WORD DLLVersion = MAKEWORD(2, 1);
    //checking if WSA did initialise 
    if (WSAStartup(DLLVersion, &wsaData) != 0) {
        std::cout << "Error loading WSA lib" << std::endl;
        exit(1);
    }

    //configuring client socket
    SOCKADDR_IN addr;
    int sizeofaddr = sizeof(addr);
    char serverIP[15];
    std::cout << "Insert server's IP to connect: ";
    std::cin >> serverIP;
    addr.sin_addr.s_addr = inet_addr(serverIP);
    addr.sin_port = htons(1111);
    addr.sin_family = AF_INET;


    //creating client socket
    ClientSocket = socket(AF_INET, SOCK_STREAM, NULL);
    if (connect(ClientSocket, (SOCKADDR*)&addr, sizeof(addr)) != 0) {
        std::cout << "Error: client cannot connect to the server" << std::endl;
        return(1);
    }
    else {
        std::cout << "Connected to the server!" << std::endl;

        receiveMsg();
    }

    system("pause");

    return 0;
}

Here's how it works step by step(there was a screenshot for each step:

  • I launch the server

  • I launch the client and enter IP of a server to connect to it:

  • When I hit Enter, client connects to the server and receives a welcoming message from the server.

  • At the same time, server prints that a new client has connected

  • Then I can print a list of all clients connected to the server and send a command to the client using its socket number(I've implemented pwd so far).

  • The client sends back it's working directory, as expected

  • Then I close the client console window by pressing the x button in the upper left corner.

  • Server prints, that client has disconnected.

  • If I run clients command again, it will show that this client disappeared from the list.

But then, when I try to start a client app again, entering IP and try to connect to the server, it connects, but instantly the server stops working and connection breaks. Here's screenshots of a second try:

Starting the client:

client.cpp

server side:

server.cpp

I tried googling this problem for two days but didn't find any solution. I don't know if it's sockets problem, threads problem or some sort of pointers/links/variables problem. It's my first time working with sockets and threads. Please help.

user16217248
  • 3,119
  • 19
  • 19
  • 37
ZEN
  • 1
  • 1
  • Your compiler should be complaining, because you have a few functions that can end without returning anything, which leads to *undefined behavior*. Enable more warnings if you don't get any at the moment. The servers `getCLientBy...` functions should not return by value by the way, as that will return copies. – Some programmer dude Jun 03 '23 at 12:53
  • Also, you have multiple threads which access shared data without protection, leading to data-races. Which also leads to undefined behavior. – Some programmer dude Jun 03 '23 at 12:56
  • And the problem you have is a *crash*. You should use a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to catch the crash as and when it happens, and locate where in your code it happens. Examine the values of all variables at the point of the crash to see if they give any hints. – Some programmer dude Jun 03 '23 at 12:58
  • Ok, I'll try all of this and come back later with new questions – ZEN Jun 03 '23 at 13:04
  • 1
    Once you do `CLIENTS.removeClient(i)` on one thread, the client index on all other threads suddenly points to a wrong client. Say you have three threads running, that called `Receive(0)`, `Receive(1)` and `Receive(2)`. The first client disconnected, and the first thread called `CLIENTS.removeClient(0)`. In the `CLIENTS.v_Clients` vector, element 0 is erased; element that used to be at index 1 is now at zero, and element at 2 is now at 1. (continued) – Igor Tandetnik Jun 03 '23 at 22:56
  • 1
    Which means that a) no one serves the second client (that used to be at 1 and is now at 0) anymore; b) the thread that served the second client now switches to serving the third client mid-sentence; and c) the thread running `Receive(2)` is accessing index out of bounds in `CLIENTS.v_Clients`. – Igor Tandetnik Jun 03 '23 at 22:58
  • A network programmer discovers a problem: blocking I/O is incompatible with serving multiple clients simultaneously, because a single unresponsive client can cause the server to become unresponsive for all other clients. "I'll solve this problem by using a separate thread for each client", he thinks. Now he has two problems. – Jeremy Friesner Jun 04 '23 at 05:24

0 Answers0