<
>
Loading awesome quotes of wisdom to live by...
h o m e | m y   s o f t w a r e | a b o u t   m e | c o n t a c t   m e
  Rose,
    Unedited

A   s i m p l e   c o l l e c t i o n   o f   J o h n n i e ' s   m u s i n g s   a b o u t   h i s   l i f e
Does your website DoMarquee? Does your website DoMarquee?
Do you need to enhance your website with famous quotes and wise sayings, show banner ads to earn extra revenue from your website, or create a beautiful online photo album for your family and friends to admire? Show your visitors stylized text, images, Flash animations and any content you can find on a webpage with an unlimited number of animated slides that smoothly fade from one to another---FREE. See the demo and download, today!
last updated:
Mar
12

---
average rating:
4.9 / 5
---
comments:
32


Johnnie's Winsock Tutorial   By Johnnie Rose, Jr.
If you've purposely arrived at my Winsock tutorial, you've most likely found the idea of your own applications communicating via the Internet as fascinating a prospect as I have. Or, perhaps someone else has found the prospect equally interesting and you have been entrusted with bringing this vision into reality. In either case, the Winsock network service and this tutorial will assist you in achieving your goals of commercial enterprise, simply exploring the realm of network programming for personal use, or something in between.

Do you want to show your appreciation for my tutorial but don't want to donate money? No problem---all it takes is a click of your mouse! Help my software titles, DoMarquee and DoComments, rise through the ranks on Hotscripts.com by clicking on the banner, below. Thank you for your assistance! :-)

Here's what we'll be covering:

  • Creating a listening socket: Given a small army of networking functions, can we build a program that patiently waits for incoming connections? (Yes, we can.)
  • Making your own connections: Given a few more functions, can we create a program that successfully links with a listening server? (Yes, we can.)
  • More Tutorials and Links: What resources are there above and beyond this tutorial? I highlight 3 that should keep you busy for a while (after you've digested my tutorial, of course :-).

Though you may be eager to reach that awe-inspiring point at which your application successfully makes its first connection, be aware of the concepts behind the code. Try to avoid simply manipulating the given code to suit your immediate needs and instead identify the requirements of your application and only then implement what seems to be the best solution. That's enough of my Zen of Software Development advice for now; let's do some network programming...

Feel free to download the entire tutorial code listing. Remember that any code presented in this tutorial should be linked with the Winsock library, usually wsock32.lib or something similarly named. Also, when using code exactly as presented in the tutorial in your own IDE (Dev-C++, Microsoft VC++, C++ Builder, etc.), choose to build a Windows project with a WinMain() to avoid errors.

Creating a Listening Socket

Applications servicing outside machines are called servers. Server applications listen for clients by initializing one or more listening sockets. When a client connects to one of these listening sockets, the server receives a notification from Winsock, accepts the connection, and begins to dispatch and intercept messages to and from the new client. Perhaps the most simplistic method by which servers handle multiple clients is to spawn a new thread for each client connection. This server model most often utilizes blocking sockets, which pause temporarily to wait for incoming data, a new connection, and other network events. First, let's identify some structures we'll need to initialize a blocking socket:

  • WSADATA: This structure is used to query the operating system for the version of Winsock our code requires. An application calls WSAStartup() to initialize the correct Winsock DLL.
  • SOCKET: An object (in fact, it's defined as a u_int, unsigned integer, in winsock.h---good to know for smalltalk at parties) used by applications to store a socket handle.
  • SOCKADDR_IN: An application utilizes this structure to specify how a socket should operate. SOCKADDR_IN contains fields for an IP address and port number:

struct sockaddr_in
{
  short sin_family;         // Protocol type
  u_short sin_port;         // Port number of socket
  struct in_addr sin_addr;  // IP address
  char sin_zero[8];         // Unused
};

The first field is the protocol type, which is usually AF_INET (TCP/IP). As a listening socket isn't concerned with the network address of the machine on which it resides, Winsock automatically assigns an IP address and port number to listening sockets upon creation.

We'll build our first listening server with the above structures and a small army of network functions:

#include <windows.h>
#include <winsock.h>
#include <stdio.h>

#define NETWORK_ERROR -1
#define NETWORK_OK     0

void ReportError(int, const char *);


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow) {
	WORD sockVersion;
	WSADATA wsaData;
	int nret;

	sockVersion = MAKEWORD(1, 1);			// We'd like Winsock version 1.1


	// We begin by initializing Winsock
	WSAStartup(sockVersion, &wsaData);


	// Next, create the listening socket
	SOCKET listeningSocket;

	listeningSocket = socket(AF_INET,		// Go over TCP/IP
			         SOCK_STREAM,   	// This is a stream-oriented socket
				 IPPROTO_TCP);		// Use TCP rather than UDP

	if (listeningSocket == INVALID_SOCKET) {
		nret = WSAGetLastError();		// Get a more detailed error
		ReportError(nret, "socket()");		// Report the error with our custom function

		WSACleanup();				// Shutdown Winsock
		return NETWORK_ERROR;			// Return an error value
	}


	// Use a SOCKADDR_IN struct to fill in address information
	SOCKADDR_IN serverInfo;

	serverInfo.sin_family = AF_INET;
	serverInfo.sin_addr.s_addr = INADDR_ANY;	// Since this socket is listening for connections,
							// any local address will do
	serverInfo.sin_port = htons(8888);		// Convert integer 8888 to network-byte order
							// and insert into the port field


	// Bind the socket to our local server address
	nret = bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));

	if (nret == SOCKET_ERROR) {
		nret = WSAGetLastError();
		ReportError(nret, "bind()");

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Make the socket listen
	nret = listen(listeningSocket, 10);		// Up to 10 connections may wait at any
							// one time to be accept()'ed

	if (nret == SOCKET_ERROR) {
		nret = WSAGetLastError();
		ReportError(nret, "listen()");

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Wait for a client
	SOCKET theClient;

	theClient = accept(listeningSocket,
			   NULL,			// Optionally, address of a SOCKADDR_IN struct
			   NULL);			// Optionally, address of variable containing
							// sizeof ( struct SOCKADDR_IN )

	if (theClient == INVALID_SOCKET) {
		nret = WSAGetLastError();
		ReportError(nret, "accept()");

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Send and receive from the client, and finally,
	closesocket(theClient);
	closesocket(listeningSocket);


	// Shutdown Winsock
	WSACleanup();
	return NETWORK_OK;
}


void ReportError(int errorCode, const char *whichFunc) {
   char errorMsg[92];					// Declare a buffer to hold
							// the generated error message
   
   ZeroMemory(errorMsg, 92);				// Automatically NULL-terminate the string

   // The following line copies the phrase, whichFunc string, and integer errorCode into the buffer
   sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);

   MessageBox(NULL, errorMsg, "socketIndication", MB_OK);
}

One thing you may immediately notice about the code is the amount of effort put into error checking. Whenever an error occurs, the code obtains a specific error code with WSAGetLastError() and stores the result in nret. The error code is then sent along with a string indicating the name of the failed function to a custom function named ReportError(). There, an error message is constructed and shown to the user with a call to MessageBox(), which is part of the standard WinAPI. For example, had listen() failed with an error code of 10093 (defined as WSANOTINITIALISED), the finished error string would be "Call to listen() returned error 10093!". You, the prudent developer, would then look up the code and discover that the error occurred because a successful call to WSAStartup() hadn't yet been made.

Aleksandar Pavlov expanded this ReportError() to include descriptions for about a dozen common socket errors. Using his upgraded version, you'll no longer need to lookup what the code means, and your program becomes much more user-friendly with very little effort on your part.

Also included are defines for NETWORK_ERROR and NETWORK_OK. These might be useful when checking the return value of your own networking functions. If your functions returned one of these values, the calling function could perform a simple equality test to reveal any errors: if (myNetworkingFunction() == NETWORK_ERROR) {...}. The calling function could then obtain a specific code with WSAGetLastError() and handle the error accordingly. Ultimately, implementing a good error-handling scheme now will save you many days or weeks of development time as you'll instantly know why your program has failed.

In addition to returning a new client connection, accept() allows the server to extract information about the client rather than through methods requiring extra function calls or time (which may become an issue in game servers, where the speed of the accept loop is especially critical). To take advantage of this functionality, pass in the address of a sockaddr_in struct cast to a sockaddr pointer, i.e. (LPSOCKADDR)&aSockaddrInStructure. Also, declare an integer variable, set the value of the int to the sizeof the sockaddr struct, and pass the address of the integer as the third parameter. If address information is to be returned after the function call, the length parameter must be present.

jdarnold warns us not to believe the MSDN documentation regarding this third parameter: "The MSDN docs imply you don't have to pass in addrlen, that it is just an optional output parameter, but they are wrong. Inbound it says how many bytes are in the sockaddr buffer, and outbound [Winsock] fills in how many [Winsock] used. If you pass zero as the len, [Winsock] won't touch the buffer."

This isn't much of a server since it waits for only one user to connect and then immediately disconnects, but that is the most basic design. Just to clear things up, a call to WSAStartup() includes a WORD specifying what version you want to load (in this case it's 1.1) and the address of a WSADATA structure. Next, we'll cover how to connect with other computers.

Making Your Own Connections

Creating a socket to connect to someone else uses most of the same functions, with the exception of the HOSTENT struct:

  • HOSTENT: A structure used to tell the socket to which computer and port to connect. These structures commonly appear as LPHOSTENT variables, which are merely pointers to HOSTENT structures. As you code for Windows, you'll generally find that any data type preceded by LP denotes that the type is actually a pointer to a "base" type (for example, LPCSTR is a pointer to a C string, also known as char *).

So, let's get right to the code:

#include <windows.h>
#include <winsock.h>
#include <stdio.h>

#define NETWORK_ERROR -1
#define NETWORK_OK     0

void ReportError(int, const char *);


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow) {
	WORD sockVersion;
	WSADATA wsaData;
	int nret;

	sockVersion = MAKEWORD(1, 1);


	// Initialize Winsock as before
	WSAStartup(sockVersion, &wsaData);


	// Store information about the server
	LPHOSTENT hostEntry;

	hostEntry = gethostbyname("www.yahoo.com");	// Specifying the server by its name;
							// another option: gethostbyaddr()

	if (!hostEntry) {
		nret = WSAGetLastError();
		ReportError(nret, "gethostbyname()");	// Report the error as before

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Create the socket
	SOCKET theSocket;

	theSocket = socket(AF_INET,			// Go over TCP/IP
			   SOCK_STREAM,			// This is a stream-oriented socket
			   IPPROTO_TCP);		// Use TCP rather than UDP

	if (theSocket == INVALID_SOCKET) {
		nret = WSAGetLastError();
		ReportError(nret, "socket()");

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Fill a SOCKADDR_IN struct with address information
	SOCKADDR_IN serverInfo;

	serverInfo.sin_family = AF_INET;

	// At this point, we've successfully retrieved vital information about the server,
	// including its hostname, aliases, and IP addresses.  Wait; how could a single
	// computer have multiple addresses, and exactly what is the following line doing?
	// See the explanation below.

	serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);

	serverInfo.sin_port = htons(80);		// Change to network-byte order and
							// insert into port field


	// Connect to the server
	nret = connect(theSocket,
		       (LPSOCKADDR)&serverInfo,
		       sizeof(struct sockaddr));

	if (nret == SOCKET_ERROR) {
		nret = WSAGetLastError();
		ReportError(nret, "connect()");

		WSACleanup();
		return NETWORK_ERROR;
	}


	// Successfully connected!


	// Send/receive, then cleanup:
	closesocket(theSocket);
	WSACleanup();
}


void ReportError(int errorCode, const char *whichFunc) {
   char errorMsg[92];					// Declare a buffer to hold
							// the generated error message
   
   ZeroMemory(errorMsg, 92);				// Automatically NULL-terminate the string

   // The following line copies the phrase, whichFunc string, and integer errorCode into the buffer
   sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);

   MessageBox(NULL, errorMsg, "socketIndication", MB_OK);
}

The most complicated line in the listing is the following:

serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);

because it performs several operations---one of them relatively hidden---at once. Let's take it apart step by step:

The h_addr_list member of the HOSTENT struct is basically defined as char **h_addr_list, which is an array of strings, or char *'s. gethostbyname() identified and copied all of the known addresses of the server into this list. However, does the concept of multiple addresses fundamentally make sense? Actually, it does. Your computer, in fact, has an array of general networking addresses. Your Internet address may be 205.182.67.96, your LAN address may be 10.0.0.2, and all computers on which Windows is installed naturally have a "loopback" address of 127.0.0.1, used by the computer to refer to itself on the local network. The same concept applies in the realm of Internet addresses or IP's, which is why a list is needed rather than storage space for a single address. Note that the preferred address, that is, the most accessible address, is always copied into the first element of the list, followed by the second preferred or other addresses.

What's *hostEntry->h_addr_list doing? You might guess that the deference operator (*) is being used to access a single address in the list. However, by failing to provide a specific index, the dereference operation automatically reveals the first, preferred address. That particular section is equivalent to *hostEntry->h_addr_list[0], which is guaranteed to exist since the server must have at least one address.

Next, the char * returned by the dereferencing operation is cast into an in_addr * or LPIN_ADDR. Finally, another deference operation is performed to return the in_addr struct referred to by the pointer, which can only hold a single address. The resulting in_addr struct is then assigned to serverInfo.sin_addr. The subsequent connect() takes the one address as a parameter when forming a connection to the server.

If the server's IP address is known, a valid HOSTENT may be obtained through the use of gethostbyaddr() (as opposed to gethostbyname() used in the previous listing):

LPHOSTENT hostEntry;
in_addr iaHost;

iaHost.s_addr = inet_addr("204.52.135.52");

hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);

if (!hostEntry) {
	// Handle accordingly
}

In this case, inet_addr() is utilized to copy a string denoting the IP address directly into an in_addr struct. Afterwards, the address of the struct is cast into a const char * as required by gethostbyaddr(). Both methods are referred to as resolving the server address since Winsock returns full address records from partial information.

A few additional notes: port 80 was used simply because Internet web page transfers occur over that port. If you were to send a string to a web server requesting a specific file and attempt to receive something back, you'd have a very simple web browser. Of course, that string must include a full HTTP command. It's great that we can listen and connect to other computers, but communication also involves sending and receiving.

Sending and Receiving

Sending is handled, conveniently enough, by the send() function:

int send(
  SOCKET s,
  const char * FAR buf,
  int len,
  int flags
);

Basically you would copy whatever you wanted into a buffer and use the send() function on a connected socket to make the data go to the other end:

char buffer[256];		// Declaring a buffer on the stack
char *buffer = new char[256];	// or on the heap

ZeroMemory(buffer, 256);
strcpy(buffer, "Pretend this is important data.");

nret = send(theSocket,
	    buffer,
	    strlen(buffer),	// Note that this specifies the length of the string; not
				// the size of the entire buffer
	    0);			// Most often is zero, but see MSDN for other options

delete [] buffer;		// If and only if the heap declaration was used

if (nret == SOCKET_ERROR) {
	// Get a specific code
	// Handle accordingly
	return NETWORK_ERROR;
} else {
	// nret contains the number of bytes sent
}

Receiving is the same process, backwards:

char buffer[256];		// On the stack
char *buffer = new char[256];	// or on the heap

nret = recv(theSocket,
	    buffer,
	    256,		// Complete size of buffer
	    0);

delete [] buffer;		// Manipulate buffer, then delete if and only if
				// buffer was allocated on heap

if (nret == SOCKET_ERROR) {
	// Get a specific code
	// Handle accordingly
	return NETWORK_ERROR;
} else {
	// nret contains the number of bytes received
}

What's interesting to note is that there's a button on the toolbar in Microsoft Outlook labeled "Send/Recv." Is "Receive" abbreviated to "Recv" simply to ensure the button looks right, or is it a programmer's habit from typing recv() so many times? Form your own conspiracy theories (again, good for smalltalk at parties).

This is where I ran into a little problem when writing my own Winsock programs. Just using recv() is great when you know exactly how much data you'll be receiving (such as in a game, where the first byte can be a command and the next byte be a parameter, etc.), but when you don't know, what do you do? If the data you're receiving is terminated by a newline character (a common problem with Java clients talking to C servers), you can write a readLine() function to capture everything up to that character. Here's what I used:

char * readLine() {
   vector theVector;
   char buffer;
   int bytesReceived;

   while (true) {
      bytesReceived = recv(theSocket, &buffer, 1, 0);
      if (bytesReceived <= 0)
         return NULL;

      if (buffer == '\n') {
         char *pChar = new char[theVector.size() + 1];
         memset(pChar, 0, theVector.size() + 1);

         for (int f = 0; f < theVector.size(); f++) {
            pChar[f] = theVector[f];
	 }
         return pChar;
      } else {
         theVector.push_back(buffer);
      }
   }
}

A vector is utilized instead of an array because its storage space may be increased automatically to suit the length of the line. If recv() returns an error (indicated by bytesReceived being less than zero), NULL is returned. Since this is a possibility, calling functions should ensure that the string returned from readLine() is valid before use. Inside the loop, a single char is received from the socket and, if not a newline character, added to the vector. If it is a newline character, the contents of the vector are copied into a C string and returned. The string is declared to be one char larger than the vector and memset()'ted to zero so that the returned line will be automatically NULL-terminated. Ending strings with NULL prevents unusual errors and is generally good programming practice.

Nor presents this cleverly improved version with support for backspaces and the ability to change the newline character easily:

// Code originally written by Nor.  Modified slightly to
// support the MessageBox() API, make logic more readable,
// align spacing, and add comments.  Posted with permission.

#define backKey '\b'					// To disable backspaces, #define backKey NULL
#define endStr  '\n'

char *readLine(SOCKET s) {
	vector theVector;
	char buffer;
	char *pChar;
	int bytesReceived;

	while (true) {
		bytesReceived = recv(s, &buffer, 1, 0);

		if (bytesReceived <= 0) {
			MessageBox(NULL, "recv() returned nothing.", "socketIndication", MB_OK);
			return NULL;
		}

		switch (buffer) {
			case backKey:			// Handle backspace
				if (theVector.size() > 0)
					theVector.pop_back();
				break;
			case endStr:			// End of string char reached
				pChar = new char[theVector.size() + 1];
				memset(pChar, 0, theVector.size() + 1);

				for (int f = 0; f < theVector.size(); f++) {
					pChar[f] = theVector[f];
				}
				break;
			default:			// Any regular char
				theVector.push_back(buffer);
				break;
		}
	}
}

Non-blocking and Asynchronous Sockets

Up until this point we've been talking about blocking sockets, where calling a function such as accept() waits indefinitely for a user to connect. A non-blocking socket returns immediately whenever it is told to do something, either with a successful result, an error, or nothing (indicating that there will be something to receive later). The disadvantage of using this type is that you'll have to manually query the socket to see if a result has come in on every function you call. You can pass a set of sockets to the select() function to see which ones are ready for reading, writing, or have returned errors.

Functions using asynchronous sockets also return immediately, but you can specify a message to send to your window procedure when a specified event has occurred. For example, you can have the socket send an SOCKET_GOTMSG message whenever it receives something. Usually it's smart to check for errors (cumbersome, but necessary) when you get a socket message to prevent causing unnecessary problems later. First, let's define some functions we'll use to set up an asynchronous socket:

  • int WSAAsyncSelect ( SOCKET s, HWND hwnd, unsigned int wMsg, long lEvent )
    This function is used to identify a socket as asynchronous and associate a message with it. s is the socket you're working with. hwnd is the handle to the window that will receive the message when the socket generates an event. wMsg is the message you want to send to your window procedure (an example is the SOCKET_GOTMSG message from above). The lEvent parameter takes one or more flags that tell the socket on which events to send your message. Some of those flags are:
    • FD_READ: Socket is ready to receive data
    • FD_WRITE: Socket is ready to send data
    • FD_ACCEPT: Used in servers, this message indicates a user has connected
    • FD_CONNECT: Used in client applications, this message tells you the socket has connected
    • FD_CLOSE: The socket has just been closed
  • WSAGETSELECTERROR ( LPARAM lparam )
    Determines if the socket has returned an error. Technically, this is not a function but a macro (you can really generate smalltalk at parties with this little factoid).
  • WSAGETSELECTEVENT ( LPARAM lparam )
    Another useful macro defined in winsock2.h is WSAGETSELECTEVENT(), which is used to see exactly what the socket has done.

So, let's set up an asynchronous socket:

// We begin by creating a flag that Windows will use to contact us when something happens
#define THERE_WAS_A_SOCKET_EVENT	WM_USER + 100	// WM_USER is a base for custom messages

// Somewhere in our initialization code after CreateWindow (), we call WSAAsyncSelect ()
WSAAsyncSelect ( theSocket, hwnd, THERE_WAS_A_SOCKET_EVENT, FD_READ | FD_WRITE | FD_CONNECT | ... );

// This translates: Windows, please contact me using the THERE_WAS_A_SOCKET_EVENT flag that I
// previously defined whenever there's data to read (FD_READ), or when I'm free to send data
// (FD_WRITE), or when I've successfully connected to someone else (FD_CONNECT), or when...etc.

// In our window procedure (the function which handles all the messages that Windows sends to your app)
LRESULT WINAPI TheWindowProcedure ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) {

	switch ( msg ) {
		case THERE_WAS_A_SOCKET_EVENT:
			if ( WSAGETSELECTERROR ( lParam ) ) {		// If an error occurred,
				closesocket ( theSocket );
				WSACleanup ();				// Shutdown Winsock
				return NETWORK_ERROR;
			}
			switch ( WSAGETSELECTEVENT ( lParam ) ) {	// What happened, exactly?
				case FD_READ:
					// Receive data
					break;
				case FD_WRITE:
					// Write data
					break;
				case FD_CONNECT:
					// Just connected to server
					break;
				case ...				// Same setup for other flags
					break;
			}
			break;

		// other case statements with logic that handles other Windows messages

	}
}

Note that you cannot define one message for each event, like SOCKET_GOTMSG for FD_READ and then SOCKET_CONNECTED for FD_CONNECT. This is because repeated calls to WSAAsyncSelect () to setup each flag will cancel the effects of the last call to WSAAsyncSelect ().

More Tutorials and Links

I wrote this tutorial in December of 2000, and the seven or so years since then have seen a constant stream of visitors and improvements. I hope you've enjoyed reading as much as I've enjoyed writing: thank you for using Johnnie's Winsock Tutorial. The above is but a brief overview of the possibilities you can reach through Winsock, and others have done a far better job than me at probing the specifics of this subject:

(Move your mouse over a book cover for more information.)

Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs TCP/IP Sockets in C: Practical Guide for Programmers Programming Windows, Fifth Edition 1 year subscription to Maxim magazine

I agree with Thomas Bleeker (MadWizard) that "network programming seems easier than it is." I cannot impress upon you the importance of practicing the use of these functions along with a debugger so you can see what's going on. You'll ultimately have a much better grasp of how things work if you get it wrong, investigate why you got it wrong and then experience the pleasure of getting it right. Making mistakes, in other words, is how we learn.

How Can I Improve?

Is there something that needs clarification? Does the tutorial fail to cover a Winsock-related topic that you wanted to learn about? Has the tutorial met your needs as a software developer? Is it amusing? smartly-written? overly-simplistic? or just right? Post your comment or critique!

Ahmed writes...
"Hi Johnnie. I've been trying to create a proxy with this tutorial, but with no success. What I would like to do is, to redirect the outgoing packets sent by an application to some other IP. Is this possible ? If so, could you please write an example ? Thanks !" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on August 19, 2008, 3:43 am; contact author.

Johnnie Rose, Jr. writes...
"Hi Jamey: The solution may be much simpler than creating a full-blown lobby server. Given the IP address of the game host, you can use inet_addr () and gethostbyaddr () as described in the 'Making Your Own Connections' section to connect to a foreign computer. You can arrange for your game to popup a dialog requesting the IP of a host to connect to, and then provide your friends with your IP to input into your game. Once all your friends connect, you can start the game---no lobby server needed."
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on August 17, 2008, 8:30 am; contact author, or visit author.

Jamey Holden writes...
"Thanks for your reply. The problem was that I was using local addresses to communicate over the local network. It wasn't that I got any error messages, but at that time, I assumed 192.168.0.2 was my worldwide ip address. Now I understand more (and realise I was wrong), but I'm still unsure how to go about connecting to another computer elsewhere in the world. I saw a way to find the 'real' ip address of a person, by connecting to whatismyip.com, but I don't know how to use that to reach someone." and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on August 15, 2008, 2:40 am.

Johnnie Rose, Jr. writes...
"Hi Jamey! Thanks for using my tutorial. When you say that your game didn't work once you moved off your local network, what error messages did you receive?"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on August 14, 2008, 10:11 pm; contact author, or visit author.

Jamey Holden writes...
"Hi I like your tutorial, if only I'd found it earlier :D. Using some other tutorials, I made a simple networked turn based strategy game. When I got to testing it with friends however, it didn't work (internet instead of local network). I now know I need a server to connect to? I was wondering if you tell me what steps I need to take, or correct me if I'm wrong. For my current project, ideally I'd like a lobby server which assigns a game host, and sets up a connection between him and all clients" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on August 12, 2008, 3:19 am.

Johnnie Rose, Jr. writes...
"If you run a search on Google, say for Kayleigh Pearson, you'll find that the URL of the search results page is: http://www.google.com/search?q=kayleigh+pearson. In general, a Google results page will have an address like /search?q=[keyword] where the Host is www.google.com. You can send a GET request for a results page via Winsock (see http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Sample) to execute a search on Google---of course, you must then parse the plain HTML that you get back."
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on July 12, 2008, 12:10 pm; contact author, or visit author.

qqqqqqqq writes...
"How do I use WinSock to type characters into, say, Google, and then send it? Or click on a link on a website? A good link might be the best answer. :) Thank you." and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on July 8, 2008, 1:13 pm.

Johnnie Rose, Jr. writes...
"Actually, I think it's 20 questions, and don't worry--I can always choose to not answer. XD Nevar forget to validate the move on the server: send to server -> validate on server -> loop through all players if the move is valid. Culling polygons outside the view frustum is a feature that one graphics library might handle and that another might not, depending upon its level of sophistication. You'll have to check with the specific library you use to determine whether you must handle this yourself."
0 people found this comment helpful; 2 people did not. I did / did not find this comment helpful.
Posted on June 10, 2008, 3:37 pm; contact author, or visit author.

Sam writes...
"Sorry that was me i forgot to change my name... Lawd its 4chan all over again lol. Also for checking if.. say a player is behind a wall.. would it be something like... player requests move -> validate -> move if correct -> send to server -> loop through all players -> all players refresh the players position and check if they are in view or not, or do most graphic libraries automatically check if they are in view via the axes? Sorry for the 21 questions XD -Sam" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 10, 2008, 10:58 am.

Johnnie Rose, Jr. writes...
"Thank you, Anonymous, for raising that point. In fact, you should validate on *both* the client and server because of this important rule: DON'T TRUST THE PLAYER. Assume he will figure out what messages to send to the server to allow him to move wherever he wants, skipping any clientside validation. If there is no serverside validation, he can cheat. First, validate the move on the client. If it's valid, update his view and tell the server. If it's invalid, play cantmovethere.mp3 and do nothing."
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 10, 2008, 8:26 am; contact author, or visit author.

Anonymous writes...
"Strange, i thought it would be quicker to check if the move is valid on the client rather then the server. Can you tell me why i should check if the move is valid on the server instead of the client?"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 10, 2008, 3:46 am.

Johnnie Rose, Jr. writes...
"The usual way this is handled is for the player's location on his computer to change immediately, then the command is sent to the server. If the move is valid, the server updates its view of the player's position, and the move is sent to all other clients. If the move is invalid, the server's view is NOT updated, the move is NOT sent to others, and that player's computer is told to return to the last valid position, which is determined by the server. Sending a struct or 'UP' is up to you. :-)"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 9, 2008, 5:50 pm; contact author, or visit author.

Brandon writes...
"I mean, say you were making a MUD. The user connects, and types UP. You would want the y value for that player to move up by 1 on the server, say the player info was stored in an array of struct's. I'm sure you would do some stuff with recv(). Would you change the player's locations on their computer, then send the struct, or send the characters 'u' and 'p' for the server to handle? "
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 9, 2008, 5:38 pm; contact author, or visit author.

Johnnie Rose, Jr. writes...
"Hi Brandon! I hope that young, indie game programmer finds much success in his upcoming endeavor into networking. I appreciate your comment because you indicated new content that you'd like to see added to the tutorial. What do you mean by 'user that connects to the server specifying variables?' Can you be more specific?"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 9, 2008, 3:13 pm; contact author, or visit author.

Brandon writes...
"This is a great tutorial. One thing I would like to see is adding some functionality here, such as the user that connects to the server specifying variables or something along that. This was a great read for a 13, going on 14 year old indie game programmer who's going to be networking for his next project. Thanks for this tutorial!" and rates this at 4 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 9, 2008, 12:14 am; contact author, or visit author.

Johnnie Rose, Jr. writes...
"Cool, I'll set aside some time to clean up the code and write a short installation guide. I'm thinking of charging a small amount per download (maybe $5?); I think this is fair given how well it works and how pretty it looks! :-) Is there any feature that you, Sam, or others reading this, would add, change or remove from the comment system? I love the music, too! Thinner has some great stuff! As for an ASM tutorial, I recommend http://www.madwizard.org/download/tutors/win32asmtutorial.zip"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 8, 2008, 9:14 pm; contact author, or visit author.

Sam writes...
"Personally i think it's a great idea, i love this site, it's very educational :). I don't do much PHP but im going to take a look to see if i can understand how it works, it will help me be able to make my own as i learn more =).Nice music aswell, and you will probably get the occasional person looking at these tutorials to make pranks and trojans, but you can't really escape that anywhere... Nice music, if you get any time,or if you have nothing to do,can you make a beginners asm tutorial?(" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 8, 2008, 11:25 am.

Johnnie Rose, Jr. writes...
"Hi Sam! I'm pleased that my tutorial is still useful to you! I developed this comment system specifically to enable visitor feedback on my blog. I call it DoComments---literally, software to "do comments"---written in PHP and Javascript. You can read about how it works from my March 12 blog entry, "Johnnie's Winsock Tutorial Relaunched." I'd like to ask you, Sam, as well as others reading this: Do you think that my releasing this software to the public is a good idea?"
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 7, 2008, 6:50 pm; contact author, or visit author.

Sam writes...
"Hey, Sam again. Just wanted to say, i'm still using this tutorial as a reference all the time, btw, the comment system is HOT! :P Thanks Johnnie ;)" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on June 7, 2008, 3:02 pm.

Johnnie Rose, Jr. writes...
"Through the magic of PHP, I've managed to sufficiently randomize the music playlist so that you, my lovely visitors, can now enjoy a Web-based version of the iPod Shuffle. You saw it here first, folks. (That somebody else may have really done it first notwithstanding :-) I've also uploaded 3 new tracks by Selffish to moreso make the problem of listening to the same two songs for an hour, straight, a thing of the past."
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on May 24, 2008, 9:41 pm; contact author, or visit author.

Johnnie Rose, Jr. writes...
"Thank you for letting me know about your concerns, Anonymous. I've improved the visibility of the player bar (tell me if you agree), and I'll see what I can do about randomizing the playlist to avoid repetitive music. Thanks for your compliment about the tutorial being clear and easy to understand: it's satisfying to me to hear that bold simplicity goes a long way. If you enjoyed the tutorial, do consider sending a small donation my way. :-)"
1 person found this comment helpful; 1 person did not. I did / did not find this comment helpful.
Posted on May 23, 2008, 10:53 pm; contact author, or visit author.

Anonymous writes...
"My apologies Johnnie, after a hour of listening to the same two songs, i finally downloaded the page with the intention of reading it off of my computer and cutting out the part with the streaming music, but when i looked at the code i realized that there was a player bar somewhere on the page. I didn't look hard enough the first time because sure enough there it was." and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on May 23, 2008, 8:12 pm.

Anonymous writes...
"Good tutorial, very clear and easy to understand, I would make a suggestion though, the ability to toggle the music on and off, even though it's delightful, I like to listen to my own music while coding." and rates this at 4 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on May 23, 2008, 7:57 pm.

Johnnie Rose, Jr. writes...
"Hi Sam! You're a 14-year-old programmer? Bravo! Keep choosing to encourage that side of yourself and you'll be a professional software developer in no time. I'm glad my tutorial was able to help you."
1 person found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on May 5, 2008, 8:09 am; contact author, or visit author.

Sam writes...
"Thanks for this man, I'm only 14 and I've been dying to create a simple login applet for ages, this tutorial has really helped me alot with it. =D" and rates this at 5 / 5.
0 people found this comment helpful; 0 people did not. I did / did not find this comment helpful.
Posted on May 5, 2008, 7:09 am.

Rose, Unedited ©2008 Johnnie Rose, Jr.
Ad: Personal Loan - Loans - New York Hotel - Unsecured Loans