...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..
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:
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.Socket myclientsocket = new Socket("ironbark", 79);
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:
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.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(); } }
getInputStream()
,
and writing to (via getOutputStream()
), the connected
socket:
getInetAddress()
getPort()
getLocalPort()
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(); } } }
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)
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!#!/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; }
Content-length:
" header.
Later we will look at application (and other) protocols which take a different approach, whereby data is always sent in a highly structured format, making parsing either not necessary, or much simplified. As we shall see, the tradeoff in these is to lose the "human-readable" exchange of data which makes the typical Internet application protocols so conceptually simple.