Demo 2.1 Hello Internet Server

[ LiB ]

Now that you've learned the basics of network programming, you are ready to start your own network program. In this demo, I show you how to create a server that listens for a string of data, prints it out, and quits. You can find the code for this demo on the CD in the directory / Demos/Chapter02/Demo01-HelloInternetServer/, in the file Demo01.cpp. Appendix A (found on the CD) contains instructions for compiling this demo with various compilers.

Basically, the aim of this demo is to create a listening socket and wait for an incoming connection; the demo then reads 128 bytes of data, prints it out to the screen, closes the sockets, and quits.

Include Files

The first piece of code I am going to show you is what I call Code Block 2.1. This block of code appears in all the demos in this chapter, and I want to show it to you only once. Here it is:

 // Code Block 2.1 - Header Includes // This code block includes all of the standard Sockets API/Winsock headers #ifdef WIN32                // Windows 95 and above     #include "winsock2.h"     #include "Ws2tcpip.h" #else                       // UNIX/Linux     #include <sys/types.h>     #include <sys/socket.h>     #include <netinet/in.h>     #include <unistd.h>     #include <netdb.h>     #include <arpa/inet.h> #endif // End Code Block 2.1 - Header Includes 

This block of code essentially relies on the definition of the preprocessor macro WIN32 to tell if it's running on Windows or UNIX/Linux. If you're running Windows, the code just includes Winsock2.h and ws2tcpip.h; if you're running something else, it includes all the official Sockets API header files.

Platform-Independent Defines

Since the base Sockets API and the Winsock libraries differ in some respects, you need to create a way to make your code work no matter which implementation you are using. Since the APIs are almost equivalent, this is easy.

You have several options in this regard; for example, you could do this whenever you want to close a socket:

 #ifdef WIN32     closesocket( sock ); #else     close( sock ); #endif 

This method tends to be a little messy unless, of course, you isolate the socket-closing code into one area, as I do in Chapter 4 when creating a socket wrapper. But for now, this is an awkward and somewhat ugly method. Instead, let me show you Code Block 2.2:

 // Code Block 2.2 - Redefinitions and globals for cross-compatibility #ifdef WIN32                // Windows 95 and above     WSADATA g_wsadata;      // Winsock data holder     #define CloseSocket closesocket     #define GetSocketError WSAGetLastError     #define StartSocketLib WSAStartup( MAKEWORD( 2, 2 ), &g_wsadata );     #define CloseSocketLib WSACleanup();     #ifndef socklen_t         typedef int socklen_t;     #endif #else                       // UNIX/Linux     #define CloseSocket close     #define GetSocketError errno     #define StartSocketLib {}     #define CloseSocketLib {} #endif // End Code Block 2.2 - Redefinitions and globals for cross-compatibility 

Because Winsock needs a WSADATA structure, one is defined in the WIN32 branch of the code. The next four lines address the four differences between Winsock and the Sockets API. For the Windows branch, the macro CloseSocket is just another name for closesocket (note the capitalization differences), and for the Linux branch, it is another name for close . The same thing is done for GetSocketError , which calls either WSAGetLastError or errno , depending on the system.

Finally, the last two lines define the socket library initialization and shutdown stages, which exist in Winsock, but not in the Sockets API. Therefore, the Windows version of StartSocketLib calls WSAStartup , and the UNIX version does absolutely nothing (empty brackets).

The Windows block has an extra three lines in it. It turns out that in some older versions of Winsock, the datatype socklen_t is not defined; so whenever the function detects that it doesn't exist, I simply define it as an int . You'll need socklen_t for many socket functions.

The Rest of the Code

This demo follows the basic life cycle of a standard server:

  1. Create socket.

  2. Bind socket.

  3. Listen on socket.

  4. Accept connection.

  5. Receive/send data.

  6. Close socket.

Here's the code for starting up the program:

 #include <iostream> using namespace std; int main() {     int err;     StartSocketLib; 

The program uses the Standard C++ iostream library; if you're not familiar with the library and namespaces, you should check out "C++ Primer" Appendix C (found on the CD).

The err variable is used for error reporting, as you will see in a minute. After that, the StartSocketLib macro is invoked, and depending on what system you are using for compilation, starts up Winsock (Windows) or does nothing (Linux).

Here's the next code segment that creates a socket. (Note that this begins Code Block 2.3.)

 // BEGIN CODE BLOCK 2.3 - Create a Listening Socket on port 4000     int sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );     if( sock == -1 ) {         cout << "Socket creation error!" << endl;         return 0;     }     cout << "Socket created!" << endl; 

After the call to socket() , I check to see if an error occurred, and if so, print out a message and return, quitting the program. If no error occurred, I print out a success message. All of the other functions have similar error message blocks, so in the interests of brevity, I will not print them here.

Here's the rest of the program:

 // create a sockaddr_in for binding, listening on port 4000     struct sockaddr_in socketaddress;     socklen_t sa_size = sizeof( struct sockaddr_in );     socketaddress.sin_family = AF_INET;     socketaddress.sin_port = htons( 4000 );     socketaddress.sin_addr.s_addr = htonl( INADDR_ANY );     memset( &(socketaddress.sin_zero), 0, 8 );     // bind the socket     err = bind( sock, (struct sockaddr*)&socketaddress, sa_size ); 

The first block of code fills out a sockaddr_in structure. The second block binds the socket to port 4000 on every IP address available. Here's the listening and accepting code. (Note that Code Block 2.3 ends after the socket is told to listen.)

 // listen on the socket     err = listen( sock, 16 );     // END CODE BLOCK 2.3 - Create a Listening Socket on port 4000     // wait for an incomming connection now     int datasock;     datasock = accept( sock, (struct sockaddr*)&socketaddress, &sa_size ); 

It creates a new socket when a connection is received. The datasock is used for communicating with the client. And here is the code for receiving the message from a client and printing it:

 // receive data     char buffer[128];     err = recv( datasock, buffer, 128, 0 );     cout << "Data received:" << endl;     cout << buffer << endl; 

Finally, here is the code for closing the sockets and shutting down the system:

 shutdown( datasock, 2 );     CloseSocket( datasock );     shutdown( sock, 2 );     CloseSocket( sock );     CloseSocketLib; } 

At this point, you could compile and run the program, but it's pretty pointless without the existence of anything that could send data to the program. That is what the next section is for.

[ LiB ]


MUD Game Programming
MUD Game Programming (Premier Press Game Development)
ISBN: 1592000908
EAN: 2147483647
Year: 2003
Pages: 147
Authors: Ron Penton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net