Tutorial 1 - Winsock
Since DirectPlay is now deprecated(It's got old), I've decided Winsock(Windows Sockets) is a good alternative
to creating multiplayer games in C++. So this tutorial is about setting up a peer-to-peer
client and server using Winsock.
There is a difference between blocking network communication and
asynchronous network communication. Blocking is where a function
hangs or waits for a response before it returns, which could prevent
a game from running smoothly having to wait. Asynchronous or event-driven architectures are
the solution to that. Instead of polling and blocking until a network
response is received, instead you can do something when an event occurs. This
article focuses on asynchronous networking.
I will explain how to use Winsock with a simple windowed app but leave it to you to
incorporate it into an actual game.
You more-or-less need a main WndProc that handles all network events.
Ok, the Winsock app for this tutorial is a Win32 windowed app. It connects
two computers together using TCP/IP and then the two computers can send data to each other.
The data is received on one end through a windows message e.g. WM_SOCKET. We define the
window message ourselves, WM_SOCKET, and that way we can identify
a network event by this message in the application's main WndProc.
To request network messages, use the winsock function WSAAsyncSelect(). However
you must create a Server/Client socket before calling WSAAsyncSelect();
A socket is basically something that let's you talk to a computer
over a network. More on sockets later.
The application for this tutorial can act as a server or a client. There is no need to create a
separate executable, one for client and one for server.
127.0.0.1 is a special ip address called the loop back address. It can be used to test
networking on one machine. You can find the actual ip address of your computer by
running ipconfig.exe from the command line(See Figure 2).
Once you have chosen to host a game(See Figure 1), you can then join that game by
typing the ip address of the host and press Join.
The window uses GDI+, a graphics API, to render white text; You will need to type gdiplus.lib in
Linker -> Input -> Additional Dependencies to use GDI+. You may also have
to download the Windows Platform SDK, if it is not already on your system.
You need to create a socket on the server side and a socket on the client side.
The server then listens for an incoming connection request from a client
on the server socket and on some port, e.g. 5678. If a client tries to connect then the server
accepts and gets a socket that represents a socket on the client side. The server
can then communicate with the client using that socket.
So that covers the theory.
The User Interface
I will give you the code that creates a UI like in Figure 1, and you
can use this as a starting point if you like for Winsock.
main.cpp is intended for an empty Win32 project.
If you start a new project you will have to add gdiplus.lib and ws2_32.lib to
Additional Dependencies to be able to use GDI+ and Winsock.
Once you have a window set up and a WndProc, you can start adding Winsock code.
As far as GDI+ goes void OnPaint(HDC hdc) is probably the only method worth
looking at if it's just Winsock you are interested in. using namespace Gdiplus;
is necessary if you want to use GDI+ objects without Gdiplus::
The winsock header must come before <Windows.h>:
I have created the following methods to handle client-Server
void ProcessReceivedMessage(string msg);
//Client side methods.
bool RequestNetworkEventsClient(HWND proc_window);
bool ConnectToServer(string ip_address);
//Server side methods.
bool RequestNetworkEventsServer(HWND proc_window);
InitializeWinsock() prepares Winsock for use. I recommend calling it
only when you need to use winsock rather than when the program is started. It calls
To connect a Client to a Server you need to create a Socket on
the client side and a Socket on the Server side. A SOCKET is a
pointer variable that is part of Winsock. I have made
the functions CreateSocketClient() and CreateSocketServer() to
If you look at CreateSocketClient() you can see a socket is created with the
Where "Socket" is a global variable: SOCKET Socket;
AF_INET means we want to use Internet Protocol v4 addresses (See Figure 2).
CreateSocketServer() is a bit different from CreateSocketClient().
htonl(INADDR_ANY) is to do with byte-order, which I won't go into. bind() associates the
SockAddr object and Port number with the socket. The Port
is a number used to identify an incoming connection request from the client. For example
a client may try to connect to a socket on port 5678.
#define WM_SOCKET WM_USER + 1
By calling WSAAsyncSelect(...) with a valid socket as a parameter requests network
events in the form of a windows message e.g. WM_SOCKET. When WM_SOCKET is received
we can use WSAGETSELECTEVENT(lParam) to get the associated event FD_READ, FD_CLOSE or
FD_ACCEPT for example. Take a look at the code for these events to see
how they are handled by the WndProc or see The Window Procedure below.
Host or Client?
You can examine the code to see what happens when the Host button is pressed or
if Join is pressed. Just look at void HandleButtonPressed(HWND hWnd, int notification_code).
When Host is pressed the program tries to inizialize winsock. If successful, it
tries to create a socket, a socket on the server side because Host is chosen. If successful
a request for Network windows messages is made with the line: RequestNetworkEventsServer(main_window).
(See code below)
if(hWnd == hWnd_BtnHost)
msg_status = L"Hosting Game, Waiting for client to join. ";
The RequestNetworkEventsServer(main_window) function simply calls WSAAsyncSelect() to request the WM messages(Notice WM_SOCKET is passed as a parameter).
If the socket was created and request for WM_SOCKET successfull then the host listens on the socket
with the following code:
//Now the socket is set up,
//Listen for incoming connections on the socket:
A similar thing is done for the client if Join is pressed:
msg_status = L"Joining Game";
string ip = GetIPFromTextBox();
ConnectToServer(ip) calls the Winsock connect() method to try to connect to the host computer.
StopConnectionActivity() can then be used to cleanup winsock or stop any network
activity. You might for example want to cancel joining or hosting a game and end any network activity such
as listening on a socket. After freeing Winsock you can reinitialize it when needed with InitializeWinsock().
bool ConnectionEstablished() depends on the variables bConnectedToClient or bConnectedToServer. These variables
need to be managed to reflect the state of the application. A possible improvement might be
to create some class to manage a network session.
The Window Procedure
If all is good then network events will be sent to the WndProc. Here is how you
might handle them:
//Handle Winsock messages.
L"Connection to server failed",
msg_status = L"Connection to Host failed.";
//FD_READ on Client side and Server side.
//read incoming data
string received_msg = szIncoming;
//FD_CLOSE on Client side and Server side.
//Server has disconnected from client or
//client has disconnected from server.
msg_status = L"Connection Lost.";
//FD_ACCEPT on Server side only.
//Decide whether to allow client to connect:
//Get client sock addr:
int nret = WSAGetLastError();
bConnectedToClient = true;
msg_status = L"Client Connected!";
I have made it so the server sends the message "connection_response"
to the client to let the client know a connection was established.
ProcessReceivedMessage() updates the status on receipt to let the
user know too.
Lastly, if the user presses the send button, a text message is
sent to the other computer. ProcessReceivedMessage() will set the
msg_received string to this text message.
The global variables I have used for the example application are defined as:
int Port = 5678;
bool bWinsockInitialized = false;
bool bConnectedToServer = false;
bool bConnectedToClient = false;
//Is this computer a host or a client?
bool bClient = false;
bool bHost = false;
Download Tut Files