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:
server side:
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.