By dave | February 5, 2016

Following on from the article Introduction to data communications we will now go into more depth, by looking at an example of stream based communication. We will use sockets for the example; mainly because sockets are available out of the box on any computer connected to the internet.

A socket is a stream based connection between a server and a client. Recall from the prior page this means that data is received in the order it was sent. Usually, the server and client are two processes, that may run on the same computer or different computers. It makes no difference what operating system they run, or what hardware they run on as long as it supports sockets.

Server listens and client connects.

When we create the server side of the socket, we specify a port, this is like a unique postbox on the server computer; where a client can obtain a connection to a service; importantly it is not the actual connection. When we request to connect with the server it allocates another port for the communication. Accept can be called over and over again on the server socket, so that our process could handle many clients. In our example we only allow one connection at once.

Ports allow one computer to send and receive data from more than one place at once. In the diagram below we show two ports open on computer 1, on the client side, it does not matter what port number is assigned for this port, as it will be passed to the server in the initiation (connection phase). At this point the server process will also select a port to be used for the rest of the communication (as we discussed before the accept port only establishes connection).

data comms - socket connection diagram

Above: graphical example of two ongoing socket connections.

It should be noted that we’ve skipped a lot of important topics in producing this simple example, but they will be covered later on, in more detail examples. Think of this as the simplest code to communicate between two processes.

Building a simple tele-type interface.

For the next example we will build a simple teletype system. This teletype system will echo back whatever we type; it is about the simplest program we can build with sockets. In order to run these two examples, create a project in your favourite Java IDE and run the server side of the project, followed by the client side. Once both sides are successfully running, you’ll see the following lines logged:

Server log

Feb 05, 2016 4:13:27 PM com.thecoderscorner.example.stream.TeletypeServer start
INFO: Listening on 5000
Feb 05, 2016 4:13:41 PM com.thecoderscorner.example.stream.TeletypeServer start
INFO: New socket connection to: /127.0.0.1:61405
Feb 05, 2016 4:13:56 PM com.thecoderscorner.example.stream.TeletypeServer processSocketConnection
INFO: Received data from client: hello world

Client log

Feb 05, 2016 4:13:41 PM com.thecoderscorner.example.stream.TeletypeClient start
INFO: Connected to localhost/127.0.0.1:5000
hello world
Feb 05, 2016 4:13:56 PM com.thecoderscorner.example.stream.TeletypeClient fromConsoleToSocket
INFO: Written to socket: hello world
Feb 05, 2016 4:13:56 PM com.thecoderscorner.example.stream.TeletypeClient start
INFO: From Server:HELLO WORLD

At this point anything typed into the client will be sent over the socket to the server, the server will echo it back to the client in upper case and it will appear in the output area. You don’t get simpler than this.

Looking at the server code, once the server socket is created, we then call accept to await a connection. When accept returns we are given a socket and it’s this stream we communicate on. If we were writing a large scale server, then we may wish to keep accepting connections, and service them in a different thread. However, for this example we’ll only accept one connection at once.

From the client perspective we need two threads, one to read from the socket and print the received data to the console. The other thread will read from the console and send those bytes over the socket.

To run the system you must first start the server, then run the client. You may need to change the port number on your system, if it cannot be bound to the default port number. This tutorial needs Java 8 or later. You can download the examples directly from github where you’ll find these classes in the stream package.

Server example code

package com.thecoderscorner.example.stream;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This is the server side of the teletype process, it reads lines of input
 * from the socket and echos it back to the client in upper case. This is
 * the simplest form of communication possible over a socket.
 *
 * Designed for use with the data communications section at https://www.thecoderscorner.com/data-comms
 */
public class TeletypeServer {
    private final Logger logger = Logger.getLogger("SERVER");
    public static final int SERVER_PORT = 5000;

    public static void main(String[] args) throws IOException {
        TeletypeServer server = new TeletypeServer();
        server.start();
    }

    private void start() throws IOException {
        // we firstly create a server socket on a spare port, this allows
        // our client to connect
        ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
        logger.info("Listening on " + SERVER_PORT);

        // while our process is running
        while(!Thread.currentThread().isInterrupted()) {

            try {
                // attempt to accept a connection on our server port.
                Socket socket = serverSocket.accept();

                // as per the documentation above, here we only process
                // one connection at a time. IE: we don't call accept
                // again until the current socket is disconnected.
                logger.info("New socket connection to: " + socket.getRemoteSocketAddress());
                processSocketConnection(socket);

            }
            catch(IOException e) {
                logger.log(Level.SEVERE, "Exception in server", e);
            }
        }
        serverSocket.close();
    }

    private void processSocketConnection(Socket socket) throws IOException {
        // memory buffer for the socket data.
        byte[] bytes = new byte[1024];
        int len;

        // while the socket has more data (ie: not closed)
        while ((len = socket.getInputStream().read(bytes)) > 0) {

            // convert the bytes to a string.
            String toEcho = new String(bytes, 0, len);
            logger.info("Received data from client: " + toEcho);

            // upper case it and send it back to the client.
            socket.getOutputStream().write(toEcho.toUpperCase().getBytes());
        }
        socket.close();
    }
}<br />

Client example code

package com.thecoderscorner.example.stream;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class makes up the client side of our teletype, by reading
 * input from stdin and sending it over the socket. Then another
 * thread reads the resulting text that is echoed back by the server
 * and prints it to the console.
 *
 * Designed for use with the data communications section at https://www.thecoderscorner.com/data-comms
 */
public class TeletypeClient {
    private final Logger logger = Logger.getLogger("CLIENT");
    private Socket socket;

    /**
     * Create the teletype client when started.
     * @param args unused.
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        TeletypeClient client = new TeletypeClient();
        client.start();
    }

    /**
     * We start the teletype client that will both read from stdin
     * and send to the server
     * @throws IOException
     */
    private void start() throws IOException {

        // Create a socket that will connect to the server and request
        // a new connection.
        socket = new Socket("localhost", TeletypeServer.SERVER_PORT);
        logger.info("Connected to " + socket.getRemoteSocketAddress());

        // we now create another thread that will read from the console
        // and write the captured text to the socket.
        new Thread(this::fromConsoleToSocket).start();

        // and on the main thread we read from the socket and write
        // the contents to the console.
        byte[] bytes = new byte[1024];
        int len;
        while((len = socket.getInputStream().read(bytes)) > 0) {
            String echoStr = new String(bytes, 0, len);
            logger.info("From Server:" + echoStr);
        }
    }

    /**
     * This method reads from stdin (console) and writes it to the socket.
     */
    public void fromConsoleToSocket() {
        // we need to read lines from stdin, buffered reader makes life easy.
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        // while we still have the socket
        while(!socket.isClosed()) {
            try {
                // read a line from stdin.
                String toSend = reader.readLine();

                // and write it to the socket.
                socket.getOutputStream().write(toSend.getBytes());
                logger.info("Written to socket: " + toSend);
            } catch (IOException e) {
                logger.log(Level.SEVERE, "Exception writing to socket", e);
            }
        }
    }
}

Following on from this article is: Message based communication using sockets.

Other pages within this category

comments powered by Disqus

We use cookies to analyse traffic and to personalise content. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Please see our privacy policy should you need more information or wish to adjust your settings.

Send a message
X

This message will be securely transmitted to Nutricherry LTD servers.