|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
BackgroundA generic, light-weighted client/server socket class that is written in C++ is presented here, together with a mini-messenger that is constructed by using this class. There is indeed a vast amount of information about socket programming over the Internet, and it is not time-consuming for one to download relevant code directly, therefore, it is certainly necessary to discuss the advantage of this class first. First of all, this class is light-weighted. One can find socket APIs in MFC, in C#, in .NET framework, but apparently, to take advantage of these APIs, one has to bind the application with MFC, with .NET framework. In some cases, MFC, C# or .NET may not be the optimal solution to the application needs. Also, if the application is to be developed on a Unix/Linux platform, then these APIs are not even available. On the other hand, the class that is presented here can be easily used in a console application; there is no need to provide any window handle for this class to be used. Also, this class can be incorporated directly into applications that are developed on Unix/Linux platform by simply adding Secondly, this class provides high performance client/server structure by using the most generic and low-level building blocks. This gives the great flexibility back to the developer, for example, it is then up to the developer to add an appropriate custom-designed security system. Among other advantages, a server that is built by using this class can run on a Windows machine, the client can be on the same machine (the same machine with inter-processes communication), or more often on a different machine that runs either Windows system or Unix system. The other communication party can be a station on a local network, or it can be a machine that communicates across the Internet. IntroductionThe rest of this article is organized by following the intuitive flow of making a communication happen:
Before the communication, we need to know the basic information about the communication parties: what is the local machine’s name, what is the local machine’s IP address? If I know the remote host’s name is www.yuchen.net, then what is the corresponding IP address? What about vise versa? This basic information can be easily retrieved by using our To make the communication happen, After the communication, the house-keeping job is done by the above star class. It is also worthy of mentioning the other two classes that we developed to help the communication. Because of the complexity of communication, it is important to protect against possible failures. A very simple exception handling class, Another simple class is the system log class called The last section of this article presents a small application that is developed by using the above classes. This application is called mini-messenger, two people can use it to talk to each other. It is essentially a Yahoo! Messenger, but when using it, you don’t have the Yahoo! logo showing on your screen, so you don’t have to worry that your boss who might be watching over your shoulders. The main purpose of this small application is certainly to show a possible way to use the classes we developed. Class myHostInfoThe first step to build a communication is to understand the other party: its domain name, its corresponding IP address. Constructor/Destructor Summary:
Methods Summary:
The following example shows the usage of this class. string serverName = "www.yuchen.net"; myHostInfo myServer(serverName,NAME); string serverName = myServer.getHostName(); string serverAddr = myServer.getHostIPAddress(); cout << "Server name: " << serverName << endl; cout << "Server IP address: " << serverAddr << endl; The output of the above code is as follows (I cannot get the screenshots, let me just use the simple cut-and-paste): Server name: www.yuchen.net
Server IP address: 66.218.85.169
Press any key to continue
Class mySocketSocket is the most fundamental building block for all Internet communications. Constructor/Destructor Summary:
Methods SummaryWe only present the set methods, the get methods are omitted, refer to the code for detail:
The following is an example showing the setting of a socket (notice that we used cout << "Retrieve the local host name and address:" << endl; myHostInfo myLocalMachine; // use the default constructor string localHostName = myLocalHost.getHostName(); string localHostAddr = myLocalHost.getHostIPAddress(); cout << "Name: " << localHostName << endl; cout << "Address: " << localHostAddr << endl; mySocket localSocket(1000); // 1000 is the port number cout << localSocket; // show this socket // change some setting: set linger on localSocket.setLingerOnOff(true); localSocket.setLingerSeconds(10); // show it again cout << endl << "After changing the socket settings ... " << endl; cout << localSocket; Here is the output from the above code: Name: liyang
Address: 209.206.17.121
--------------- Summary of socket settings -------------------
Socket Id: 1960
port #: 1000
debug: false
reuse addr: false
keep alive: false
send buf size: 8192
recv bug size: 8192
blocking: true
linger on: false
linger seconds: 0
----------- End of Summary of socket settings ----------------
After changing the socket settings ...
--------------- Summary of socket settings -------------------
Socket Id: 1960
port #: 1000
debug: false
reuse addr: false
keep alive: false
send buf size: 8192
recv bug size: 8192
blocking: true
linger on: true
linger seconds: 10
----------- End of Summary of socket settings ----------------
Press any key to continue
Class myTcpSocket
Constructor/Destructor Summary:
Once a TCP socket is created, the following methods can be used to build the communication between two sockets. Methods Summary:
Building a communicationNow, it is the time to describe the flow of building a communication between a server and a client (we are not going into the details about The server has to be started first: myTcpSocket myServer(PORTNUM); // create the socket and everything cout << myServer; // show the server configuration This will create a server called The next thing to do is to bind your phone with the phone number you got, you accomplish this by connecting the phone to the phone jack on the wall. The phone jack on the wall is the socket that is just created, and the phone itself is like the myServer.bindSocket(); After this, you are ready to receive phone calls from your friends (and sadly, your billing companies). This is done by waiting for the incoming calls. In our model, this is accomplished by the following: myServer.listenToClient(); When the phone rings, you will accept the incoming call by picking it up: /* wait to accept a client connection. processing is suspended until the client connects */ cout << "server is waiting for client connection ... " << endl; // connection dedicated for client communication myTcpSocket* client; string clientHost; // to hold the client’s name client = myServer.acceptClient(clientHost); // pick up the call! Notice that the server accepts the incoming client call by creating a new socket, it will in fact continue to wait for other incoming calls on its own socket – this can be understood like the “call waiting” function that is offered by phone companies. Once we reach this point, the only thing the server will do is to pass information back and forth with the client by calling the The client side, on the other hand, is quite simple. However, before we start the client, we do need to know which server we are going to call. Assuming we know the IP address of the server (if not, we should know the name of the server, then we can use First, let us build the client: myTcpSocket myClient(PORTNUM); // build the client myClient.setLingerOnOff(true); myClient.setLingerSeconds(10); cout << myClient; // show the client configuration Assuming the server’s IP address is saved in // connect to the server. myClient.connectToServer(serverAddr,ADDRESS); The next is to communicate with the server by using the Mini-MessengerTo demo the usage of the classes we discussed so far, a small application is developed. This application lets you to talk with your party over the Internet without using things like Yahoo! Messenger. With the discussion above, it should be fairly easy to understand both the server and client side now. To use this messenger, assuming your party is the server, he has to start the server on his side, and then he will let you know the IP address of his server, say, by giving you a call (we still need the low-tech). After writing the IP address in your serverConfig.txt file, you can start your client, and you are ready to talk. The server side:#include "..\mySocket\mySocket.h" #include "..\myLog\myLog.h" #include "..\myException\myException.h" #include "..\myHostInfo\myHostInfo.h" myLog winLog; int main() { #ifdef WINDOWS_XP // Initialize the winsock library WSADATA wsaData; winLog << "system started ..." << endl; winLog << endl << "initialize the winsock library ... "; try { if (WSAStartup(0x101, &wsaData)) { myException* initializationException = new myException(0,"Error: calling WSAStartup()"); throw initializationException; } } catch(myException* excp) { excp->response(); delete excp; exit(1); } winLog << "successful" << endl; #endif // get local information for the server winLog << endl; winLog << "Retrieve the local host name and address:" << endl; myHostInfo uHostAddress; string localHostName = uHostAddress.getHostName(); string localHostAddr = uHostAddress.getHostIPAddress(); cout << "----------------------------------------" << endl; cout << " My local host information:" << endl; cout << " Name: " << localHostName << endl; cout << " Address: " << localHostAddr << endl; cout << "----------------------------------------" << endl; winLog << " ==> Name: " << localHostName << endl; winLog << " ==> Address: " << localHostAddr << endl; // open socket on the local host myTcpSocket myServer(PORTNUM); cout << myServer; winLog << "server configuation: " << endl; winLog << myServer; myServer.bindSocket(); cout << endl << "server finishes binding process... " << endl; winLog << endl << "server finishes binding process... " << endl; myServer.listenToClient(); cout << "server is listening to the port ... " << endl; winLog << "server is listening to the port ... " << endl; // wait to accept a client connection. // processing is suspended until the client connects cout << "server is waiting for client connecction ... " << endl; winLog << "server is waiting for client connnection ... " << endl; // connection dedicated for client communication myTcpSocket* client; string clientHost; // client name etc. client = myServer.acceptClient(clientHost); cout << endl << "==> A client from [" << clientHost << "] is connected!" << endl << endl; winLog << endl << "==> A client from [" << clientHost << "] is connected!" << endl << endl; while(1) { string clientMessageIn = ""; // receive from the client int numBytes = client->recieveMessage(clientMessageIn); if ( numBytes == -99 ) break; cout << "[RECV:" << clientHost << "]: " << clientMessageIn << endl; winLog << "[RECV:" << clientHost << "]: " << clientMessageIn << endl; // send to the clien char sendmsg[MAX_MSG_LEN+1]; memset(sendmsg,0,sizeof(sendmsg)); cout << "[" << localHostName << ":SEND] "; cin.getline(sendmsg,MAX_MSG_LEN); if ( numBytes == -99 ) break; string sendMsg(sendmsg); if ( sendMsg.compare("Bye") == 0 || sendMsg.compare("bye") == 0 ) break; winLog << "[" << localHostName << ": SEND] " << sendMsg << endl; client->sendMessage(sendMsg); } #ifdef WINDOWS_XP // Close the winsock library winLog << endl << "system shut down ..."; try { if (WSACleanup()) { myException* cleanupException = new myException(0,"Error: calling WSACleanup()"); throw cleanupException; } } catch(myException* excp) { excp->response(); delete excp; exit(1); } winLog << "successful" << endl; #endif return 1; } The client side:#include "..\mySocket\mySocket.h" #include "..\myLog\myLog.h" #include "..\myException\myException.h" #include "..\myHostInfo\myHostInfo.h" myLog winLog; string serverIPAddress = ""; void readServerConfig(); void checkFileExistence(const string&); int main() { #ifdef WINDOWS_XP // Initialize the winsock library WSADATA wsaData; winLog << "system started ..." << endl; winLog << endl << "initialize the winsock library ... "; try { if (WSAStartup(0x101, &wsaData)) { myException* initializationException = new myException(0,"Error: calling WSAStartup()"); throw initializationException; } } catch(myException* excp) { excp->response(); delete excp; exit(1); } winLog << "successful" << endl; #endif // get local information for the client winLog << endl; winLog << "Retrieve the localHost [CLIENT] name and address:" << endl; myHostInfo uHostAddress; string localHostName = uHostAddress.getHostName(); string localHostAddr = uHostAddress.getHostIPAddress(); cout << "Name: " << localHostName << endl; cout << "Address: " << localHostAddr << endl; winLog << " ==> Name: " << localHostName << endl; winLog << " ==> Address: " << localHostAddr << endl; // get server's information readServerConfig(); winLog << endl; winLog << "Retrieve the remoteHost [SERVER] name and address:" << endl; winLog << " ==> the given address is " << serverIPAddress << endl; myHostInfo serverInfo(serverIPAddress,ADDRESS); string serverName = serverInfo.getHostName(); string serverAddr = serverInfo.getHostIPAddress(); cout << "Name: " << serverName << endl; cout << "Address: " << serverAddr << endl; winLog << " ==> Name: " << serverName << endl; winLog << " ==> Address: " << serverAddr << endl; // an instance of the myTcpSocket is created. At this point a TCP // socket has been created and a port has been defined. myTcpSocket myClient(PORTNUM); myClient.setLingerOnOff(true); myClient.setLingerSeconds(10); cout << myClient; winLog << "client configuation: " << endl; winLog << myClient; // connect to the server. cout << "connecting to the server [" << serverName << "] ... " << endl; winLog << "connecting to the server [" << serverName << "] ... " << endl; myClient.connectToServer(serverAddr,ADDRESS); int recvBytes = 0; while (1) { // send message to server char sendmsg[MAX_MSG_LEN+1]; memset(sendmsg,0,sizeof(sendmsg)); cout << "[" << localHostName << ":SEND] "; cin.getline(sendmsg,MAX_MSG_LEN); string sendMsg(sendmsg); if ( sendMsg.compare("Bye") == 0 || sendMsg.compare("bye") == 0 ) break; winLog << "[" << localHostName << ": SEND] " << sendMsg << endl; myClient.sendMessage(sendMsg); // receive message from server string clientMessageIn = ""; recvBytes = myClient.recieveMessage(clientMessageIn); if ( recvBytes == -99 ) break; cout << "[RECV:" << serverName << "]: " << clientMessageIn << endl; winLog << "[RECV:" << serverName << "]: " << clientMessageIn << endl; } #ifdef WINDOWS_XP // Close the winsock library winLog << endl << "system shut down ..."; try { if (WSACleanup()) { myException* cleanupException = new myException(0,"Error: calling WSACleanup()"); throw cleanupException; } } catch(myException* excp) { excp->response(); delete excp; exit(1); } winLog << "successful" << endl; #endif return 1; } void readServerConfig() { string serverConfigFile = "serverConfig.txt"; checkFileExistence(serverConfigFile); ifstream serverConfig(serverConfigFile.c_str()); // read server's IP address getline(serverConfig,serverIPAddress); serverConfig.close(); } void checkFileExistence(const string& fileName) { ifstream file(fileName.c_str()); if (!file) { cout << "Cannot continue:" << fileName << " does NOT exist!" << endl; exit(1); } file.close(); } Here is a sample output screen (notice that I am using a single PC to talk, still via Internet though): The server:------------------------------------------------------
My local host information:
Name: liyang
Address: 209.206.17.121
------------------------------------------------------
--------------- Summary of socket settings -------------------
Socket Id: 1960
port #: 1200
debug: false
reuse addr: false
keep alive: false
send buf size: 8192
recv bug size: 8192
blocking: true
linger on: false
linger seconds: 0
----------- End of Summary of socket settings ----------------
server finishes binding process...
server is listening to the port ...
server is waiting for client connecction ...
==> A client from [liyang] is connected!
[RECV:liyang]: hello?
[liyang:SEND] yes?
[RECV:liyang]: so we can talk...
[liyang:SEND] looks like so.
!! your party has shut down the connection...
Press any key to continue
The client:Name: liyang
Address: 209.206.17.121
Name: liyang
Address: 209.206.17.121
--------------- Summary of socket settings -------------------
Socket Id: 1944
port #: 1200
debug: false
reuse addr: false
keep alive: false
send buf size: 8192
recv bug size: 8192
blocking: true
linger on: true
linger seconds: 10
----------- End of Summary of socket settings ----------------
connecting to the server [liyang] ...
[liyang:SEND] hello?
[RECV:liyang]: yes?
[liyang:SEND] so we can talk...
[RECV:liyang]: looks like so.
[liyang:SEND] bye
Press any key to continue
ConclusionWe presented a light-weighted server/client class in C++ in this article, I hope this will be of some use in your development work, and certainly there are more things to consider to make this work well. I welcome any comments/suggestions.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||