Multithreaded server in C++ and Win32

Started by
9 comments, last by Antheus 16 years ago
I've been searching around for about 4 hours now about how to implement Winsock in a threaded program. I didn't find anything good at all, only a bunch of weird examples that doesn't actually work or use Winsockets. How can I structure my program so I can handle many clients at once? My code so far is good enough to handle one client. Right now it looks something like this.

	WSADATA wsaData;
	SOCKET ListenSocket;
    int iResult;

    struct addrinfo *result = NULL,
                *ptr = NULL,
                hints;

    // initiera Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
		std::cout << "WSAStartup failed: " << iResult << std::endl;
    }

    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::cerr << "getaddrinfo failed: " << iResult << std::endl;
        WSACleanup();
    }

	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
		std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl;
        freeaddrinfo(result);
        WSACleanup();
    }

    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
		std::cout << "bind failed: " << WSAGetLastError() << std::endl;
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
    }

    freeaddrinfo(result);

    if (listen(ListenSocket, SOMAXCONN ) == SOCKET_ERROR) {
		std::cout << "Error at bind(): " << WSAGetLastError();
        closesocket(ListenSocket);
        WSACleanup();
    }

	SOCKET ClientSocket = INVALID_SOCKET;

    // accepterar för tillfället bara en anslutning från klientsidan
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
    }

    // closesocket(ListenSocket);

    char recvbuf[DEFAULT_BUFLEN];
    int iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;

	// ta emot paket tills den anslutade stänger av sin anslutning
    do {
		iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
			std::cout << "Bytes received: " << iResult << std::endl;

            // Echo the buffer back to the sender
            iSendResult = send(ClientSocket, recvbuf, iResult, 0);
            if (iSendResult == SOCKET_ERROR) {
				std::cout << "send failed: " << WSAGetLastError() << std::endl;
                closesocket(ClientSocket);
                WSACleanup();
                return 0;
            }

		std::cout << "Bytes sent: " << iSendResult << std::endl;

	} else if (iResult == 0) {
		std::cout << "Connection closing.. " << std::endl;

	} else {
		std::cout << "recv failed: " << std::endl;
        closesocket(ClientSocket);
        WSACleanup();
        return 0;
    }

    } while (iResult > 0);

    // shutdown the send half of the connection since no more data will be sent
    iResult = shutdown(ClientSocket, SD_SEND);
	if (iResult == SOCKET_ERROR) {
		std::cout << "shutdown failed: " << WSAGetLastError() << std::endl;
        closesocket(ClientSocket);
        WSACleanup();
        return 0;
    }
Thankful for help. I've been trying everything I can.
Advertisement
Hi password

I recently took a close look at boost::asio. And I have to say it's really smooth. I really like it. Also there are plenty of examples that deal with severeal standard networking scenarios. Maybe it could be a usefull for you.

Best Jochen
Why did Doug Gregor create the empty directory?
Oh, and just in case you don't know boost, here is a direct link
http://boost.org/doc/libs/1_35_0/doc/html/boost_asio.html.

Best
Jochen
Why did Doug Gregor create the empty directory?
Quote:Original post by jochen
Hi password

I recently took a close look at boost::asio. And I have to say it's really smooth. I really like it. Also there are plenty of examples that deal with severeal standard networking scenarios. Maybe it could be a usefull for you.

Best Jochen


Nice one, that's even pure C++, I missed that from Win32 which was mostly coded in C. I'll take a look into that library.
Hi password,
I hope you don't mind I reply in the public section... maybe this info is interesting for some others, too.

There is a very comprehensive "Getting Started Guide" that I recommend to work through.

Well here is how I "boost":

To keep track of changes easily I use tortoise to connect to subversion and update from time to time. In case you dont know these, simply google for "tortoise or/and subversion".

You can find a detailed description on how to setup access to subversion here.

So now that you have the boost sources and bjam on your HD, you will have to build. Simply fire up a MSVC command shell and issue bjam. Since I do this quite often I created two batch files.

JamIt32.bat for 32 bit builds:
bjam --build-dir=E:\Temp\build-boost\x86 --toolset=msvc-9.0 --build-type=complete --prefix=D:\Development\3rdParty\boost --libdir=D:\Development\3rdParty\boost\Lib\x86 --without-python --without-wave install

and JamIt64.bat for 64 bit builds:
bjam address-model=64 --build-dir=E:\Temp\build-boost\x64 --toolset=msvc-9.0 --build-type=complete --prefix=D:\Development\3rdParty\boost --libdir=D:\Development\3rdParty\boost\Lib\x64 --without-python --without-wave install

Now sit back, since this will take some time. Hint: Don't forget to use a 64bit MSVC command shell to build the 64bit version and of course you will have to adjust the paths to match your system.

Now you should have a ready to use boost version on your system.

To manage include paths, I use the Property Sheets feature of Visual Studio.
These are quite usfull, if you are tired of setting up the same include paths in any new prject over and over again. MSDN has good documentation on that: Property Sheets (C++).

You don't need to explicitely link against boost libs, because most of them are header only anyways. And the non header only libs are autolinked via pragmatas.

Well finally there is one thing expecially in case you intend to use asio:
Documentation. Building boost documentation is imho a tricky thing and therefore I prefer online documentation. However in case you don't have constant internet access, simply download a release package from time to time.

Also: Version 1.35 has just been released. But I think in a few weeks you will find a 1.35 boost msi here: boost consulting

I hope this is somehow usefull for you. In case not, simply drop me a line and I will do my best to supply more/better information.

Well finally enjoy boost::asio. Which imho makes network programming really fun.

Best
Jochen



Why did Doug Gregor create the empty directory?
There was a lot of those steps I didn't understand, but i've done the following now.

1. Extracted the latest .rar of Boost into the following folder.
"C:\Program Files\boost\boost_1_35_0"

2. Downloaded bjam and opened its *build file and it did something (have no idea). Now when i'm trying to issue the commands like this one: bjam --build-dir=build-directory --toolset=toolset-name stage

It doesn't find bjam. It can only find it when I type "bjam ^".

> bjam ^> More?> More? --help> 'bjam' is not a recognized command...The same happens when I use this command.> bjam --build-dir=build-directory --toolset=toolset-name stage> 'bjam' is not a recognized command...


Sorry, but it feels like they intentionally made it as hard as it can be to install this library. I don't have a clue what to do, thanks for writing all that stuff up though.

Isn't it possible to just download all of the .lib files? Right now the only thing I miss are the lib files.
You could create a thread for each incoming connection.
Here is some pseudo code (without error detection or cleanup code)

...while(server_is_still_running){  // Wait for the next client to connect  clientSocket = accept(...);  // Spawn a thread to deal with the new client  CreateThread(..., ThreadFunc, (void*)clientSocket);}int WINAPI ThreadFunc(VOID* arg){  int* pClientSocket = (int*)arg;  // use send/recv here to communicate with this socket...}


edit:
If you don't want to go with threads you should be able to deal with multiple clients by using the select call. select can be tricky to handle right but it can be used to everything from detecting new connections to dealing with existing ones in an asynchronous manner.

[Edited by - pulpfist on April 3, 2008 5:30:25 PM]
Before you do anything, I suggest you learn about threading first. Learn about critical sections/mutexes/semaphores/etc. first and then incorporate winsock in to that. You need a good background in multithreading before you start messing with multithreaded servers as they are quite complex. MSDN is your best bet to look at. If you need help, you can pm me.
__________________________________________
Eugene Alfonso
GTP | Twitter | SFML | OS-Dev
hi password,

If u have the latest package installed extracted to C:\Program Files\boost\boost_1_35_0, do the following:

Copy bjam.exe to this directory, too.
Start a MSVC command shell.
type cd "C:\Program Files\boost\boost_1_35_0"
type (all in one line) bjam --build-dir=E:\Temp\build-boost\x86 --toolset=msvc-9.0 --build-type=complete --prefix=D:\Development\3rdParty\boost --libdir=D:\Development\3rdParty\boost\Lib\x86 --without-python --without-wave install

I hope this works for u.

Best Jochen
Why did Doug Gregor create the empty directory?
Quote:Original post by jochen
hi password,

If u have the latest package installed extracted to C:\Program Files\boost\boost_1_35_0, do the following:

Copy bjam.exe to this directory, too.
Start a MSVC command shell.
type cd "C:\Program Files\boost\boost_1_35_0"
type (all in one line) bjam --build-dir=E:\Temp\build-boost\x86 --toolset=msvc-9.0 --build-type=complete --prefix=D:\Development\3rdParty\boost --libdir=D:\Development\3rdParty\boost\Lib\x86 --without-python --without-wave install

I hope this works for u.

Best Jochen


Great, it worked now. It created a directory named boost with the following folders.

| build-boost  | x86    | boost      | bin.v2        | libs          | lots of maps here| Lib  | x86    | lots of .lib files here


Another question (hopefully my last one). The boost directory that was created, am I supposed to add it directly into the lib/ folder of the IDE so I get: MSVC/VC++/lib/boost/...?

This topic is closed to new replies.

Advertisement