Using these classes, getting your program hooked to the Net really can be as simple as plugging in a socket.
Introduction
Building client/server multi-platform applications requires a common method of communication between client and server across multiple platforms. Almost all non-embedded operating systems contain an implementation of the TCP/IP network protocol; and given the dominance of TCP/IP as the backbone of the Internet, most client/server environments use TCP/IP as the protocol of choice.
The TCP/IP socket is an IPC (Interprocess Communication) mechanism which resides on top of the network protocol. It provides programmers with a somewhat common API so that they do not have to deal with the actual TCP/IP protocol.
There are two basic types of sockets: a connection oriented socket (TCP) and a connectionless socket (UDP). Using a connection oriented socket is like talking to a friend. You have the attention of your friend and you also know that your message is being received. A connectionless oriented socket works like mailing a letter. The message has been sent but you have no idea if the message was received, and you also do not know when the message may be received.
This article discusses two C++ classes that encapsulate connection oriented TCP multi-platform communications. One class is used by a client application and the other is used by a server. Both of these C++ classes require an ANSI C++ compiler.
The client socket class is used to connect to a socket server, send a request consisting of a character string, receive a reply, and disconnect from the socket server. The socket server listens on a specific TCP port, reads the client request, performs some sort of processing to derive a reply, sends the reply to the client, disconnects from the TCP port, and resumes listening.
There are two major benefits of using these classes as opposed to calling the raw socket API functions: 1) the classes shield the complexity of socket communications from the programmer; and 2) these socket classes function in many different OS environments. I implemented this socket interface using classes instead of straight functions to allow an application to easily use multiple sockets at once and to reduce the number of function parameters.
I present the source code here for the Unix operating system. The full source available via on the CUJ website (www.cuj.com/code) includes conditional compilation directives for both Unix and Windows.
The Client Class
The client class is called csocket. The class header file is shown in Listing 1. The class contains the socket data members clientsock, sockClientAddr, and lpHostEnt. The class also contains the data members client_hostname and client_port to keep track of the details of the current socket server. The additional data member client_init indicates whether the client socket has been properly initialized.
Before using the client class, you must know the host name of the socket server you wish to connect to as well as the TCP port number that the socket server is listening on. See the article, "A Socket Location Server," in the September 2000 issue of CUJ for an example method of isolating the TCP/IP addressing details from the socket client.
The client socket class contains three constructors. The no-arg constructor can be used to create the socket. This constructor will not properly initialize the socket server details. The other two constructors take two parameters consisting of the host name and TCP port number of the socket server. The client class will not actually test the connection between the client and server during initialization even if the socket server details are given.
If the socket server details were not provided during the creation of the client socket class, the host name and TCP port number can be manually supplied using the classs set_active member function shown in Listing 2. This function can also be used at any time to change which socket server the client class is talking to.
Once the class contains the socket server host name and port number information, you can use the class to connect to the socket server, send a request, and receive a reply, by using its send_receive function, also shown in Listing 2. This function will automatically initiate a connection with the socket server, send your request, wait for a reply, and then close the socket.
The get_active member function shown in Listing 2 can be used to obtain the details of the currently defined socket server.
The Server Class
The server socket class is called ssocket. Its header file is shown in Listing 3. The class implements a connection-oriented iterative server. The server waits while being read-blocked for a client connection and processes one request at a time.
The server class contains similar data members to the client socket class except that there are two socket definitions, one for the server and another for the connecting client.
Before using the socket server class, you must decide which TCP port number the server will listen to. The port number must be unique across all socket servers within the environment.
The server socket class operates in a manner very similar to the client socket class. There are two constructors in the server socket class: the no-arg constructor and a constructor that requires the TCP port number. The second constructor properly initializes the class by calling the init function shown in Listing 4. If you create the server socket by using the no-arg constructor, you must properly initialize the class by calling its init member function. The status of the server socket initialization should be checked by using the is_init member function shown in Listing 3.
Constructing a server using the server socket class consists of the following steps. Once the server socket has been properly initialized, your socket server application should enter a loop that consists of calling the member function server_wait shown in Listing 4. This function listens to the TCP port for a client connection. The server application will block until a connection is detected. Once a client connection has been established, your server application should read the incoming request using the recv_data member function shown in Listing 4 followed by whatever processing is necessary to derive the reply. You then send the reply to the client by calling the send_data member function. When your server is through communicating with the client, it should close the socket to the client by calling the close_client member function.
Your socket server application should also have a provision for graceful termination upon receiving a specific request to do so from the client. When your server application receives such a request, it should call the close_all member function shown in Listing 4. This function will close the client and server sockets.
Construction Details
When sending or receiving socket data, the TCP/IP protocol stack decides how much data will be sent or received at any one time. It is possible that the data will not be sent or arrive as a single unit.
Many application designers deal with this issue by using a terminal value as part of the data. For example, many Internet protocols use two carriage return/line feed sequences in a row to denote the end of the data stream.
These two C++ classes do not depend on a terminal value. Instead, the classes use a specific data size to determine whether more data is available. The data size is based on the #defined constant IPC_SR_BUFSIZE, which is part of the sockets header file flsocket.h (supplied with the online source code at www.cuj.com). Data is sent or received in chunks. Each chunk consists of one or more bytes but is no larger than the defined size. A chunk consisting of less than the defined size is used to indicate that there is no more data to send or receive.
When you use these classes instead of straight API function calls, you get reduced complexity but reduced flexibility as well. The raw socket API functions have many more features than I have included in these classes. The classes concentrate on delimiter-less string communication using a connection oriented environment. Any developer using these classes would gain simplicity but lose flexibility and especially low-level control over the socket.
Both classes make heavy use of the standard std::string class. Your C++ compiler must provide a proper implementation of the string class, including the member functions c_str, substr, and length.
Example Applications
A simple client socket application is shown in Listing 5. It uses the client socket class. This application will connect to a specific socket server (host node6 on TCP/IP port 10010 in the example), send a data string, receive the reply, and then terminate.
The example socket server application shown in Listing 6 uses the server socket class to start listening on TCP/IP port 10010. The server will accept one connection at a time, display the data received, and reply with the same string each time. The server will self terminate when it receives a string containing the word "end".
Conclusion
These two classes create an easy way to perform socket communications. The programmer need not know anything about socket communications in order to use these classes.
The classes are written in ANSI C++ and are multi-platform (Unix and Windows).
All C++ code is licensed using the GPL (GNU General Program License), which allows anyone to acquire a copy of executable programs and source code free of charge. A full copy of the GPL license is included along with the C++ code available on the CUJ website.
Further Reading
For more information on Unix socket programming, refer to W. Richard Stevens, Unix Network Programming Volume 1, Second Edition (Prentice Hall, 1998).
For more information on WinSock programming, see Bob Quinn and Dave Shute, Windows Sockets Network Programming (Addison Wesley, 1996).
For further information on these two C++ classes, see the complete HTML documentation available via ftp or view the documentation online at the Future Lab website, www.future-lab.com.
Richard Smereka helps run Future Lab Inc., providing system development and programming services specializing in human resource and payroll software and the Open Source movement. You can reach him at rsmereka@future-lab.com.