Cliser Examples: TCP/IP (TCP) Echo Service over Winsock |
Echo is a standard TCP/IP service used primarily for testing reachability, debugging software, and identifying routing problems. The service is defined for both the TCP and UDP communication protocols. In this example, we will build a client and server for the echo service in C++ using TCP.
An echo server listens for clients on port 7. When it receives a message from a client, an echo server simply transmits the message it received back to the client. A TCP (connection-oriented) echo server continuously echoes what the client sends it until the connection is closed.
A TCP daytime client thus connects to an echo server, reads what the user enters and sends it to the server, and then reads and displays the server's reply, until the user enters some kind of end-of-input sentinel (e.g., control-d, or the string quit).
To build a client and server for the daytime service, we follow the steps described previously:
$ java cliser.GUIThis will display the Cliser graphical user-interface, as shown below:
void Echo_TCP_Winsock_Thread::interactWithClient() { string message; int numCharsReceived; for (;;) { numCharsReceived = this->receive(message); if (numCharsReceived == 0 || message == "") break; this->send(message); } }Our server customization is this simple because of the Cliser system architecture which, among other things, provides the easy-to-use, protocol-independent send() and receive() communication primitives.
Note that because the TCP protocol is connection-oriented, and the client connection is accepted prior to the execution of interactWithClient(), our method need only define what the server does to interact with the client.
The result is the file Echo_POSIX_Concurrent_TCP_Winsock_Server.cpp.
void Echo_TCP_Winsock_Client::interactWithServer() { string messageSent, messageReceived; const string SENTINEL = "quit"; cout << "Enter lines of text (" << SENTINEL << " to quit)\n"; for (;;) { getline(cin, messageSent); if ((messageSent == SENTINEL) || cin.eof()) break; this->send(messageSent); this->receive(messageReceived); cout << messageReceived << endl; } }The result is the file Echo_TCP_Winsock_Client.cpp. As was the case with our server, our client customization is straightforward thanks to Cliser's simple send() and receive() communication primitives.
If you haven't done so already, you should extract the Cliser library code. For example you could extract the files using WinZip to C:\CLISER\. You will want to copy the LIB files ( cliser.lib and pthread.lib, from the directory CLISER\LIB\C++\WINSOCK) into the Visual C++ Library directory, which may be C:\Program Files\Microsoft Visual Studio\VC98\LIB\.
Once the files are extracted, we can run Visual C++. We want to create a new blank workspace. From the File menu, select New. Select the Workspaces tab, enter in a Workspace name. "Echo" in our example. Your location can be anywhere (ie: C:\CLISER\ECHO).
In this new workspace, we need to add two Win32 Console Application projects. You may create these projects by selecting New from the File menu. Select the projects tab, and then the approriate entry. Ensure that "Add to current workspace" is selected for both of the projects. You should name your servers approriately. For the Echo programs, I called the applications "EchoClient" and "EchoServer". You may wish to modify the Cliser generated files to suit your own purposes after adding them to the project, so that you may use the IDE (Intergrated Development Environment).
The next step is to add the correct files to the projects. Choose the client first, by going to the Project menu, Set Active Project and the name of your client. Then, again, goto the Project menu, Add To Project, and Files. This will bring up a file dialog box. From here you should find your Cliser generated client source code, Echo_TCP_Winsock_Client.cpp in our case. Then we select the server project, and add to it the Cliser generated server source code, Echo_POSIX_Concurrent_TCP_Winsock_Server.cpp in our case.
The last step before compiling is to configure the projects. Bring up the Workspace window (View, Workspace), click on FileView, and select Workspace ..., at the top of the list. This allows us to configure both projects properly at the same time. Now we can goto the Settings in the Project menu. Change the "Settings For" option to "All Configurations". Select the C/C++ tab. Change Category to Preprocessor. Now add the Cliser include path to the "Additional include directories:" listing.
Next, change category to "C++ Language", and we enable Run-Time Type Information (RTTI). We also need to tell Visual C++ that we would like to link our program to a few non-standard libraries. We do this by clocking on the Link tab, beside the C/C++ tab. Under Category General, we add three libraries to the Object/library modules: ws2_32.lib cliser.lib pthread.lib.
Everything should now be configured properly. We may now test to make sure it works, by selecting Rebuild All from the Build menu. If you are happy with your program, you may compile the program for releaseing by going the the Build menu, Set Active Configuration, to the approriate Release configuration.
All of the Cliser code is statically linked. So, if you did not use the pthread library (ie. in the client), you should be able to distribute the compiled program as is. If you used the pthread library, you will also need to distribute the pthread.dll. Pthread.dll needs to be in the same directory as the executable, or in the DOS search PATH. For most people the C:\WINDOWS\ directory will work perfectly.
Note: When building with the Debug configuration, you may receive a linking warning like this: LINK : warning LNK4098: defaultlib "LIBC" conflicts with use of other libs: use /NODEFAULTLIB:library. This warning can safely be ignored. When building under Release configurations, this warning does not appear.
$ ServerExeNameIf you are not the system administrator, you can still run the server by giving a different port as a command-line argument:
$ ServerExeName 7777This allows a server written for a standard service to be thoroughly tested before it is deployed. It also allows server-writers without sysadmin status to test their servers on unrestricted ports.
$ ClientExeName ursa.calvin.eduor if your server is listening at port 7777 on myMachine.myDomain:
$ ClientExeName myMachine.myDomain 7777A sample client session might be as follows:
tasting, tasting client tasting, tasting server strawberry client strawberry server lemon client lemon server orange client orange server quit