TCPCom Module
 
PC-to-PC Communication
 

 

The Most Famous Training Example of Socket Programming: Echo Server

The next demonstration is famous because it is the archetype of a client-server communication. The server makes nothing else than sending back the non-modified message received from the client For this reason it is called an echo server. The system can be easily extended so that the server analyzes the message received from the client and returns a tailored response. This is exactly the concept of the  all WEB servers, because they reply to a HTTP request issued by the client browser.

You first code the echo server and start it immediately. As you see, the code differs only slightly from the time server. Here we use a modeless dialog from the ch.aplu.util package that you may download from here.

Java server:

import ch.aplu.tcpcom.*;
import ch.aplu.util.*;

public class EchoServer implements TCPServerListener
{
  private TCPServer server;
  private ModelessOptionPane mop;

  public EchoServer()
  {
    int port = 5000;
    server = new TCPServer(port);
    server.addTCPServerListener(this);
    mop = new ModelessOptionPane("Echo server running. Close to stop.");
    mop.setTitle("Echo Server");
    while (mop.isVisible())
      Thread.yield();
  }

  public void onStateChanged(String state, String msg)
  {
    if (state == TCPServer.MESSAGE)
    {
      mop.setText("Received msg: " + msg + " - echoing it...");
      server.sendMessage(msg);
    }
  }

  public static void main(String[] args)
  {
    new EchoServer();
  }
}

Execute the program locally using WebStart

The client sends the numbers 0 to 99 and displays in a modeless dialog the reply from the server.

Java client:

import ch.aplu.tcpcom.*;
import ch.aplu.util.*;

public class EchoClient implements TCPClientListener
{
  private ModelessOptionPane mop;

  public EchoClient()
  {
    int port = 5000;
    InputDialog id = new InputDialog("Echo Client", "Host?");
    String host = id.readString();
    if (host == null)
      return;
    TCPClient client = new TCPClient(host, port);
    client.addTCPClientListener(this);
    mop = new ModelessOptionPane("Trying to connect to " + host + ":" + port);
    mop.setTitle("Echo Client");
    boolean success = client.connect();
    if (!success)
    {
       mop.setText("Connection failed");
       return;
    }
    mop.setTitle("Reply from server");
    for (int i = 0; i < 100; i++)
    {  
      client.sendMessage("" + i);
      Console.delay(100); // slow down
    }
    client.disconnect();
  }

  public void onStateChanged(String state, String msg)
  {
    if (state.equals(TCPClient.MESSAGE))
        mop.setText("Got: " + msg);
    else if (state.equals(TCPClient.DISCONNECTED))
        mop.setTitle("Disconnected");
  }
  
  public static void main(String[] args)
  {
    new EchoClient();
  }
}

Execute the program locally using WebStart

The client sends the numbers 0 to 99 and displays in a modeless dialog the reply from the server

To simplify the code, the Python versions do not use modeless dialogs, but show the run-time output in the console. The programs are extremely short, but show the essential concepts.

Python server:

from easygui import msgbox
from tcpcom import TCPServer

def onStateChanged(state, msg):
    if state == TCPServer.MESSAGE:
        print "Received msg:", msg, " - echoing it..."
        server.sendMessage(msg)

port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
msgbox("Echo server running. OK to stop","Echo Server")
server.terminate()

 

Python client:

from easygui import msgbox, enterbox
from tcpcom import TCPClient
import time

def onStateChanged(state, msg):
    if state == TCPClient.MESSAGE:
        print msg

title = "Echo Client"
port = 5000

host= enterbox("Echo Server IP Address?", title, "localhost", True)
if host != None:
    client = TCPClient(host, port, stateChanged = onStateChanged)
    client.connect()
    for i in range(100):
        client.sendMessage(str(i))
        time.sleep(0.1)
    client.disconnect()

 

Work-Sharing by Two Computers

It is a challenge since the beginning of computer technology to construct a system where long lasting calculations are performed in parallel by several processors. The different processes must communicate to synchronize their work or, in the simplest case, to collect the results. The computers can be located all over the world and communicate via the Internet. To do this without human interaction, the programs use TCP-sockets to exchange information.

In this demonstration example we want to determine the number PI using the well-known Monte-Carlo method by throwing n raindrops randomly on a square of side 1 that contains a quarter circle with radius 1. If k drops lay in the circle, the limiting value of k / n is the proportion of the quarter circle and the square areas which is pi/4.

Unfortunately the convergence of this method is very bad, so n must be very large, say 100'000'000 to get PI to only a few decimals. Throwing raindrops on a square can easily be shared between two computers, because there is no need to synchronize the processes. In our demonstration the server just collects the results from the client worker and displays the individual data of the server and client and the total. In this demonstration we only show the Java version because we need a modeless dialog window that is offered by the EntryDialog class in the ch.aplu.util package (download from here.). Nevertheless the code is blown up a little bit by the user interface. We also use Monitor.putSleep() to put the main thread into a waiting state before we call Montitor.wakeUp() to start the calculation after a client came in. The essential code for the calculation and exchange of data is still rather simple due to the event based structure of the programs.

Java server:

import ch.aplu.tcpcom.*;
import ch.aplu.util.*;

public class StatServer implements TCPServerListener
{
  private final int sliceSize = 20000000;
  private StringEntry local = new StringEntry("Local Status: ");
  private StringEntry remote = new StringEntry("Remote Status: ");
  private EntryPane pane1 = new EntryPane(local, remote);
  private StringEntry locresult = new StringEntry("Local Result: ");
  private StringEntry remresult = new StringEntry("Remote Result: ");
  private StringEntry totresult = new StringEntry("Total Result: ");
  private EntryPane pane2 = new EntryPane(locresult, remresult, totresult);
  private EntryDialog dlg = new EntryDialog(pane1, pane2);
  private int local_n = 0;
  private int local_k = 0;
  private int remote_n = 0;
  private int remote_k = 0;
  private TCPServer server;

  public StatServer()
  {
    initDialog();
    int port = 5000;
    server = new TCPServer(port);
    server.addTCPServerListener(this);
    Monitor.putSleep();
    if (dlg.isDisposed())
      return;
    local.setValue("Working...");
    int n = 0;
    int k = 0;
    long startTime = System.nanoTime();
    while (!dlg.isDisposed() && server.isConnected())
    {
      double zx = Math.random();
      double zy = Math.random();
      if (zx * zx + zy * zy < 1)
        k += 1;
      n += 1;
      if (n % sliceSize == 0)
      {
        double pi = 4.0 * k / n;
        double t = (System.nanoTime() - startTime) / 1E9;
        String info = String.format("n: %d; k: %d; pi: %f; t: %3.1f", n, k, pi, t);
        locresult.setValue(info);
        local_n = n;
        local_k = k;
        showTotal();
      }
    }
    server.terminate();
  }

  public void onStateChanged(String state, String msg)
  {
    if (state.equals(TCPServer.LISTENING))
    {
      local.setValue("Waiting");
      remote.setValue("Not connected.");
    }
    else if (state.equals(TCPServer.CONNECTED))
    {
      remote.setValue("Connected. Working...");
      Monitor.wakeUp();
    }
    else if (state.equals(TCPServer.TERMINATED))
    {
      local.setValue("Terminated");
      remote.setValue("Not connected.");
    }
    else if (state.equals(TCPServer.MESSAGE))
    {
      String[] li = msg.split(";");
      int n = Integer.parseInt(li[0].trim());
      int k = Integer.parseInt(li[1].trim());
      double pi = 4.0 * k / n;
      String info = String.format("n: %d; k: %d; pi: %f", n, k, pi);
      remresult.setValue(info);
      remote_n = n;
      remote_k = k;
      showTotal();
    }
  }

  private void initDialog()
  {
    local.setEditable(false);
    remote.setEditable(false);
    locresult.setEditable(false);
    remresult.setEditable(false);
    totresult.setEditable(false);
    dlg.setTitle("Server Information");
    dlg.show();

    local.setValue("Waiting for connection...");
    locresult.setValue("(n/a)");
    remresult.setValue("(n/a)");
    totresult.setValue("(n/a)");

    dlg.addExitListener(new ExitListener()
    {
      public void notifyExit()
      {
        dlg.dispose();
        server.terminate();
        Monitor.wakeUp();
      }
    }
    );
  }

  private void showTotal()
  {
    int n = remote_n + local_n;
    int k = remote_k + local_k;
    double pi = 4.0 * k / n;
    String info = String.format("n: %d; k: %d; pi: %f", n, k, pi);
    totresult.setValue(info);
  }

  public static void main(String[] args)
  {
    new StatServer();
  }
}

Execute the program locally using WebStart (not yet active).

Java client:

import ch.aplu.tcpcom.*;
import ch.aplu.util.*;

public class StatClient implements TCPClientListener
{
  private final int sliceSize = 20000000;
  private StringEntry local = new StringEntry("Local Status: ");
  private StringEntry remote = new StringEntry("Remote Status: ");
  private EntryPane pane1 = new EntryPane(local, remote);
  private StringEntry locresult = new StringEntry("Local Result: ");
  private EntryPane pane2 = new EntryPane(locresult);
  private EntryDialog dlg = new EntryDialog(pane1, pane2);
  private TCPClient client;

  public StatClient()
  {
    initDialog();
    String host = "localhost";
    int port = 5000;
    client = new TCPClient(host, port);
    client.addTCPClientListener(this);
    boolean success = client.connect();
    if (!success)
      return;
    int n = 0;
    int k = 0;
    long startTime = System.nanoTime();
    while (!dlg.isDisposed() && client.isConnected())
    {
      double zx = Math.random();
      double zy = Math.random();
      if (zx * zx + zy * zy < 1)
        k += 1;
      n += 1;
      if (n % sliceSize == 0)
      {
        double pi = 4.0 * k / n;
        double t = (System.nanoTime() - startTime) / 1E9;
        String info = String.format("n: %d; k: %d; pi: %f; t: %3.1f", n, k, pi, t);
        locresult.setValue(info);
        client.sendMessage(n + ";" + k);
      }
    }
  }

  public void onStateChanged(String state, String msg)
  {
    if (state.equals(TCPClient.CONNECTION_FAILED))
      local.setValue("Connection failed");
    else if (state.equals(TCPClient.DISCONNECTED))
    {
      local.setValue("Connection broken");
      remote.setValue("Disconnected");
    }
    else if (state.equals(TCPServer.CONNECTED))
    {
      local.setValue("Working...");
      remote.setValue("Connected. Working...");
    }
  }

  private void initDialog()
  {
    local.setEditable(false);
    remote.setEditable(false);
    locresult.setEditable(false);
    dlg.setTitle("Client Information");
    dlg.show();

    local.setValue("Trying to connect...");
    remote.setValue("Disconnected");
    locresult.setValue("(n/a)");

    dlg.addExitListener(new ExitListener()
    {
      public void notifyExit()
      {
        dlg.dispose();
        client.disconnect();
      }
    }
    );
  }

  public static void main(String[] args)
  {
    new StatClient();
  }
}

Execute the program locally using WebStart (not yet active).

tcpstat

The Python versions use the TigerJython IDE that includes the EntryDialog and TCPCom packages. The programs are part of the TCPCom distribution.