You are on page 1of 16

Java network programming

Bob Dickerson
October 2005
1 Sockets
1.1 Socket streams and datagrams
There are 2 forms of transport level network interprocess connection with the TCP/IP family of protocols:
TCP a bi-directional stream connection. The stream is reliable which means the underlying network
level requires acknowledgement of each packet sent in the stream, if any are lost then they are re-
transmitted transparently to the process using the stream.
UDP a connectionless single message, or datagram. There is no guarantee of delivery of a UDP datagram
(although in practice nearly all packets get through).
1.2 Java sockets API
A TCP connection on the internet is a bi-directional link between two running programs (not computers).
The connection is just a stream of characters (in Java bytes) wrtten by one program and read by another. It
is the standard service made available by operating systems to application programs, the interface is known
as the BSD socket interface. Most internet applications are programs that use TCP connections, they send
their own protocol messages to each other through the TCP connections.
No matter whether the network application is client-server or peer-to-peer whenever one program must
contact another there is asymmetry in the use of TCP connections One will wait to accept a connection and
another must connect to it.
Sockets are available in Java through the java.net.* package. There are two main classes: Socket for
connected sockets, and ServerSocket for listening sockets. The two different types of socket reect the
asymmetry of connections.
ServerSocket this is used by the receiver of connections, this is often a server program(although the
idea of server is more about the application than the way connections are established). A Server-
Socket is used to advertise a service, it is used by the program to inform the operating system that
it will deal with incoming connection requests for a specied port, The receiver program waits for
incoming connections by calling the accept function in the server socket,
Socket this is a connection it is an object containing streams that convey bytes between programs.
The connecting program will create a new Socket and give it an address and a port, this will cause
the operating system to contact the remote system to see if the connection can be established. If
the connection is accepted the socket can be used to send bytes to and receive bytes from the other
program. If the connection is successful the waiting receiver returns from the accept function with
a new connected socket.
The following picture 1 shows this. Im sorry its not a very good diagram but it might help. The sequence
of events in gure 1 is:
1. A receiver program (server) creates a server socket and gives it a port number, it will receive
incoming connections to that port:
ServerSocket ss=new ServerSocket(port);
2. it then waits for an incoming connection request by calling:
conn = ss.accept();
nothing happens for a bit. . .
3. in a distant galaxy far far away a client program creates a socket:
1
2 VERY SIMPLE EXAMPLE 2
conn=new Socket(IP,port)
server client
wakes up
ss=new ServerSocket(port)
conn=ss.accept()
ins=conn.getInputStream()
ins.read(b)
out.write(b)
outs=conn.getOutputStream()
1
2
3
4
6
time
5
Figure 1: Java socket use
Socket conn = new Socket(IP,port)
where IP is the IP address or name of the receivers computer and port is the number used when the
ServerSocket was created. This is a connection request.
If the connection request succeeds (this involves the operating systems communicating) then the
client returns with a connected socket which it can use. The receiver also returns from the accept
function and it has a newly generated connected socket of its own,
4. they are now connected. However Java does not allow direct reading and writing of the socket (unlike
Unix system calls), instead it remembers it has streams it uses for le I/O so it requires that the pro-
grams extract Streams from their sockets. In this example, the receiver will only read the connection
and the client will only write so one gets an InputStream and the other an OutputStream. The
receiver does:
InputStream ins=conn.getInputStream();
and the client does:
OutputStream outs=conn.getOutputStream();
5. the client writes (in this case),
6. and the server reads (in this case).
Since these streams are exactly the same as the streams returned when you open les they can be used
in the same way with read and write. Except reading and writeing these streams will receive and
send data to the other program to which the socket is connected.
2 Very simple example
This section includes two very trivial programs, one of which, ReceiveBytesTCP0.java creates a server
socket and then waits for connections with accept. When it gets a connection it reads everything sent to it
and writes it to the standard output System.out (usually the console). When the connection nishes (the
remote user died?) the read returns a byte count of zero and the loop stops. However instead of exiting it
goes round an outer loop and tries to accept a new connection request.
import java.io.*;
import java.net.*;
public class ReceiveBytesTCP0 {
public static void main(String[] args) throws Exception {
if( args.length != 1 ) {
System.out.println("Usage: ReceiveBytesTCP0 port");
System.exit(1);
}
ServerSocket serverSock =
2 VERY SIMPLE EXAMPLE 3
new ServerSocket(Integer.parseInt(args[0]) );
while(true) {
Socket conn = serverSock.accept();
System.out.println("Got connection from "
+ conn.getInetAddress() );
InputStream ins = conn.getInputStream();
byte buf[] = new byte[1024];
int rc = ins.read(buf,0,1024);
while (rc > 0) {
System.out.write(buf,0,rc);
rc = ins.read(buf,0,1024);
}
conn.close();
System.out.println("connection closed");
}
}
}
The program is in the le ReceiveBytesTCP0.java. Other things to note:
the port number is supplied as a string from the command line. It must be converted to an integer
before it can be used
When it gets a connection it prints out the address of the remote systemusing conn.getInetAddress(),
look at an example of simple le I/O to remind yourself how read and write work.
Nowthe connecting programthat will contact the receiver and (in this case) send it all the characters
typed at its console. It is called SendBytesTCP0.java, It requires two command line arguments to use to
make the connection: the IP address or name of the receivers computer and the port.
import java.io.*;
import java.net.*;
public class SendBytesTCP0 {
public static void main(String[] args) throws Exception {
if( args.length != 2 ) {
System.out.println("Usage: SendBytesTCP servername port");
System.exit(1);
}
Socket sock=new Socket(args[0], Integer.parseInt(args[1]));
OutputStream outs=sock.getOutputStream();
byte buf[] = new byte[1024];
int rc = System.in.read(buf,0,1024);
while (rc > 0) {
outs.write(buf,0,rc);
rc = System.in.read(buf,0,1024);
}
sock.close();
}
}
The program is in the le SendBytesTCP0.java. Note: this program nishes if the read from the
standard input System.in (usually the keyboard) returns 0 bytes, which means end-of-le, this can be
done at the keyboard by typing control-d (if you are using a Posix system).
2.0.1 Why cant this program be changed to send and receive?
Well it can. . . but not simply. If the program is going to send then it must try to read from the keyboard and
this will cause it to block (wait), however it must also be prepared to read incoming messages from the
network connection, once again it must block. How can it wait in two places at the same time? It has a
single thread of control, if it waits anywhere nothing else can happen. Section 4 on threads explains one
way of solving this problem.
2 VERY SIMPLE EXAMPLE 4
2.1 Reading and writing Strings not bytes
The Java I/O API includes lots of classes to adapt and change streams. One of the extra classes is a
BufferedReader that allows the program to read lines as Strings. In many cases it is important for
real applications to read separate lines rather than arbitrary blocks of bytes. It is also possible to have a
PrintWriter that allows strings to be written. The following program is a variant of the sender to use
strings. The receiver can be adapted in a similar way, however the original receiver can still communicate
with the new sender because although it uses strings in the program all trafc over the network is still a
sequence of bytes, the BufferedReader converts the bytes on the way in, and the PrinterWriter converts
the strings on the way out.
import java.io.*;
import java.net.*;
public class SendTCP0 {
public static void main(String[] args) throws Exception {
if( args.length != 2 ) {
System.out.println("Usage: SendTCP servername port");
System.exit(1);
}
Socket sock=new Socket(args[0], Integer.parseInt(args[1]));
PrintWriter out=new PrintWriter(sock.getOutputStream(),true);
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String userInput = stdIn.readLine();
while (userInput != null) {
out.println(userInput);
userInput = stdIn.readLine();
}
sock.close();
}
}
The program is in the le SendTCP0.java.
2.1.1 Exercise
Compile and run the example:
2.2 Catching an exception
This is yet another variant of the sender program. This time it has been altered to catch the UnknownHostException
exception. This is important because the most likely way for the connection to fail is that: the address is
wrong, the port is wrong, or the receiver isnt actually running. In all these cases the connection will fail to
be established, this is detected and reported when the sender (client) executes:
Socket conn = new Socket(IP,port)
the exception thrown can be caught and the use can be informed. There were also other exceptions that
were ignored in the simpler versions the commonest is IOException which occurs if there are any network
communication problems.
import java.io.*;
import java.net.*;
public class SendTCP {
public static void main(String[] args) {
if( args.length != 2 ) {
System.out.println("Usage: SendTCP servername port");
System.exit(1);
}
try {
3 A CLIENT EXAMPLE 5
Socket sock = new Socket(args[0], Integer.parseInt(args[1]));
PrintWriter out=new PrintWriter(sock.getOutputStream(),true);
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String userInput = stdIn.readLine();
while (userInput != null) {
out.println(userInput);
userInput = stdIn.readLine();
}
out.close();
sock.close();
} catch (UnknownHostException e) {
System.err.println("Dont know about host: " + args[0]);
System.exit(1);
} catch (IOException e) {
System.err.println("IO error"); System.exit(1);
}
}
}
The program is in the le SendTCP.java. There are probably many other improvements that can be made
to these programs but theres not much point because they are not very useful.
3 A client example
The following example just illustrates a simple client program, it takes as arguments: an internet address
and a WWW page name, it builds an HTTP request and sends it to a remote web server, it then reads and
prints everything the server sends back, this will include all the HTTP protocol response lines aswell as the
le (page). There is no need for a matching server to be written because there are lots out there already for
it to talk to.
This is a minimal version with no the checking of arguments and exception handling. An extended
version is shown afterwards.
import java.io.*;
import java.net.*;
public class HTTPGet0 {
public static void main(String args[]) throws Exception {
int rc, port = Integer.parseInt(args[2]);
byte buffer[] = new byte[8192];
Socket socket = new Socket(args[0], port);
OutputStream outs = socket.getOutputStream();
InputStream ins = socket.getInputStream();
String request = "GET " + args[1] + " HTTP/1.1\r\n"
+ "Host: " + args[0] + "\r\n"
+ "Connection: Close\r\n\r\n";
outs.write(request.getBytes());
rc = ins.read(buffer,0,8192);
while (rc > 0) {
System.out.write(buffer,0,rc);
rc = ins.read(buffer,0,8192);
}
outs.close(); ins.close(); socket.close();
}
}
The programis in the le HTTPGet0.java. This programwill act as a dumb client. It will send a request to
a remote http server. Note that this program needs three command line arguments, the remote host address,
the port and the le (page) to retrieve preceded with a /. To compile and run the program:
3 A CLIENT EXAMPLE 6
sally(373)$ javac HTTPGet2.java
sally(374)$ java HTTPGet2 plink.feis.herts.ac.uk /tiny.html
HTTP/1.1 200 OK
Date: Sun, 16 Mar 2003 23:32:10 GMT
Server: Apache/1.3.26 (Unix) Debian GNU/Linux
Last-Modified: Wed, 08 May 2002 23:45:10 GMT
Accept-Ranges: bytes
Content-Length: 492
Content-Type: text/html; charset=iso-8859-1
Connection: close
<H1> Example Page </H1>
This is the first paragraph, it is terminated by a
...
which will get tiny.html from plink.feis.herts.ac.uk. Notes:
rst attempt to connect to the server by creating a new socket using the remote system name (or
number) and the port:
socket = new Socket(args[0], port);
if this fails an exception will be raised but not caught so the program will fail,
now extract the input and output streams:
toServer = socket.getOutputStream();
fromServer = socket.getInputStream();
now build a full HTTP le request as a string in request,
and send it to the server:
toServer.write(request.getBytes());
note that since it is a stream we use write which requires an array of bytes, getBytes will get such
an array out of the string request. Now the message is sent to the server,
if the server exists and if it reads the request, and if it thinks our request well-formed and if it has
such a le then it will send it down the same connected socket. We must read the socket to get the
returning le:
rc = fromServer.read(buffer,0,8192);
read puts the characters read into a pre-allocated array of bytes, here called buffer. The return
result, put in rc, is the number of characters actually put in buffer. The client cannot know how big
the le is (if its an MPEG video it might be megabytes), so it reads in chunks of 8k, that is why
there is a loop, that reads and then prints to System.out,
when we can read no more (rc > 0 is not true) we close everything and nish.
3.1 A fuller version
This is a slightly more deluxe version of the program. It has exception handling and better checking of
command line arguments. However the basic functionality is very similar
import java.io.*;
import java.net.*;
public class HTTPGet2 {
public static void main(String[] args) {
final int BUFSIZ=8192;
int rc, port = 0;
byte buffer[] = new byte[BUFSIZ];
if( args.length == 0 ) {
System.out.println("Usage: HTTPGet2 server file [port]");
System.exit(1);
3 A CLIENT EXAMPLE 7
} else if( args.length == 3 ) {
port = Integer.parseInt(args[2]);
} else {
port = 80;
}
try {
Socket socket = new Socket(args[0], port);
OutputStream toServer = socket.getOutputStream();
InputStream fromServer = socket.getInputStream();
String request = "GET " + args[1] + " HTTP/1.1\r\n"
+ "Host: " + args[0] + "\r\n"
+ "Connection: Close\r\n\r\n";
toServer.write(request.getBytes());
rc = fromServer.read(buffer,0,BUFSIZ);
while (rc > 0) {
System.out.write(buffer,0,rc);
rc = fromServer.read(buffer,0,BUFSIZ);
}
toServer.close();
fromServer.close();
socket.close();
} catch (UnknownHostException e) {
System.err.println("Cant find: " + args[0]);
System.exit(1);
} catch (IOException e) {
System.err.println("IO error");
System.exit(1);
}
}
}
The program is in the le HTTPGet2.java. Notes:
rst it checks the command line arguments, if there is no port number provided the program will use
80,
all the code to open the connection and read and write the streams might produce horrible exceptions
so the body of the program is surrounded by try{..}catch{..},
3.2 Client server example echo
This example consists of a server and a client. They do very little except show how a stream connection
is set up. The server awaits (accept) a connection, reads from the client and immediately sends whatever
it read back again. When the connection from a client is closed (a rc==0 returned from read) the server
loops to accept the next connection from another client. The client makes a connection and then loops
each time: reading from the user, writing this text to the server, reading the servers response (which should
be the same) and then printing it. The server:
import java.io.*;
import java.net.*;
public class EchoServer0 {
public static void main(String[] args) throws Exception {
InputStream ins;
OutputStream outs;
int rc, echoPort = Integer.parseInt(args[0]);
byte buffer[] = new byte[4096];
ServerSocket serverSock = new ServerSocket(echoPort, 10);
Socket connSock;
while(true) {
connSock = serverSock.accept();
outs = connSock.getOutputStream();
ins = connSock.getInputStream();
3 A CLIENT EXAMPLE 8
rc = ins.read(buffer,0,4096);
while (rc > 0) {
outs.write(buffer,0,rc);
rc = ins.read(buffer,0,4096);
}
outs.close(); connSock.close();
}
}
}
The program is in the le EchoServer0.java.
This is a very cut-down program, there is no error checking or exception handling,
notice that the server loops forever:
while(true) {
...
nearly all servers are like this, deal with one request and loop to accept the next,
this is a server so it must create a ServerSocket bound to a port number. The port number to use is
provided as an argument. It then waits by calling accept,
this network program reads and writes bytes into a buffer not single characters or lines. However it
is worth noting how much will be read, consider:
rc = ins.read(buffer,0,4096);
with a network connection it will read all the bytes available into buffer starting at offset 0 up to a
maximum of 4096. If there are no bytes it will block, if there are more than 4096 bytes it will read
just 4096 this time. The number of bytes read is returned and assigned to rc,
it then loops reading from the client and writing back again. When it gets rc==0 from read (which
would be end of le for a le) it means the client closed the connection.
Now the client, once again this is a cutdown, non-error checking one:
public class EchoClient0 {
public static void main(String[] args)throws Exception{
Socket sock=new Socket(args[0],Integer.parseInt(args[1]));
OutputStream outs=sock.getOutputStream();
InputStream ins=sock.getInputStream();
byte buffer[]=new byte[4096];
int rc;
rc = System.in.read(buffer,0,4096); // read from user
while (rc > 0) {
outs.write(buffer,0,rc); // write to net
rc = ins.read(buffer,0,4096); // read from net
System.out.write("got: ".getBytes()); // write to user
System.out.write(buffer,0,rc); // write to user
rc = System.in.read(buffer,0,4096); // read from user
}
outs.close(); ins.close(); sock.close();
}
}
The program is in the le EchoClient0.java. To test the client and server programs: compile them both,
run the server with an arbitrary port number:
tink(257)$ java EchoServer0 3333
it should just wait and go nothing, it is not waiting for keyboard input, it is waiting for network messages.
Then in another xterm run the client:
plink(258)$ java EchoClient0 tink 3333
hello
echo: hello
...
4 THREADS 9
the line hello is read from the user, sent to the server returned by it, read from the socket by the client
and then printed echo: hello. Unlike the server the client only deals with one session, it only has one
loop to read and echo, when the user nishes (by typing control-d ^d on Unix) the loop nishes and the
program nishes.
The echo service isnt obviously very useful, however it is a real internet standard protocol, its main
use is for testing. Also it is a useful example for introducing simple networking concepts.
4 Threads
Threads allow parts of a program or MIDlet to execute in parallel. With two threads it allows one thread
to wait for one event and the second thread to wait for another event, this means a program can watch for
more than one event at the same time, why is this useful in networking?
A thread is a sequence of statement execution through a program, it is like a virtual processor with
a program counter and a stack of saved returned addresses. Every program has at least one thread, it is the
execution path through the program. It is possible to have more than one thread within the same program
being run. That means that different bits of the program are being executed in parallel, in other words
different bits of code are being executed at the same time (at least logically although not absolutely simul-
taneously unless the computer has more than one processor). The Java virtual machine and the underlying
operating system switch between all the threads and allow each to execute, switching to another one when
the running one blocks (ie. doesnt want to keep executing because it is waiting for some event).
4.1 The need for multiple threads
There are many reasons for using multiple threads but the principle underlying reason is that the program
needs to wait for more than thing at a time. There will be three examples considered:
1. The rst example is a server that needs to deal with more than one client transaction at a time,
2. the second example is the need for a program to send to and receive the network at the same time,
3. and the nal one is the problem of receiving from the network and awaiting GUI commands in one
program.
4.1.1 A server dealing with multiple clients
Consider the simple (stupid?) echo server EchoServer0.java, it creates a ServerSocket and awaits a
connection from a client, it then enters a loop reading everything from the client and sending it back. While
it is looping dealing with one client it cannot be executing accept and getting other connections. Does
this matter? Well for the echo server probably not BUT it is the same problem for other servers, all web
servers work in the same way, suppose that a client has a slow internet connection, the web server must read
their request and write the required le (maybe a large MP3) to the slow connection, all this time no other
browser requests can be accepted.
The solution is to create a separate thread for each client request, each thread then reads and writes its
own socket connection to its single client. When one thread blocks to wait for input from its client it
doesnt affect the other threads.
4.1.2 Reading and writing asynchronously
Consider the rst simple pair of programs SendBytesTCP0.java and ReceiveBytesTCP0.java, why
shouldnt they be combined into a single version that could enable a proper dialogue with another such
program (a bit like a very simple messenger program)? Do do this there would need to be two threads
because there would be a need to wait in two places: one thread would wait for user input and then write
it to the network, and the other thread would be waiting for bytes arriving from the network and writing
them to the console.
4.2 Threads in Java
Most languages and operating systems provide threads, unfortunately each does it in different way. Java
makes it even worse by providing two ways to do it (why does Java always make life so hard by being so
complicated and having a thousand different ways to do something???).
4 THREADS 10
4.2.1 By inheritance
One way is to write a new class that inherits from, or extends, the class Thread, in other words write
your own thread class.
class MyThread extends Thread {
...
public MyThread(...) { ... }
public void run() {
...
}
...
}
...
...
MyThread mythrd = new MyThread(...);
mythrd.start();
...
Note:
every thread must provide public void run(), this is the code that will be executed when the thread
starts,
when a thread is created with new it doesnt start immediately, the thread is a class type so the
constructor is executed in order to initialise the object but run doesnt start
to start a thread the code that created it must then call start, that causes the Java virtual machine to
start the run() function in parallel.
4.2.2 By implementing Runnable
Another way to create a thread to execute a separate body of code is to write a class that implements the
interface Runnable, which really means it must provide a public void run() function. This in itself is
not a thread, a plain Thread must then be created separately and given an object of the Runnable to run.
Here is the skeletal code:
class MyRun implements Runnable {
...
public MyRun(...) { ... }
public void run() {
...
}
...
}
...
...
MyRun runObj = new MyRun(...);
Thread thrd = new Thread(runObj);
thrd.start();
...
Note:
any class implementing Runnable must provide public void run(), this is the code that will be
executed when the thread starts,
a runnable object is created, in the example above it is runObj, it is not a thread but it has a run
function that could be used by a thread,
a Thread is created, in this case thrd, it is a plain, standard, un-extended thread. It is given the
runnable object as an argument to its constructor, this means that the separately executable thread
will execute the run function in runObj,
but runObj cannot be scheduled for execution (even though it provides) the instructions, it is the
thread that must be started: thrd.start().
4 THREADS 11
4.2.3 What is the difference?
Very little. Maybe the use of inheritance (extending) is a bit simpler. On the other hand because a class
can only extend at most one other class you might implement Runnable so the class can extend some other
class.
4.2.4 A two thread program (not doing networking)
Here is a very simple example that declares one thread class, then creates and starts two thread objects:
class Loopy extends Thread {
String message;
Loopy(String mess) {
message = mess;
}
public void run() {
while(true) {
System.out.println(message);
}
}
}
public class Threads0 {
public static void main(String[] args) {
Thread thread1 = new Loopy("One ");
Thread thread2 = new Loopy(" Two");
thread1.start();
thread2.start();
}
}
The program is in the le Threads0.java. When the threads are started they execute their run routine
for ever repeatedly printing out their message. If this is compiled and run it can produce almost any output
sequences deoending on how the threads are scheduled, which means how they are allocated a share of the
CPU time. Here is part of one sequence:
Two
Two
Two
Two
One
Two
One
Two
One
4.3 A concurrent echo server
If a server has to deal with a long transaction for a client, involving lots of waits for reading and writing les
and sockets, it will be unable to accept new requests. One simple solution is to change the server so that
after the accept it creates a child thread. This new thread uses the new connected socket to service the
clients request, and then dies. The parent thread goes back to accept to await another connection. This is
called a concurrent server.
import java.io.*;
import java.net.*;
class ServiceThread extends Thread {
Socket conn=null;
public ServiceThread(Socket c) { conn = c; }
public void run() {
final int bufsiz=16384;
byte buff[] = new byte[bufsiz];
try {
OutputStream out = conn.getOutputStream();
4 THREADS 12
InputStream in = conn.getInputStream();
int rc = in.read(buff,0,bufsiz);
while (rc>0) {
out.write(buff,0,rc);
rc = in.read(buff,0,bufsiz);
}
out.close();
in.close();
conn.close();
} catch (IOException e) {
System.err.println("EchoServer: error reading socket");
System.exit(1);
}
}
}
public class EchoServerConc0 {
public static void main(String[] args) {
ServerSocket serverSock = null;
Socket connSock = null;
ServiceThread serve = null;
if( args.length != 1 ) {
System.out.println("Usage: EchoServer port");
System.exit(1);
}
try {
serverSock=new ServerSocket(Integer.parseInt(args[0]));
while(true) {
connSock = serverSock.accept();
serve = new ServiceThread(connSock);
serve.start();
}
} catch (IOException e) {
System.err.println("EchoServer: error on socket");
System.exit(1);
}
}
}
The program is in the le EchoServerConc0.java.
After the accept a new thread is created and given the connected socket connSock,
the new thread is now started, and runs in parallel with other threads and main,
the parent (main) thread loops to do accept again,
the child thread runs and handles the client transaction, when this is nished it reaches the end and
terminates.
4.4 Sending and receiving revisited
We can use threads to combine the rst example programs SendBytesTCP0.java and ReceiveBytesTCP0.java
into a program that can both read from the keyboard and send to a remote program, and also read from the
remote program and print on the console.
4.4.1 The problem
This rst attempt at a solution only adds one thread because all Java programs have one already. The new
thread will read from the network blocking on every read, and then write to the keyboard:
int rc=netIn.read(buf,0,1024);
while (rc > 0) {
System.out.write(buf,0,rc);
rc=netIn.read(buf,0,1024);
}
4 THREADS 13
The other thread (the main program thread) will read from the keyboard, blocking each time, and write to
the network:
int rc=System.in.read(buf,0,1024);
while (rc > 0) {
netOut.write(buf,0,rc);
rc=System.in.read(buf,0,1024);
}
This solves the problem, the two asynchronous activities, each a read/write loop, are handled by separately
scheduled threads.
However there is still a decision to make: which end will execute accept and which will do connect?
In the earlier pair of example programs the receive program also took on the role of server, or acceptor
of the connect request, it executed accept and the sender did connect. This was an arbitrary decision, it
would have worked if the roles were exchanged. In the combined version it is completely arbitrary because
we should be able to right just one programthat can both send and receive but we must make them different
just to make one accept and one connect. So we have two, both with identical send and receive code but
differing in how the connection is established. One is called SendAndReceiveAcceptor00.java, which
will be examined rst, and the other SendAndReceiveConnector00.java.
4.4.2 The acceptor SendAndReceiveAcceptor00.java
class FromRemote extends Thread {
InputStream netIn;
public FromRemote(InputStream i){ netIn=i; }
public void run() {
try {
byte buf[]=new byte[1024];
int rc=netIn.read(buf,0,1024);
while (rc > 0) {
System.out.write(buf,0,rc);
rc=netIn.read(buf,0,1024);
}
} catch (Exception e) { }
}
}
public class SendAndReceiveAcceptor00 {
public static void main(String[] args) throws Exception {
ServerSocket serverSock=new ServerSocket(Integer.parseInt(args[0]));
Socket sock=serverSock.accept();
OutputStream netOut = sock.getOutputStream();
FromRemote fromRemote=new FromRemote(sock.getInputStream());
fromRemote.start();
byte buf[]=new byte[1024];
int rc=System.in.read(buf,0,1024);
while (rc > 0) {
netOut.write(buf,0,rc);
rc=System.in.read(buf,0,1024);
}
netOut.close();
}
}
The program is in the le SendAndReceiveAcceptor00.java. Notice:
main starts and needs one command line argument which it uses as a port number, it creates a
ServerSocket and executes accept, it blocks awaiting a connection. This program must be started
rst.
when it gets the connection it creates a separate thread FromRemote with the stream fromthe network,
it then starts the thread,
the FromRemote thread loops reading from the network connection: netIn.read(..) and writing
to the console: System.out.write(..),
4 THREADS 14
in parallel with the newthread main executes the other loop reading the keyboard System.in.read(..)
and writing to the socket netOut.write(..).
4.4.3 The connector SendAndReceiveConnector00.java
class FromRemote extends Thread {
// identical to FromRemote in SendAndReceiveAcceptor00.java
...
}
public class SendAndReceiveConnector00 {
public static void main(String[] args) throws Exception {
Socket sock=new Socket(args[0],Integer.parseInt(args[1]));
OutputStream netOut = sock.getOutputStream();
FromRemote fromRemote=new FromRemote(sock.getInputStream());
fromRemote.start();
byte buf[]=new byte[1024];
int rc=System.in.read(buf,0,1024);
while (rc > 0) {
netOut.write(buf,0,rc);
rc=System.in.read(buf,0,1024);
}
netOut.close();
}
}
The program is in the le SendAndReceiveConnector00.java. Notice:
the only difference is that this program takes two arguments (though it too is naughty, it doesnt
check!), one is the IP address or host name to connect to and the other is the port,
after that it starts a FromRemote thread and itself enters loop. The thread code is omitted because it
is the same as the previous program.
4.4.4 Even simpler if you use three threads
In this version the same thread class XFer is used for both asynchronous tasks, it is just a loop that reads
an InputStream and writes an OutputStream. It is parameterised with the two streams. So if one thread,
toRemote, is created with System.in as the InputStream and the OutputStream for the socket then it
will read the keyboard and write to the remote host. The other thread is created with input from the socket
and output to the console.
The constructor parameters InputStream and OutputStream are abstract classes, they exist only to
be parent classes, so they can take different child class actual parameters. In the case of InputStream, it
is the parent of all non-abstract input streams: FileInputStreams, BufferedInputStreams (thats what
System.in is) and SocketInputStreams. Its similar for OutputStream.
Lastly note that there are only two asynchronous tasks but three threads, what does the main thread do?
If the main thread reaches the end of the function main when other threads are still running, then it doesnt
terminate the program as it would with a single thread program, instead it waits for both other threads to
nishing before exiting.
class XFer extends Thread {
InputStream in; OutputStream out;
public XFer(InputStream i, OutputStream o){ in=i; out=o; }
public void run() {
try {
byte buf[]=new byte[1024];
int rc=in.read(buf,0,1024);
while (rc > 0) {
out.write(buf,0,rc);
rc=in.read(buf,0,1024);
}
out.close();
} catch (Exception e) { }
}
}
5 DATAGRAMS 15
public class SendAndReceiveAcceptor01 {
public static void main(String[] args) throws Exception {
ServerSocket serverSock=new ServerSocket(Integer.parseInt(args[0]));
Socket sock=serverSock.accept();
XFer toRemote=new XFer(System.in,sock.getOutputStream());
XFer fromRemote=new XFer(sock.getInputStream(),System.out);
fromRemote.start();
toRemote.start();
}
}
The programis in the le SendAndReceiveAcceptor01.java. The programabove is SendAndReceiveAc-
ceptor01.java, it creates a ServerSocket and awaits a connection. The other matching connecting
program is SendAndReceiveConnector01.java, it isnt shown, you can guess what it looks like.
5 Datagrams
There is another transport service (aswell as TCP) called UDP, it is a datagram service. It doesnt require
a connection, it can send and receive packets, the packets can be out-of-order, they are not retransmitted if
they are lost.
UDP datagrams still require a socket (to communicate with the kernel) but it is not connected. In Java
this is called a DatagramSocket. If the socket is to be used to receive datagrams it is bound to a port, if it
is for sending then it does not need a port.
The Java type for the datagramis DatagramPacket. It is used for sending and receiving. ADatagramPacket
must be given storage when it is created, this is the buffer that will contain the message to send or the data
received. The storage is an array of bytes, that is a more primitive form of String. If the datagram is to
be sent it must be given the InetAddress and port of the receiver. Here is an example datagram receiver, it
just waits for a datagram and prints it:
import java.io.*;
import java.net.*;
public class ReceiveUDP0 {
public static void main(String[] args)
throws Exception {
int port = Integer.parseInt(args[0]);
byte buffer[] = new byte[1024];
DatagramSocket socket=new DatagramSocket(port);
DatagramPacket packet=new DatagramPacket(buffer,1024);
while(true) {
socket.receive(packet);
System.out.println("Got packet from "
+ packet.getAddress().getHostName()
+ " port " + packet.getPort() + ": "
+ new String(packet.getData(),0,packet.getLength()));
}
}
}
The program is in the le ReceiveUDP0.java. Note:
it takes its port as a command line argument, and uses it to create a new listening DatagramSocket,
it creates a DatagramPacket with a byte array of length 1024, there is no receiver address so this is
to store received packets,
it then enters a loop and waits for a datagram by calling receive on the socket:
socket.receive(packet);
the packet to put the datagram into is the argument.
when a packet arrives this program can extract information from the Datagram, the sender address,
the senders (ephemeralmore later) port number, and the data.
Here is a sender program:
5 DATAGRAMS 16
import java.io.*;
import java.net.*;
public class SendUDP0 {
public static void main(String[] args)
throws Exception {
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet;
InetAddress iaddr = InetAddress.getByName(args[0]);
int port = Integer.parseInt(args[1]);
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String userInput;
userInput = stdIn.readLine();
while (userInput != null) {
packet = new DatagramPacket(userInput.getBytes(),
userInput.length(),iaddr,port);
socket.send(packet);
userInput = stdIn.readLine();
}
stdIn.close();
socket.close();
}
}
The program is in the le SendUDP0.java. Note:
it takes a destination address and port as a command line arguments, it will use these to send the
datagram,
it creates a DatagramSocket with port, it is the sender,
it enters a loop, each time reading a line from the keyboard, if end of le is typed (d) it gets null
and stops, otherwise it sends the input to the receiver program and repeats the loop,
it creates a new DatagramPacket. The rst argument is the message obtained by converting the
String to a byte array using .getBytes(). It also has to provide the destination InetAddress and
port number,
it then sends the datagram using socket.send(...).

You might also like