Lecture 8: Socket Programming Interface


The TCP Connection (Revisited)

In the last several lectures, discussion of how an application protocol operates invariably starts with the phrase:
...the client process opens a TCP connection to a server, at port number xx...
In this lecture, we examine what this really means from the perspective of a programmer writing a TCP-based client or (more complex) a server.

We will use the Java programming language for our examples. Previously we used C, the standard system-level programming language on Unix systems. You should be aware that the concepts we will introduce in today's lecture were originally (like most Internet protocols) developed in a Unix environment, and still retain some of the flavour of Unix -- that is, when you occasionally wonder why the designers took a particular approach, the answer is quite likely "...that's how it's done in Unix..


The Socket Abstraction

The socket was introduced in BSD Unix (in the early 1980s) as a way of extending the Unix file I/O model to handle network communications.

Sockets (and the TCP software to which they form the interface) are implemented in all modern operating systems as system calls; that is, they form part of the operating system kernel. In modern object-oriented languages such as Java (and others), a socket class is implemented "on top of" these basic system-level services.

TCP sockets in Java are different for server and client processes. For a client process, a socket is created as follows:

Socket myclientsocket = new Socket("ironbark", 79);
This creates a new Socket object which is associated with a TCP (ie, reliable) connection to port 79 on ironbark. The newly created socket can be used as both an input and output stream, see next slide.


An Example Client.

Port 79 is the well-known port for the finger application protocol. If we send a text string containing a valid username on ironbark, we should receive some interesting facts about the user. Here's the Java code:
import java.io.*;
import java.net.*;
class FingerClient {
    public static void main(String argv[]) throws Exception
    {
        String user = "pscott";
        String response;
        Socket clientSock = new Socket("ironbark", 79);
        DataOutputStream toserver =
            new DataOutputStream(
                clientSock.getOutputStream());
        DataInputStream serverReply =
            new DataInputStream(
                clientSock.getInputStream());
        toserver.writeBytes(user + "\n");
        response = serverReply.readLine();
        System.out.println("Finger Reply: " + response);
        clientSock.close();
    }
}
Note that this program only outputs the first line of the data returned from the server. Important points: we first create a "connected" socket, then we send a line of text, and finally we receive a line of text in response -- the classic Internet application protocol scenario.


Other Socket Operations

Java provides a few other entertaining socket operations, apart from reading from (via getInputStream(), and writing to (via getOutputStream()), the connected socket:
getInetAddress()

Returns the IP address of the system that this socket is connected to.

getPort()

Returns the port number that the socket is connected to on the remote system -- basically you'd hope that this is the same as you specified at "socket creation" time...:-)

getLocalPort()

Returns the port number on the local system. For a client socket, this will be the random (?) port number allocated when the initial TCP connection was established. Usually this isn't of much interest.


Server Sockets in Java

A server is potentially far more complex than a client, although Java makes it easy to code a simple example:
import java.io.*;
import java.net.*;
class SillyServer {
    public static void main(String argv[]) throws Exception
    {
        String incoming;
        String outgoing;
        ServerSocket servSock = new ServerSocket(7277);
        
        while(true) {
            Socket connectedSocket = servSock.accept();
            DataInputStream fromClient =
                new DataInputStream(
                    connectedSocket.getInputStream());
            DataOutputStream toClient =
                new DataOutputStream(
                    connectedSocket.getOutputStream());
            incoming = fromClient.readLine();
            outgoing = incoming.toUpperCase() + "\n";
            toClient.writeBytes(outgoing);
            connectedSocket.close();
        }
    }
}

Multi-Threaded Servers

The server in the previous slide was single threaded, meaning that only one client could be connected to it at any instant. This is a serious restriction in a "Real World(tm)" environment.

Whilst the Java code for a multi-threaded server is too complex to include into the lecture, the basic concept is that the accept() method returns a new, connected, socket. A program can start up a new thread of execution which handles communications using the connected socket, whilst allowing the "main program" to again go back to "accepting connections".

Conceptual Conundrum -- how can the newly-created program thread perform communications on (eg) port 7277 at the same time as the server is "waiting for connections" at the same port number?

The answer to this is very subtle. In fact, an active TCP connection is characterised by a 4-tuple, consisting of the IP addresses of each of the computers involved, plus both of the port numbers (both client and server) in use. Because this 4-tuple is unique for every TCP connection (Exercise: why?), it's possible to have many server threads serving client requests simultaneously (or, more correctly, concurrently)


Sockets in Other Languages

The Perl programming language has become very popular in recent years. It's interesting to compare a Perl client program with the Java program from earlier:
#!/usr/local/bin/perl
use IO::Socket::INET;

$sock = IO::Socket::INET->new(PeerAddr => 'ironbark',
                              PeerPort => 'finger(79)',
                              Proto    => 'tcp');
print $sock "pscott\n";

while(<$sock>) {
    print;
}
On the other hand, a C program to perform the same function (actually it does a little more, but we won't quibble...) is about 70 lines, not including comments!


Philosophical Digression

In the trivial Java client example given above, we sent a single line of text to the server, and our client only handled the first line of text returned -- if there was more than one line, we ignored it. This highlights some important aspects of the "lines of text"-based application protocols which we have considered:

La Trobe Uni Logo


Copyright 2004 by Philip Scott, La Trobe University.
Valid HTML 3.2!