Remote Communication Made Easy


This article was contributed by Ken Reed (see the demo project for contact details).

Environment: VC6, NT4, Windows 2000 Professional, XP Professional

Now that networked computers are so common, it is becoming an increasingly frequent programming task to get a program running on one PC to talk to one on another (as in multi-player games, for example). There are lots of ways to do it, but they all seem to require so much hard work. So, starting off with a blank sheet of paper, how would I like to send something to another program?

Well, if you want to send something to a file, you can just write the following (assuming the file is already open):

some_file << "Here is some value " << some_value << "\n";

Seems easy enough. Why can't you do exactly the same to send something to another program? Well, if you write a little bit of code you can, and you'll find that code in the demo project in the file socket.cpp.

Behind the scenes it's all done with sockets. However, I'm not going to teach you how to use sockets (there are some good books on that subject). The idea is for me to hide all the detail away so you don't have to know how sockets work and you can just get on and get programs to talk to each other easily. Hopefully, the Socket class provided will let you do just that.

To use the socket class, you will need to include socket.h in your code and incorporate socket.cpp as a module. Having done that, you're ready to go and you'll find examples in the demo project. There is a little more to do than just streaming the things we want to send; we also have to do the equivalent of opening a file (for example, saying which computer and which program we want to talk to). Looking at the example demo project, we have two programs. One is a "server" that sits waiting for some other program to come and talk to it. When one does, it just writes the information coming from the other program into a window. The other program is the "client" that connects to the server and just sends it the time every second.

Ignoring all the Windows "plumbing" code, the server does the following:

  string text;
  Socket socket;
  socket.bind(3333);
  socket.listen();

  while (true) {
    socket >> text;

    if (text == "exit") {
      socket << "ok\n";

      socket.close();
      PostMessage(main_window, WM_CLOSE, 0, 0);
      break;
    }

    SetWindowText(static_text, text.c_str());
  }

The server first creates a socket. Then it tells it which port number to use (this is the bind call). A port number is needed so that more than one program on the same computer can all use sockets (as long as each program uses a different port number). The port number can be any number not in use on your computer (and should be greater than 1024). To find out which ports are in use, issue the following command from a DOS prompt:

netstat -a

The ports in use are the numbers after the colon in the local address column.

Next, the server calls the listen function. This basically waits until some other program wants to talk. When it does, the listen call returns and we can start reading data from the remote program. This is done just by using the >> operator. In this example, we've read in a string. If the string is "exit", hen we close the socket and post a message to shut down the server (we also send back an "ok" to show that the communication isn't just one-way). If it isn't an exit command, we display the text in a window (the time in our demo example).

On the client side, the code is just as simple:

  server.connect("localhost", 3333);

  while (! shutdown) {
    GetTimeFormat(0, 0, 0, 0, buffer, sizeof(buffer));
    SetWindowText(static_text, buffer);

    text = buffer;
    server << text << "\n";

    Sleep(1000);
  }

  string reponse;

  server << "exit\n";
  server >> response;

  server.close();

The client issues a connect call saying which computer and which port (or program) it wants to connect to. In this case, we have used localhost to specify the local machine but it could be the computer name of anything on the network.

Having done that, it gets the time and sends it to the server. When shutting down, it sends an exit command to the server, asking it to shut down too (and reads back the response from the server although the demo program does nothing with it).

Note the end-of-line character ('\n') when writing to the socket. This is important because the standard library likes streamed data to be surrounded by white space. Programs can appear to hang if you don't provide it.

Try it out (the demo project was created using Visual Studio Version 6). Compile the client and server programs in the demo project and run the server. It will just display the text "Waiting". Then, start the client. Both windows will then display the time, counting up in seconds. Now, close the client window. The server window will also close (because the client told it to shut down).

If all you want to do is get programs to talk to each other, you can stop reading now. However, if you want to "get technical," read on.

Now, what are all those other things in the socket include file?

// This file needs -*- c++ -*- mode

I use emacs. If you do, too, you know what this means. If you don't use emacs, you don't care.

#include "exception.h"

C++ lets you handle errors with exceptions. Some people like them; some people don't. I do, so if I detect an error, I throw an exception (the exception class that I use is included in the demo project). I recommend you use exceptions because they are a great help in debugging. Always put any code using the socket class inside a try block (note the caveat about streams below, though) and catch and report any errors. For example:

  try {
    my_routine_that_uses_sockets();
  }
  catch (Exception & e) {
    MessageBox(0, e.get_error().c_str(), "Ooops!",
               MB_SETFOREGROUND);
  }

Now, there is one thing to watch out for. If there is an error while you are streaming (using the << or >> operators), the standard library will catch the thrown exception and quietly set the stream state to bad or fail (depending on the error). If you stream your data, you must use the standard library error test functions fail() and bad() and not rely on an exception being caught.

  void   bind          (const int port);
  void   close         ();
  void   connect       (const char * const host, const int port);
  void   listen        ();

These have been covered in the example above; there's really not much more to say apart from the fact that if you give a port number of zero to the bind call, it will automatically allocate a free port number. If you want to find out what number has been allocated, use the following call:

  int    get_number     ();

Of course, you still have to let the clients who want to talk to you know this number, but that is a little esoteric for this introductory article.

  int    bytes_read     (bool reset_count = false);
  int    bytes_sent     (bool reset_count = false);

You can read (and reset) the number of bytes sent and received over a socket. I used this when I wanted to display a progress bar while sending files over a socket.

  void   getline        (std::string & s);

The Microsoft string getline function doesn't work with sockets (it can block forever). Consequently, this is a replacement (which also doesn't include the eol character in the result ... just a personal preference).

  void   read_binary    (void * buffer, int buffer_size);
  void   write_binary   (void * buffer, int buffer_size);

Streaming is great because it is just so easy to read (and modern PCs are so fast that it is rare that you notice the overhead). However, if you are sending lots of data very often (especially floating point numbers), it can be very CPU intensive (i.e. slow) converting everything to text and back. So, if you need performance, use these binary functions. Define a structure containing the elements you want to send and do a write_binary at the sender and a read_binary at the receiver. You won't get data transfer between PCs much faster than that.

  void   set_trace      (const char * filename);

If things are not working as expected and you can't figure out what's going wrong, turn on tracing. Pass a file name to this function and everything streamed over the sockets will be logged to that file (another good reason for streaming). Look at it to see exactly what was sent and what was received by each program (saves bags of debug time).

  void   set_connection (void * handle);
  void * get_connection ();

If you need to use these functions, you are smart enough to look at the code to find out what they do. The intended user of the Socket class does not need to go down to this level.

private:
  Socket (const Socket & Socket);    // No copying allowed

No copying of Sockets is allowed because I haven't written a copy constructor ... yet (and I'm not sure I want to). Why? The Socket class contains a buffer where it builds up a line of text to send. Taking a copy (passing the socket as a parameter to a function) and adding to the buffer (inside the function) and then reverting to the original buffer (returning from the function call) is just too error prone. I don't even want to think about it. Does this mean you can't pass a socket as a function parameter? No, it doesn't; just pass it by reference rather than by copy. For example:

void my_function(Socket & socket);

Other Odds and Ends

"What versions of Windows will it run under?" I've tried it on NT4, Windows 2000 Professional, and Windows XP Professional. In principle, it should work on anything from Windows 95 onwards, but I haven't been able to try it out on anything other than those three versions mentioned.

"What's the deal on licencing?" The socket and exception class are free software. You can use, modify, and redistribute the code as described by the GNU Public Licence (version 2). A copy of the licence is included in the demo project.

"What's in the demo project?" Ignoring the Visual Studio-generated files, you will find the following files in Socket_demo.zip:

\Contact.txt                     How to contact me
\Socket.html                     This article in CodeGuru HTML
                                 format

common\exception.h               Exception include file
common\exception.cpp             Exception implementation
common\socket.h                  Socket include file
common\socket.cpp                Socket implementation
common\GNU_Public_Licence.txt    Software licence for the above
                                 two classes

client\client.cpp                Demonstration client program

server\server.cpp                Demonstration server program

Downloads

Download demo project - 35 KB


Comments

  • thank you!

    Posted by johnhuke on 06/09/2011 11:13pm

    thank you!

    Reply
  • Client generates an unhandled exception, if started first.

    Posted by Legacy on 11/27/2003 12:00am

    Originally posted by: Venelin

    The demo is very easy and it works properly only if you 
    
    start the server first. If you star the client first, you
    get a runtime error and abnormal program termination in
    release mode. In debug mode the client generates an
    unhandled exception at the end of the Socket::connect
    method. The best way to handle it is to use the exception
    class. If you replace the line with the connect call in the
    update_handler function with the following lines

    try {
    // try to connect the server
    server.connect("localhost", 3333);
    }
    catch (Exception & e) {
    // exception occured
    // inform the user about the situation
    MessageBox(0, e.get_error().c_str(), "Ooops!", MB_SETFOREGROUND);

    // send a close message to the main application window
    ::PostMessage(main_window, WM_CLOSE, 0, 0);

    // wait for the shutdown signal
    while (! shutdown);
    // clear the shutdown signal
    shutdown = false;

    // exit the update handler
    return 0;
    }

    you can avoid the abnormal program termination. This is a
    good example for the use of the Exception class.

    Best Regards
    v.

    Reply
  • Is your Socket object thread safe?

    Posted by Legacy on 11/11/2003 12:00am

    Originally posted by: dzhao

    Could your Socket object be shared safely by several threads?

    Reply
  • Please help - it won't compile for me !!!

    Posted by Legacy on 11/07/2003 12:00am

    Originally posted by: Patrick

    Sorry, but it won't compile for me. I get an error saying that StdAfx.h is missing from the 'Common' folder.

    Please help.

    Thanks
    Patrick

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The first phase of API management was about realizing the business value of APIs. This next wave of API management enables the hyper-connected enterprise to drive and scale their businesses as API models become more complex and sophisticated. Today, real world product launches begin with an API program and strategy in mind. This API-first approach to development will only continue to increase, driven by an increasingly interconnected web of devices, organizations, and people. To support this rapid growth, …

  • A majority of organizations are operating under the assumption that their network has already been compromised, or will be, according to a survey conducted by the SANS Institute. With many high profile breaches in 2013 occurring on endpoints, interest in improving endpoint security is top-of-mind for many information security professionals. The full results of the inaugural SANS Endpoint Security Survey are summarized in this white paper to help information security professionals track trends in endpoint …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds