I have been reading tutorials and example that I have found online, but not getting to far. I know I'm not that great at c++ coding, but I have fun with what I can do. I want to make a simple two player game. All I really want the networking code to do it send the x and y cordinates to the other player. I have tried to compile some examples before, but I don't know exactly what I need to make it happen. I am using windows xp with Bloodshed Dev-c++. Do I have to download anything or am I just so newbish that I can even compile right?
philkr
July 31st, 2005, 01:08 PM
Can you explain a little more how far you are learning c++, windows api and networking (winsock). Do you know how to program a Win32 program with windows proc message handling and so on?
Incarnation
July 31st, 2005, 01:20 PM
Well.... I would like to say I understood what you just said, but I did. I just don't know how to code it... I know I am jumping in over my head because I know just about nothing in the ways of networking, but I want to see how far over my head I am. I am trying to atleast understand how it all works before I go to college on the 18th and actually be to busy to try and learn anything. I am bored of really cool games that are not multiplayer and multiplayer games that really suck. So I wanted to take a crack at it. It you think this is going to be way to hard for me to even think about doing, then maybe there is some tool that will help do the networking for me, or a game maker of some kind. I would really like to avoid the maker though.
philkr
August 1st, 2005, 02:13 AM
OK, the good news: networking is rather easy imho. The bad news: A whole game is not so easy to make. Usually a beginner should start with a simple one like tetris to learn the basics and then move on and become more complex step by step.
As with the networking: Google for tutorials about WinSock and read them. If you already did and didn't get it, maybe you read a more advanced tutorial. To send only x and y coordinates you really just need the basics. If you have a specific question come back and ask.
Incarnation
August 1st, 2005, 12:36 PM
I would be happy with a multiplayer pong game, that way I would only need to send a y variable for the players and an x and y variable for the ball. So the player that starts the game would be the server that controls all the variable s for player 1, the ball, and the score. I shall try and google it, but I know I am going to mess something up.
philkr
August 2nd, 2005, 02:43 AM
Networking working with WinSock has many parallels with a telephone talk, so I thought it would be a good idea (although it is not mine) to follow this parallel in this tutorial.
1) Get your telephone contract with a provider
At the beginning of every WinSock program you have to include the winsock header file and add the library to the linker:
Now we only have to call the startup function. This function must be called before any other WinSock calls can be made. It requires the version number, usually 1.1 or 2.0 (in this case 2.0) and a WSADATA structure which is not important in this basic tutorial.
WSADATA dWinSock;
int nError = WSAStartup(MAKEWORD(2, 0), &dWinSock);
if(nError)
{
// There was an error
// Do something
}
Now we can use our telephone lines. We have about 65000 of them. Each line has it's own telephone number consisting of your ip address and the so called port number. The ip address consists of four bytes separated by dots, while the port number is a two-byte integer.
In our first example we will call somebody. This means we are the client, the other side is the server. Note that this means we will be using the TCP protocol which is connection-oriented (like a telephone). There is also the UDP protocol, which is connectionless, the parallel in everyday life would probably be sending any kind of messages.
2) The next step to do is to get a telephone and set it up. In WinSock we have to create a socket. SOCK_STREAM means we are using the TCP protocol:
SOCKET telephone;
telephone = socket(AF_INET, SOCK_STREAM, 0);
if(telephone == INVALID_SOCKET)
{
// There was an error
// Do something
}
3) Now we have to select a number which we are going to dial. This information goes into the sock_addr_in data structure. As said before we need an ip address and a port number. The inet_addr() function converts our ip address string into the numerical format unsigned long. We also need to convert the portnumber with htons(), because WinSock internally uses another format for numbers. In this example the server ip is 192.168.0.1 and the port number will be 80. If you don't know the ip, take a look at step 3 of the server example.
4) We are now going to dial the number we set up. In WinSock this means calling connect(). The parameters are of course our socket (telephone) and the sock_addr_in structure (phone number) and its size.
nError = connect(telephone, (struct sockaddr *)&server, sizeof(server));
if(nError)
{
// There was an error (maybe nobody is at home, or we forgot to hang up after the last call?)
// Do something
}
5) If there was no error we are now connected with the other side and can send and receive. During a telephone talk you normally don't go away from the phone to do other things. In a computer program other things also have to be done. So you have to set someone up which is going to notify you, when the other side is sending again. In WinSock you do this mostly by using WSAAsyncSelect(). We give it a number of events we want to be notified of and a window handle and it will send a window message, which we can process in the window or dialog procedure.
6) Now if we receive data from the other side (the other person is speaking) we can receive it in the window procedure (hear what it says). But we also wanted to be notified if the other side closes the connection (hangs up). To know what of both happened we use the WSAGETSELECTEVENT macro on lParam. BTW: wParam identifies the socket, if we have multiple connections.
// In WindowProc() or DialogProc()
switch(uMsg)
{
case: WM_WINSOCK:
if(WSAGETSELECTEVENT(lParam) == FD_READ)
{
// the other side sent information, receive it with recv()
}
else if(WSAGETSELECTEVENT(lParam) == FD_CLOSE)
{
// the other side hung up, let's do so too
}
break;
//...
}
7) Now it's time for the most important thing: Receiving and sending. Fortunately it is one of the easiest parts. To receive use recv() with socket, a buffer and buffer length as parameter. To send use send() with exactly the same parameters. In the example we simply echo back what the other side said.
8) Finally at some time you want to stop the telephone talk. You could do this by simply hanging up ( call closesocket() ). But this won't be very nice for the other person, would it? I will show you how to perform a so called graceful shutdown. I commented the steps in the example source code.
char strBuffer[512];
shutdown(telephone, SD_SEND); // You tell the other side you have nothing more to say and want to interrupt
// If the other side is the one who wants to interrupt, this is of course unneccessary but does not do any harm
while(recv(telephone, strBuffer, sizeof(strBuffer), 0) > 0); // You let the other person finish talking
// If you are interrupted yourself, you would instead send any 'remaining response data'
closesocket(telephone); // You hang up the phone
9) If you are finished with all your connections and don't want to use WinSock again during this program run, you can cancel your telephone contract (or do you still want to pay money/memory for it?):
WSACleanup();
Being the server
There is no big difference in the code for setup, sending, receiving and closing, when you are the server. But, of course, the connection process is now to be done from the opposite side. You don't call somebody, but you plugin a telephone in one of your lines, give your number to a friend and sometime your telephone starts ringing.
As I already mentioned setup does not change. So we can jump directly to step 3:
3) At the time we were calling somebody it was the same wether we plugged our phone in line 1400 or 23045. (If you are now wondering where we plugged the phone in the first example: The connect() function automatically used a port which was free) Now you have to decide for one, because the other person has to know which number to call. In WinSock you have to use the bind function to plug your phone in. You should use port numbers greater than 1024, because the lesser are reserved (we will use 21234 in this example). But first you need to find your ip address. Therefore you first use gethostname() to get your hostname and then gethostbyname() to get the ip address.
char* strHost[32];
sock_addr_in server;
gethostname(strHost, sizeof(strHost));
HOSTENT* host = gethostbyname(strHost);
server.sin_family = AF_INET;
server.sin_port = htons(21234); // Setup the port number
CopyMemory(&(server.sin_addr.s_addr), host->h_addr, host->h_length); // Copy our ip to the struct
nError = bind(telephone, (struct sockaddr *)&server, sizeof(server));
if(nError)
{
// There was an error
// Do something
}
4) So what do we have to do, if we want to hear the telephone ring? Right, we have to listen(). And because we don't want to sit in front of the telephone the whole time, we again setup our little helper which notifies us.
#define WM_CONNECTION WM_USER+1
listen(telephone, SOMAXCONN); // SOMAXCONN is default value for maximum pending connections
WSAAsyncSelect(telephone, hWnd, WM_CONNECTION, FD_CONNECT);
5) If somebody wants to connect to our server, we will now receive a WM_CONNECTION message in our window procedure. Now comes a big difference to a telephone talk. If we receive a new connection the talk is redirected to another telephone, so that our first telephone line stays free and more incoming connections can be accepted. This may sound complicated, but it's just one function: accept(). As an option one can use a sock_addr_in structure to get information about who connected. You will have to specifiy a new socket variable for every new connection, so you will have to make an array and a client counter.
// In the window procedure
switch(uMsg)
{
case WM_CONNECTION:
if(nClientCount >= 32) break; // Too many connections for socket array
clientSockets[nClientCount] = accept(telephone, (struct sock_addr*)&dClientInfo, sizeof(dClientInfo));
WSAAsyncSelect(clientSockets[nClientCount], hWnd, WM_WINSOCK, FD_READ | FD_CLOSE);
++nClientCount;
break;
//....
}
The rest is exactly like with the client. You only have to note that you must check the wParam of the WM_WINSOCK message if you are notified, because it tells you for which client the notification is. To start the simple you may want to start with only one client who connects first. You can restrict connections with the third paramter of the listen() functions. Or simply don't call accept() on unwanted connection attempts.
That's it for this tutorial. I hope it was easy to understand. If there are questions, feel free to ask.
Shumakriss
April 19th, 2007, 10:26 AM
Thank you Philkr! Your post is a little more straightforward than beej's site. After going over your post sockets made more sense. The only conflict I see is that the structure sock_addr_in is actually sockaddr_in when I use it in VC++. I'm not sure if that's a technicality, version issue, or something else I'm not taking into consideration. I've also found that having prior knowledge of networking makes this MUCH simpler. Thanks again philkr!
MikeAThon
April 19th, 2007, 08:29 PM
In all fairness to Beej, his tutorial is directed to blocking sockets in Linux, whereas Philkr's post is directed to non-blocking asynchronous sockets under Windows.
They are very different beasts.
Beej's tutorial os good for what it is. It obviously does not cover the same ground as Philkr's post, which also is very good.
I'm not sure if Philkr is still active here. This post is nearly two years old, and it looks like last activity from him was in mid-2006.
Mike
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.