This spring, I follow a course in mobile and distributed systems at the IT University of Copenhagen. During the course, I am going to post some of my homework to this blog.
The course is part of the The Master of Science in Software Development and Technology programme, and focuses on distributed programming, concurrency, Web Services, network communications and so on. And althougth most of it actual code will be written i Java, I figure that most of it stuff is highly relevant in .NET as well.
One of the first assignments is to write three echo services, one using TCP, another using UDP and a third using a JAX Web Service. The service should be able to handle multiple concurrent clients. Heres is my shot at a TCP echo server.
Echo server using TCP
First of all I encapsulated the client connection in a classed called TCPConnection
. The naming is probably a bit off: Good naming should hide internal implementation details, and hence the class should be refactored to something like ClientConnection
, I guess.
At last, notice that the class contains not exception handling, and merely forwards the exceptions to a more appropriate place(s) for exception handling. This allowes the class to be used in different environments:
package echoservice.model; import java.net.*; import java.io.*; public class TcpConnection { Socket socket = null; InputStream inputStream = null; OutputStream outputStream = null; PrintWriter outputWriter = null; BufferedReader inputReader = null; String clientAddress = null; int port; public TcpConnection(Socket socket) throws IOException { this.socket = socket; clientAddress = String.format("%s:%d", socket.getInetAddress().getHostAddress(), socket.getPort()); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); outputWriter = new PrintWriter(outputStream, true); inputReader = new BufferedReader( new InputStreamReader(inputStream)); } public String readLine() throws IOException { return inputReader.readLine(); } public void println(String x) { outputWriter.println(x); } public String getClientAddress() { return clientAddress; } public void disconnect() throws IOException { outputWriter.flush(); // this will cause output stream to close as well outputWriter.close(); // this will cause input stream to close as well inputReader.close(); if (socket != null) socket.close(); } }
Next I created a thread to do the actual message echoing on a TCPConnection. The TCPConnectionThread takes a TCPConnection in the constructor, and extends the java.lang.Thread class.
This allows me to excuting it as a new thread. This class includes exception handling as well, and write exception messages to the console.
package echoservice.model; import java.io.*; public class TcpConnectionThread extends Thread { TcpConnection connection = null; public TcpConnectionThread(TcpConnection connection) { this.connection = connection; } public void run() { System.out.println( String.format("Connected to: %s", connection.getClientAddress())); String input = null; while(true) { System.out.println( String.format("Waiting for: %s", connection.getClientAddress())); try { input = connection.readLine(); } catch (IOException ex) { System.out.println( String.format("Error reading from %s: %s", connection.getClientAddress(), ex.getMessage())); break; } if (input == null || input.isEmpty()) break; System.out.println( String.format("Received: %s", input)); connection.println(input); System.out.println(String.format("Sent: %s", input)); } try { connection.disconnect(); System.out.println( String.format("Disconnected from: %s", connection.getClientAddress())); } catch (IOException ex) { System.out.println( String.format("Error disconnecting from %s: %s", connection.getClientAddress(), ex.getMessage())); } } }
Finally we need the server class. Whereas there can exists multiple parallel instances of the TCPConnectionThread, each handling a single client, there exists only one server instance. The server instance listens to a port, and when a clients connects, creates a TCPConnection and pass it on to a new instance of TCPConnectionThread. The server is started by the calling its listen()
method, which causes the server to block while listening and starting threads. The only way to stop the server, is to shut it down.
package echoservice.model; import java.io.*; import java.net.*; public class TcpHandler { int port; ServerSocket serverSocket = null; public TcpHandler(int port) throws IOException { this.port = port; serverSocket = new ServerSocket(port); } public TcpConnection listen() throws IOException { while (true) { new TcpConnectionThread( new TcpConnection( serverSocket.accept())).start(); } } public int getPort() { return port; } }
The UPD service it actually quite different, because UPD is non connection based. This means that my server handles incoming datagrams from all clients in a single worker thread, and echos to the socket address where the datagram came from.
In regards to concurrency, this actual makes the server somewhat simpler. On the other hand, the low level nature of UDP makes the actual reading and writing of datagrams a bit messy.
My Web Service based service is also non connection based as well. as Web Services should be stateless if possible. Well, thats all – until next Tuesday, then is back to school again!