c++ threads winsock2

Hello Guys,

Anyone has a simple example of a Multithreaded Winsock2 server? not the one with select(); and FD_SET.
I mean normal Multithreading?! where for each client a new thread is created?

I quiet dont understand how I can come back to a specific thread for a specific client? so i can send or recv/send messages between server and that specific client.

can u share some Links, or examples maybe? im familar with basic socket one or multiple clients but not async, or multithreaded.

MY connection closes after first client connects and it seems that the recv()
gets nothing?! i get the Error:

Error recv() from Thread:1 Code: 10038

but still the client recived the message "Welcome to Server"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Networking.h"
#include <ws2tcpip.h>
#include <thread>
#include <mutex>
#pragma comment(lib, "Ws2_32.lib")


#define MAXCLIENTS 10
#define PORT 27015
#define BUFFERSIZE 512
using namespace std;

void cl_new_socket(SOCKET  cl_socket, int threadID);



SOCKET				s_socket,cl_socket;
sockaddr_in				srvInfo, clInfo;
char buffer[BUFFERSIZE];
int active_Clients = 0;
int clInfoSize = sizeof(clInfo);
int errResult = 0;
int clientID = 0;
int thread_id = 0;
int cl_conn_err = 0;

int main() {
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 0), &wsa) != 0)
	{
		std::cout << "Error initialising wsa..";
		WSACleanup();
		exit(1);
	};

    SOCKET				        



	s_socket = socket(AF_INET, SOCK_STREAM, 0);
	
	
	if (s_socket == INVALID_SOCKET)
	{
		cout << "Error Creating socket.." << endl;
		return 0;
	}
	
	//populate srv ip/service info
	srvInfo.sin_family = AF_INET;
	srvInfo.sin_port = htons(PORT);
	srvInfo.sin_addr.S_un.S_addr = INADDR_ANY;

	errResult = bind(s_socket, (sockaddr*)&srvInfo, sizeof(srvInfo));
	if (errResult != 0)
	{
		cout << "Error Binding socket.." << endl;
		return 0;
	}

	listen(s_socket, MAXCLIENTS);

	thread socket_threads[MAXCLIENTS+1];
	int i = 0;

	while (i < MAXCLIENTS)
	{
		clInfoSize = sizeof(clInfo);
		cl_socket = accept(s_socket, (sockaddr*)&clInfo, &clInfoSize);

		i++;

			socket_threads[i] = thread(cl_new_socket,cl_socket,i);
			socket_threads[i].join();
			cout << "While looop i" << i << endl;
		
		
	};


	

	closesocket(s_socket);
	WSACleanup();
	return 0;
}


void cl_new_socket(SOCKET  cl_socket, int threadID) {


	
		mutex mtx;
		mtx.lock();
		ZeroMemory(buffer, BUFFERSIZE);
		int rez = recv(s_socket, buffer, BUFFERSIZE, 0);

		mtx.unlock();
		if (rez == 0)
		{
			cout << "Client-[" << threadID << "] disconnected" << endl;

		}
		else if (rez > 0)
		{
			cout << buffer << endl;

		}
		else {

			cout << "Error recv() from Thread:" << threadID << " Code: " << WSAGetLastError() << endl;
		}
		ZeroMemory(buffer, BUFFERSIZE);


	
		send(cl_socket, "welcome to the Server",strlen("welcome to the Server"), 0);





};
Last edited on
I'm going to be vague because I can never remember the socket function names, and it's not terribly important for your question, which is mainly about how to design the server code.

You normally do something like this on the server side:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Server::server(){
    while (true){
        auto socket = listen();
        //socket is now connected to a client.
        std::thread t([this, socket = std::move(socket)](){
            this->handle_client_requests(socket);
        });
        t.detach();
        //No join or other synchronization. The listening thread is independent
        //from the request handlers.
    }
}

void Server::handle_client_requests(Socket &socket){
    while (true){
        auto request = socket.receive();
        if (request.is_exit())
            break;
        auto response = this->handle(request);
        socket.send(response);
    }
}
Using WIN32 API, something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while (listen(ListenSocket, SOMAXCONN) != SOCKET_ERROR) {
    // Accept a client socket
    sockaddr inaddr {};
    int socklen {sizeof(inaddr)};

    if (const SOCKET sock {accept(ListenSocket, &inaddr, &socklen)}; sock == INVALID_SOCKET)
        printf("Connection acceptance failed with error: %d\n", WSAGetLastError());
    else {
        auto thrd {new Thrd};		// Called thread to delete when connection fails

        // struct Thrd as needed. values set as needed. One thrd per connection/thread
        // netTerm is the thread function to use for each connection
        CloseHandle((HANDLE)_beginthreadex(NULL, 0, &netTerm, (void*)thrd, 0, NULL));
    }
}

puts("Net server stopping...");

thank u guys, but to be honest I didnt rly understand the whole thing :S

i could need a simple multithreaded server example, and than turn it to OOP step by step.
anyway thank u for trying :D
simple multithreaded server example, and than turn it to OOP step by step.


That's what my example above is. It's not OOP and is a simple example. For each client connection, a new pointer to a Thrd structure is created, a new thread is begun (netTerm) and a pointer to thrd for that connection is passed as a parameter.

You then have a separate thread for each client connection with it's own data. thrd could, of course, be allocated in the thread rather than here - but in the code I stripped this example from we need to set some thrd variables before the thread is created (such as inaddr etc).

We also have the whole of the above also as it's own thread - as it's a blocking loop. accept() only returns when a connection is attempted.

Then recv() is used to get input and its return value will indicate when the connection has been terminated.

Im not familiar with win32 api 🤦‍♂️

And when broadcating data, to all cloents via threads, should i use Global variables? Or is there another way, for example when clients share there position? How does the update with new data occur? Im sorry im just new to sockets and threads, a basic beginner.

Thank you
And when broadcating data, to all cloents via threads, should i use Global variables? Or is there another way, for example when clients share there position?
Maybe it would help if you mentioned what sort of application you're trying to make.
to be honest i tried to make a simple console gameserver, and to shar x,y position between players.

I saw some tutorials on YT using SELECT() and FD_SET to handle multiple clients, but i heard it can accept a specific amount of clients , i think 60-70 .

I'm trying to accept at least 1000 clients, i dont know if multithreading is the answare!

I found so many tutorials but most of them are for linux, and they use pthread header file,
but i want mine for windows. a simple server wich recives and sends data to clients.

btw how do big companies accept millions of clients?! i know they use servers, but as far as i know every machine uses maximum 65k ports and Facebook,YT how do they accept all those connections do they have 100-400 servers?!

thx and sorry for asking 10 questions in 1 replay.

thank u guys.
to be honest i tried to make a simple console gameserver
I'm trying to accept at least 1000 clients
These statements are in direct contradiction.

do they have 100-400 servers
Yes. It's also worth noting that HTTP connections are usually shorter-lived than game connections.
sorry my friend, with simple, i ment not sending tons of data and complicated handling.

just a player class
int x,y;
to update their positions, i know c++ might not be the easiest language, but i damn love it.

and i would love if u guys could help me out with any kind of info's regarding winsock2 server/clients
> I'm trying to accept at least 1000 clients, i dont know if multithreading is the answare!
It isn't.

A single threaded program using select() will be a lot (no seriously, a LOT) easier for you to get working.

I mean you've already thrown in a mutex into your code without understanding why (or even if) it was necessary. Unnecessary mutexes will just eat into the performance. Necessary ones you miss will result in https://en.wikipedia.org/wiki/Heisenbug (or more formally https://en.wikipedia.org/wiki/Race_condition ) problems that will screw with your mind.

No real machine you're likely to have will have anywhere near 1000 real cores, so 1000 threads are going to spend most of their time in expensive context switches.


> I saw some tutorials on YT using SELECT() and FD_SET to handle multiple clients,
> but i heard it can accept a specific amount of clients , i think 60-70 .
Then read the actual manual, not some YT wibbler.
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select

Four macros are defined in the header file Winsock2.h for manipulating and checking the descriptor sets. The variable FD_SETSIZE determines the maximum number of descriptors in a set. (The default value of FD_SETSIZE is 64, which can be modified by defining FD_SETSIZE to another value before including Winsock2.h.) Internally, socket handles in an fd_set structure are not represented as bit flags as in Berkeley Unix. Their data representation is opaque. Use of these macros will maintain software portability between different socket environments. The macros to manipulate and check fd_set contents are:

For info, Linux has a FD_SETSIZE of 1024, so this seems a good place to start.


Oh, and set your IDE to use 4 spaces for indentation, not hard tabs.
It might look reasonable on your screen, but sooner or later, something somewhere will make a dogs dinner out of your code by not interpreting tabs the same way your IDE does.


> listen(s_socket, MAXCLIENTS);
The backlog is not the number of clients.
It's the number of clients you're expecting to join in some small moment of time before you get around to doing an accept().
wow thx man, why ppl make it so complicated on YT videos, and also no clear information.
I used listen(socket,maxclients) for nearly 6 months now, and i learnd this exacly today from u.
Thank you.

so basicly i can handle 1000 clients with select() too?

Oh, and set your IDE to use 4 spaces for indentation, not hard tabs.
It might look reasonable on your screen, but sooner or later, something somewhere will make a dogs dinner out of your code by not interpreting tabs the same way your IDE does.

Where can i find those settings? i found indentation but not anywhere i can increase to 4, or input the amount of space.

And sorry for my english, it sucks.

:D

PS thank you sooo much
Last edited on
YT might be OK for an intro taster to see what it's all about.

But watching someone type badly for an hour on a fuzzy screen and make a bad job of explaining things is a poor use of both time and bandwidth. Spending an hour watching 1GB of data vs spending 10 minutes of reading 1KB of data.

Not to mention that a lot of YT videos do not give you the machine readable source code at the end for you to try (something far more likely with written text).

> Where can i find those settings?
Depends on your IDE
But in visual studio, tools->options... is a good place to start.
Just type 'tab' into the search box, and it lists all the options that mention tab.

> so basicly i can handle 1000 clients with select() too?
Yes.

You just need to do something like this for a single source file
#define FD_SETSIZE 1024
#include <winsock2.h>


Or -DFD_SETSIZE=1024 on the command line, so every file is compiled with the same constant.
Again, this would be under project->properties->compiler->preprocessor (for VS).
Other IDEs have the same idea.

Last edited on
Topic archived. No new replies allowed.