|
An Event-Driven Socket Library
for Java and Python
The Importance of Computer and Communication Technologies
The exchange of data between computer systems plays an extremely important role in our interconnected world. Therefore we often speak of the combined computer- and communication technologies that should be mastered. In this chapter you learn how to handle the data exchange between two computer systems using the TCP/IP protocol which is used in all Internet connections, for example the Web and all streaming services (data clouds, voice, music, video streaming).
The socket programming is based on the client-server model, which has already been described in some details on the TcpJLib and BtLib sites. Most important and somewhat unexpected is the fact that the communication partners, the server and the client, are not completely symmetrical. Rather, first the server program must be started before the client program can engage a connection to it. In order to identify the two computers on the Internet, its IP address is used. In addition, server and client specify one of 65536 communication channels (IP ports), that is selected with a number in the range 0..65535.
When the server starts, it creates a server socket (like a electrical plug) that uses a particular port and goes in a wait state. We say that the server is "listening" for an incoming client, so the server is in the LISTENING state. The client creates a client socket (the plug counterpart) and tries to establish a communication link to the server using its IP address and port number.
The Easy Way of Socket Programming Using Objects and Events
The library TCPCom simplifies the socket programming essentially, since it describes the current state of the server and the client with state variables and considers the change of the variables to be caused by events. This programming model corresponds to the natural feeling of many people to describe the communication between two human partners. In the free download you find three versions of the library for Java SE, Android and Python and many demonstration examples. The Java SE and Python versions may also be used on the Raspberry Pi and the Java SE version on the Lego EV3 robot running leJOS.
As usual in a event-driven model, a callback function, here called stateChanged(state, msg) is invoked by the system, when an event is triggered. The Python library module is integrated into TigerJython, but can also be downloaded from this site to be used in a pure Python environment. The Java library module is distributed as JAR archive.
When the server is started, it creates a TCPServer object specifying the ports and the callback function onStateChanged() and embarks in the LISTENING state.
Python skeleton:
from tcpcom import TCPServer
def onStateChanged(state, msg):
...
server = TCPServer(port, stateChanged = onStateChanged)
|
Java skeleton:
import ch.aplu.tcpcom.*;
TCPServer server = TCPServer(port);
server.addStateListener(this);
public void onStateChanged(String state, String msg)
{...}
|
The callback onStateChanged (state, msg) has two string parameters state and msg that describe the status change of the server:
state
|
msg
|
Description
|
TCPServer.LISTENING
|
port
|
An existing connection was terminated (or the server is started) and the server listens for a new connection
|
Server.PORT_IN_USE
|
port
|
The server cannot go into the LISTENING state because the port is occupied by another process
|
TCPServer.CONNECTED
|
client's IP address
|
A client has signed up and was accepted
|
TCPServer.MESSAGE
|
received message
|
The server has received a message
|
TCPSever.TERMINATED
|
(empty)
|
The server is terminated and does not listen anymore
|
The client starts with the creation of a TCPClient object specifying the IP address of the server, the port and the callback function onStateChanged(). By invoking connect() it starts a connection trial.
Python skeleton:
from tcpcom import TCPClient
client = TCPClient(host, port, stateChanged = onStateChanged)
client.connect()
|
Java skeleton:
import ch.aplu.tcpcom.*;
TCPClient client = TCPClient(host, port);
client.addStateListener(this);
public void onStateChanged(String state, String msg)
{...}
|
Again, the callback onStateChanged (state, msg) has two string parameters state and msg, describing the state change of the client:
state |
msg
|
Description
|
TCPClient.CONNECTING
|
server's IP address:port
|
Starting connection attempt
|
TCPClient.CONNECTION_FAILED
|
server's IP address:port
|
Connection trial failed
|
TCPClient.SERVER_OCCUPIED
|
server's IP address:port
|
Connection trial failed because server is already connected
|
TCPClient.CONNECTED
|
server's IP address:port
|
Connection established
|
TCPClient.MESSAGE
|
received message
|
The server has received a message
|
TCPClient.DISCONNECTED
|
(empty)
|
Connection interrupted (aborted by client or server)
|
The call to connect() is blocking, which means that the function returns with True / true once the connection has succeeded, or with False / false after a certain timeout period (approximately 10 seconds) if the connection fails. The information about the success or failure of the connection can also be detected via the callback.
You can try out the client-server programs on the same PC by starting two TigerJython windows. In this case you choose the host address localhost. Using two different computers for the client and the server is more close to reality. They must be connected with a network cable or via wireless LAN and the link must be open for TCP/IP communication with the selected port. If the connection fails with your normal hotspot (WLAN access point), this is mostly due to firewall restrictions. In this case you can use your own router or start a mobile host spot app on your smartphone. Access of the mobile phone to the Internet is not necessary.
A First Glance: The Time-Server/Time-Client Scenario
To demonstrate how simple it is to write a client-server application using tcpcom, your first programming duty is to create a server that provides a time service. When a client logs on, it sends the current time (with date) back to the client. There are numerous such time servers on the Internet and you can be proud that you are already in a position to code such a professional server application.
In order to turn off the time server, we use a well-known trick: The main program "hangs" in a modal message dialog opened by the blocking function. When the function returns by pressing the OK or clicking the close button, the server is stopped by calling terminate(). (In Python we use the easygui module that is much simpler than Tkinter. It can be downloaded from the Internet.)
Python server:
from easygui import msgbox
from tcpcom import TCPServer
import datetime
def onStateChanged(state, msg):
print state, msg
if state == TCPServer.CONNECTED:
server.sendMessage(str(datetime.datetime.now()))
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
msgbox("Time Server running. OK to stop","Time Server")
server.terminate()
|
Python client:
from easygui import msgbox, enterbox
from tcpcom import TCPClient
def onStateChanged(state, msg):
print state, msg
if state == TCPClient.MESSAGE:
client.disconnect()
msgbox("Server reports local date/time: " + msg, title)
elif state == TCPClient.CONNECTION_FAILED:
msgbox("Server " + host + " not available", title)
title = "Time Client"
port = 5000
host= enterbox("Time Server IP Address?", title, "localhost", True)
if host != None:
client = TCPClient(host, port, stateChanged = onStateChanged)
client.connect()
|
In Java we use JOptionPane to create a modal message dialog. Unfortunately it is not so easy to catch or inhibit the close button click. So do not use it.
Java server:
import ch.aplu.tcpcom.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JOptionPane;
public class TimeServer implements TCPServerListener
{
private TCPServer server;
public TimeServer()
{
int port = 5000;
server = new TCPServer(port);
server.addTCPServerListener(this);
// Do not click the title bar's close button
JOptionPane.showMessageDialog(null, "Time Server running. OK to stop.",
"Time Server", JOptionPane.INFORMATION_MESSAGE);
server.terminate();
}
public void onStateChanged(String state, String msg)
{
System.out.println("State: " + state + "; Msg: " + msg);
if (state.equals(TCPServer.CONNECTED))
{
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
server.sendMessage(dateFormat.format(date));
}
}
public static void main(String[] args)
{
new TimeServer();
}
}
|
Execute the program locally using WebStart
Java client:
import ch.aplu.tcpcom.*;
import javax.swing.JOptionPane;
public class TimeClient implements TCPClientListener
{
private TCPClient client;
private String host;
public TimeClient()
{
int port = 5000;
host = JOptionPane.showInputDialog("IPAddress", "localhost");
if (host == null)
return;
client = new TCPClient(host, port);
client.addTCPClientListener(this);
client.connect();
}
public void onStateChanged(String state, String msg)
{
System.out.println("State: " + state + "; Msg: " + msg);
if (state.equals(TCPClient.MESSAGE))
{
client.disconnect();
JOptionPane.showMessageDialog(null,
"Server reports local date/time: " + msg,
"Time Client", JOptionPane.INFORMATION_MESSAGE);
}
else if (state.equals(TCPClient.CONNECTION_FAILED))
JOptionPane.showMessageDialog(null,
"Server " + host + " not available",
"Time Client", JOptionPane.INFORMATION_MESSAGE);
}
public static void main(String[] args)
{
new TimeClient();
}
}
|
Execute the program locally using WebStart
|
|