Professional Documents
Culture Documents
03 10 11 Java Netprog
03 10 11 Java Netprog
TCP a bi-directional stream connection. The stream is “reliable” which means the un- 2 conn=ss.accept()
derlying network level requires acknowledgement of each packet sent in the stream, if
any are lost then they are retransmitted transparently to the process using the stream. 3 conn=new Socket(IP,port)
wakes up
A TCP connection is in some ways similar to a telephone call, the link stays open until
either end “hangs-up”, even if nothing is being said. This is the program to program 4 ins=conn.getInputStream() outs=conn.getOutputStream()
communication used by most network applications.
5 out.write(b)
6 ins.read(b)
1.2 Java sockets API
A TCP connection on the internet is a bi-directional link between two running programs Figure 1: Java socket use
(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
1. A “receiver” program (server) creates a server socket and gives it a port number, it will
internet applications are programs that use TCP connections, they send their own protocol
receive incoming connections to that port:
messages to each other through the TCP connections.
ServerSocket ss=new ServerSocket(port);
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
2. it then waits for an incoming connection request by calling:
will wait to accept a connection and another must connect to it.
conn = ss.accept();
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 nothing happens for a bit. . .
different types of socket reflect the asymmetry of connections.
3. in a distant galaxy far far away a “client” program creates a socket:
Socket conn = new Socket(IP,port)
3 Java Network Programming 2.1 ReceiveBytesTCP01.java 4
1 import java.io.*;
where IP is the IP address or name of the receiver’s computer and port is the number 2 import java.net.*;
used when the ServerSocket was created. This is a connection request. 3
If the connection request succeeds (this involves the operating systems communicat- 4 public class ReceiveBytesTCP01 {
ing) then the client returns with a connected socket which it can use. The receiver also 5 public static void main(String args[]) throws Exception {
returns from the accept function and it has a newly generated connected socket of its 6 int port = Integer.parseInt(args[0]);
own, 7 ServerSocket serverSock=new ServerSocket(port);
8
4. they are now connected. However Java does not allow direct reading and writing of 9 Socket conn = serverSock.accept();
the socket (unlike Unix system calls), instead it remembers it has “streams” it uses for 10 InputStream ins = conn.getInputStream();
file I/O so it requires that the programs extract Streams from their sockets. In this 11 byte buf[] = new byte[1024];
example, the receiver will only read the connection and the client will only write so 12
one gets an InputStream and the other an OutputStream. The receiver does: 13 while (true) {
InputStream ins=conn.getInputStream(); 14 int rc = ins.read(buf,0,1024);
15 if(rc <= 0) break;
and the client does: 16 System.out.write(buf,0,rc);
OutputStream outs=conn.getOutputStream(); 17 }
18 conn.close();
5. the client writes (in this case), 20 }
6. and the server reads (in this case). 21 }
Since these streams are exactly the same as the streams returned when you open files Other things to note:
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 • the port number is supplied as a string from the command line. It must be converted to
connected. an integer on line 6 before it can be used to open the “listening” ServerSocket on line
7. The accept() on line 9 causes the program to wait for an incoming connection,
• if and when a connection is made to this program, it wakes up and returns a new “con-
2 Very simple example nected” Socket which can be used for reading and writing to and from the connecting
program,
This section includes two very trivial programs, one of which, ReceiveBytesTCP01.java
creates a server socket and awaits a connection, the other program SendBytesTCP01.java • in Java it isn’t possible to read and write a socket directly since all reading and writing
makes a connection. The sender sends everything from its standard input over the net- is through streams, it is necessary to get an InputStream and or an OutputStream
work, the receiver (once it has a connection) reads everything from the network connec- from the socket. Since this program is going to read everything sent to it over the
tion and writes it all to its standard output. network, in order to read a socket it only needs to getInputStream() on line 10,
• the program then loops reading from the socket (on line 14) and writing it to the stan-
2.1 ReceiveBytesTCP01.java
dard output (on line 16). It only stops when the read on line 14 returns 0 or less, this
ReceiveBytesTCP0.java creates a server socket and then waits for connections with means the other end has closed the connection—it has “hung-up”. This is checked on
accept. When it gets a connection it reads everything sent to it and writes it to the line 15 when break; is executed causing the program it terminate the loop and start
standard output System.out (usually the console). When the connection finishes (the executing statements after the loop, in this case line 18 onwards.
remote user died?) the read returns a byte count of zero and the loop stops.
• Look at an example of simple file I/O to remind yourself how read and write work.
5 Java Network Programming 2.3 Running the programs 6
if conn is the connected Socket. getInetAddress returns an object of type InetAddress, Suppose that the program is busy doing other tasks and doesn’t execute read very
you can look it up in the Java API reference pages but you don’t need to because. . . An often but the other end of the connection is sending a lot of bytes. What will happen?
InetAddress will automatically be converted to a String if you use it in a con- Firstly all operating systems have per-connection buffering and will hold some bytes
text where a String is needed. So if you append an InetAddress to a string using until the program is ready to read them. If the buffers in the operating system network
“+” it will be converted. So you can put it as part of the output string argument to drivers are becoming full special TCP messages will be sent to the sender system
System.out.println for example. Compile it and test it. asking it to slow down, this will result in subsequent write calls in the sender blocking
to wait for the receiver to read something and make some space in the buffers.
2.5 More about read and write Once again, like with keyboard input, just because no bytes are available it doesn’t
mean input is finished, the sender program might just be a bit slow. A TCP socket
The read method behaves slightly differently with different sources of input. So given connection stays open indefinitely until one end “closes” the connection. So the only
the same read: time 0 will be returned to a read is when the other end executes close().
rc = ins.read(buffer,0,1024);
2.6 Why can’t this program be changed to send and receive?
there will some differences with the following three sources of input:
Well it can. . . but not simply. If the program is going to send then it must try to read from
from a file when reading is from a disc file there will be a delay because reading from a the keyboard and this will cause it to “block” (wait), however it must also be prepared
disc is thousands of times slower that main memory or the CPU. But the delay is small to read incoming messages from the network connection, once again it must block. How
and relatively predictable when compared with the next two. can it wait in two places at the same time? It has a single “thread” of control, if it waits
Each time the read is executed it will get the next 1024 bytes, it is as if it has a pointer anywhere nothing else can happen. Section 6 on threads explains one way of solving this
moving through the file. Every successful read except the last should get 1024 bytes problem.
and therefore will assign 1024 to rc. The last successful read will get the remaining
bytes, so if a file contains 2,500 bytes, the first two reads will each get 1024 bytes
but the third will get 452 bytes. If read is executed a fourth time it will get nothing, 3 Catching an exception
there are no more bytes to read so it will return 0 and assign it to rc—this indicates
“end-of-file”. This version 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
from the keyboard when standard input is read without any redirection read will almost wrong, the port is wrong, or the receiver isn’t actually running. In all these cases the
always have to wait a variable amount of time for the user to type stuff in. connection will fail to be established, this is detected and reported when the sender (client)
It will read one line (so long as it is less than 1024 characters long), the actual number executes:
of bytes read and put in buffer will be returned to rc. Socket conn = new Socket(IP,port)
It is hard to know when the end of input is reached because the user might have finished
or just be thinking for longer than usual. So a way of indicating “end-of-file” is for the exception thrown can be caught and the use can be informed. There were also other
the user to type: “control-d” on Linux or “control-z” on Windows. This will cause the exceptions that were ignored in the simpler versions the commonest is IOException
read to read no characters and return 0 which will be assigned to rc in this example. which occurs if there are any network communication problems.
An exception in a program is the failure of some operation to finish correctly. This
from a network connection like the keyboard input and unlike the file it is possible there could be a runtime error such as a failure to read or write correctly. catch the exception
are no bytes available, or there are less than 1024. If there are no bytes available read by surrounding any statements that could cause an exception with:
will wait, if some bytes arrive they will be read immediately even if there are fewer
than 1024, the number actually read into buffer will be returned as the result and try { statements-that-might-cause-exception} catch(...) {...}
assigned to rc. However it is possible that a lot of bytes have arrived since the last The catch clauses “match” each different exception and are followed by any number of
read, in which case read will get as many as it can, ie. 1024, and then read the rest statements, in this case an error message followed by exit.
on subsequent reads.
9 Java Network Programming 10
import java.io.*; This is a minimal version with no the checking of arguments and exception handling.
import java.net.*; An extended version is shown afterwards.
– if there is no port number provided the program will use 80, ie. if the number of HTTP/1.1 200 OK
arguments is threetwo args.length==2 Date: Sun, 24 Oct 2010 23:07:34 GMT
Server: Apache/2.2.16 (Debian)
– otherwise if there are not at least two arguments it’s an error,
Last-Modified: Fri, 01 Oct 2010 13:50:22 GMT
• it also has a named constant as the buffer size. The length of the array used as a buffer Accept-Ranges: bytes
Content-Length: 134
is used in more than one place so the advantage of a named constant is that it helps
Connection: close
ensure consistency,
Content-Type: text/html
• apart from that it is similar to HTTPGet0.java
<h1> Tiny Example Page </h1>
<p> This is a tiny HTML file, it contains just
4.2 A programming example: HTTPGet3.java one paragraph surrounded by open and closing tags. </p>
This section shows how to develop an improvement to the HTTPGet0.java program. It will start with the sequence of bytes:
is an example of how to use streams and to solve a network programming problem but,
perhaps more importantly, it is also an example of simple Java programming, showing H T T P / 1 . 1 2 0 0 O K CR LF D a t e : . . .
how to use arrays, strings and functions. to end the first line. And the byte sequence before and after the blank line is:
The goal is to change the program to: So the first “CR LF” ends the Content-Type: text/html” and the second “CR LF” is
the blank line—there were no other bytes.
• save the file that is returned from the server, in the first version it is just displayed on But how does the program detect the linefeed byte that ends the line? The current ver-
the console. The file will be saved with the name of the resource, so if the command sion of the program, HTTPGet0.java repeatedly reads blocks of 8192 bytes, the carriage
is: return and line feed could be anywhere, they might even be split between two blocks of
bytes. There are various possible ways of dealing with the problem. One way might be to
java HTTPGet3 tink.stca.herts.ac.uk /tiny.html 80
read a block and then search through it looking for carriage return and linefeed. But that
the program should remove the “/” and save what is returned with name tiny.html, way is quite complicated. Another possibility is to write a function readLine that will
read one line from the TCP socket InputStream every time it is called; not only will that
• it will only try to save the file if the first response line from the server is “HTTP/1.1 find the first line to check for “200” but when it reads a line with nothing in it it will have
200 OK”, in other cases it will print an error, found the blank line. The function readLine will:
• also it must only save the correct file contents, that means that it must not save any • read one line byte by byte and store all the bytes, when it finds carriage return and line
of the option lines. The HTTP protocol requires that every response has a blank line feed it will create a String and return it,
after the option lines and before the file contents. Therefore the program must skip the
option lines and the blank line and only start saving the bytes after the blank line. • it will not include the carriage return and linefeed in the string so an empty, blank line
will have a zero length, ie it contains nothing,
4.2.2 The problem: skipping the header lines • it will leave the TCP socket InputStream in a state where the next byte will be the
How do you get the first line to look for “200” and then how do you know where the one immediately after the carriage return and linefeed.
response option lines end and the file contents start? First the program must detect the
end of the first line and then it must detect the blank line. In network text messages lines The main program will have to:
usually end with two bytes: carriage return and then linefeed, written in Java as “\r” and • call the readLine function to get the first line, save it,
“\n”. So the response:
• then skip all the option lines by reading them one by one and stopping when one of
the lines has a zero length (it’s the blank line),
15 Java Network Programming 4.2 A programming example: HTTPGet3.java 16
• then the program checks if the first line contained “200” if it did it will create a file line. If the byte is the end of line marker, a linefeed, written in Java as “\n” then the loop
and read the rest of the TCP socket InputStream bytes into the file. The reading of stops because executing break: causes it to jump out of the loop to the first statement
the rest of the stream will be done, as before, in blocks, not line by line, because: following the loop:
1
1. it’s more efficient, the program will loop 8192 of the number of times and issue only 7 if(line[count] == ’\n’) break;
1
8192 the number of read library calls, and
note that line[count] is the last byte read. If the byte is not linefeed then the next
2. very often downloaded files will not be readable ASCII characters, they will be statement in the loop will be executed:
image files, movies or executable programs, in which case there is no guarantee
that they will have any bytes that are carriage return or line feed, 8 if(line[count] != ’\r’) { count++; }
• if it’s not “200” it will print a message and stop. it counts the byte stored if it wasn’t the “carriage return” character, written in Java as “\r”.
Obviously it must increment the count for the bytes stored so the next byte will be stored
4.2.3 The design of the readLine function in the next element of the array. But why not increment if it’s “\r”? The linefeed marks
the end of line but it is usually preceded by a carriage return, therefore the loop only stops
The function readLine will take an InputStream as an argument and return a String when a linefeed is read and the carriage return must be ignored, skipped, or got rid of
built from the bytes read from the stream up to the end of line character(s). somehow. If a carriage return byte is read the variable count is not incremented and on
the next iteration of the loop the read statement will overwrite it with the next byte—that
2 static String readLine(InputStream ins) throws Exception {
effectively ignores it.
3 byte line[] = new byte[256];
4 int count=0;
5 while(true) { 4.2.4 Dry run showing the operation of the read loop
6 int rc = ins.read(line,count,1); When readLine is called the array is created, it is empty, and the variable count is set to
7 if(line[count] == ’\n’) break; 0:
8 if(line[count] != ’\r’) { count++; }
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255
9 }
10 return new String(line,0,count);
11 } count = 0 input stream HTTP/1.1 200 OKCRLF Date...
It has static in front because it is called by another static function main. Don’t worry
the read reads the first byte “H” and stores it in line at offset count which is 0, it then
about this, if you are writing methods that will be called in objects they will be just the
checks if line[count] is linefeed, no, so it continues and checks if it’s carriage return,
same but without static in front.
no so it increments count to 1:
The server response will arrive as a stream:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255
input stream HTTP/1.1 200 OKCR LF Date... H
C L
count = 1 input stream TTP/1.1 200 OK R F Date...
The function has a local array line which will get the bytes of the next line, one by one,
into consecutive locations. The variable count will be used to index the array aswell as
the next iteration of the loop reads “T”, the next byte from the TCP socket connection
count the bytes in the line. The loop will read one byte on each iteration and store it into
stream, and stores it in line[1] because 1 is the current value of count. It’s not “\n” or
the correct position in line:
“\r” so count is incremented to 2:
6 int rc = ins.read(line,count,1); 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255
H T
read takes as arguments: an array, then the offset in the array where the bytes read will
be stored, then lastly a count of the number of bytes to read. So in this case if count count = 2 input stream TP/1.1 200 OK C L
R F Date...
is incremented every time the bytes will be stored in consecutive positions of the array
17 Java Network Programming 4.2 A programming example: HTTPGet3.java 18
the next byte, another “T” is dealt with similarly: 35 if(! (firstLine.startsWith("HTTP") && firstLine.contains("200"))){
36 // not 200 OK response
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255
37 System.err.println("Not 200 OK");
H T T
38 } else { // OK save the file
C L
count = 3 input stream P/1.1 200 OK R F Date...
this is probably a bit more than is needed but it first checks the first line is a server response
line, ie. it starts with “HTTP” and then checks it contains “200”. It uses the two String
and this continues until the sequence of bytes: “HTTP/1.1 200 OK” has been read and
methods:
stored in line:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255 • s.startsWith(p) which gives true if s starts with p,
H T T P / 1 . 1 2 0 0 O K • s.contains(p) which gives true if s has the substring p anywhere in it.
C L
count = 15 input stream R F Date... In order to create a new file to store the downloaded document it takes the resource
name that was the second argument on the command line, args[1], and strips off the the
on the next cycle of the loop “\r” is read in and stored in line[15]” (15 is the current leading “/”:
value of count) but it isn’t counted, count is still 15. Next time round the loop linefeed,
the end of line marker, “\n” is read and the loop stops. 39 String fname=args[1].substring(1);
At the end of the loop the array line holds “HTTP/1.1 200 OK” in the first 15 ele- 40 OutputStream ofile = new FileOutputStream(fname);
ments, that is: 0–14 and count is 15. The statement: 41
42 while (true) {
10 return new String(line,0,count); 43 int rc = fromServer.read(buffer,0,2048);
44 if(rc<=0) break;
turns the array of bytes into a Java String and returns it. The array line contains 15
45 ofile.write(buffer,0,rc);
bytes and the value of count is 15 so new String(line,0,count) uses the 15 bytes
46 }
from line[0] to line[14] to make the string, the linefeed that’s in line[15] is ignored
47 ofile.close();
and not used.
There is a String method s.substring(n) that takes on argument “n” and returns a new
4.2.5 Using the function readLine string that contains all the characters from the string s from index position “n” to the end.
So s.substring(1) returns a new string identical to s but without the first character.
Now the function is complete it must be used by the rest of the program to read and save Remember almost everything in Java starts at index position “0”, characters in strings
the first line from the server and then skip all the other option lines up to and including the aswell as elements of arrays, so position 1 is the second character.
blank line. After that the program will open a new file and write all the remaining bytes
from the InputStream into the file. The code:
4.2.6 The whole program HTTPGet3.java
29 String firstLine = readLine(fromServer); 1 public class HTTPGet3 {
30 while(true) { 2 static String readLine(InputStream ins) throws Exception {
31 String line = readLine(fromServer); 3 byte line[] = new byte[256];
32 if(line.length() == 0) break; 4 int count=0;
33 } 5 while(true) {
6 int rc = ins.read(line,count,1);
will do that. It calls readLine once to get the first line, then loops until the String line 7 if(line[count] == ’\n’) break;
has length zero meaning it is a blank line, then it stops. 8 if(line[count] != ’\r’) { count++; }
The next job the program must do is to check if the first line is an “OK” server response 9 }
and output an error if not: 10 return new String(line,0,count);
11 }
19 Java Network Programming 20
12 public static void main(String[] args) throws Exception { client and immediately sends whatever it read back again. When the connection from a
13 System.setProperty( "java.net.preferIPv4Stack", "true" ); client is closed (a rc==0 returned from read) the server loops to accept the next connec-
14 int port = 80; tion from another client. The client makes a connection and then loops each time: reading
15 byte buffer[] = new byte[2048]; from the user, writing this text to the server, reading the server’s response (which should
16 be the same) and then printing it. The server:
17 Socket socket = new Socket(args[0], port);
18 OutputStream toServer = socket.getOutputStream(); import java.io.*;
19 InputStream fromServer = socket.getInputStream(); import java.net.*;
20 String request =
21 "GET " + args[1] + " HTTP/1.1\r\n"
public class EchoServer0 {
22 + "Host: " + args[0] + "\r\n"
23 + "Connection: Close\r\n\r\n";
public static void main(String args[]) throws Exception {
24 System.setProperty( "java.net.preferIPv4Stack", "true" );
25 toServer.write(request.getBytes()); int echoPort = Integer.parseInt(args[0]);
26 byte buffer[] = new byte[4096];
27 // now read server response and skip option lines ServerSocket serverSock = new ServerSocket(echoPort);
28 // followed by file Socket connSock;
29 String firstLine = readLine(fromServer);
30 while(true) { while(true) {
31 String line = readLine(fromServer); connSock = serverSock.accept();
32 if(line.length() == 0) break; OutputStream outs = connSock.getOutputStream();
33 }
InputStream ins = connSock.getInputStream();
34 // all header lines read, check first to see if OK
35 if(! (firstLine.startsWith("HTTP") && firstLine.contains("200")) ) {
36 // not 200 OK response while (true) {
37 System.err.println("Not 200 OK"); int rc = ins.read(buffer,0,4096);
38 } else { // OK save the file if(rc <= 0) break;
39 String fname=args[1].substring(1); outs.write(buffer,0,rc);
40 OutputStream ofile = new FileOutputStream(fname); }
41 outs.close(); ins.close(); connSock.close();
42 while (true) { }
43 int rc = fromServer.read(buffer,0,2048); }
44 if(rc<=0) break; }
45 ofile.write(buffer,0,rc);
46 } The program is in the file EchoServer0.java.
47 ofile.close();
48 } • This is a very cut-down program, there is no error checking or exception handling,
49 toServer.close(); socket.close();
50 } • notice that the server loops forever:
51 }
while(true) {
...
5 Client server example echo nearly all servers are like this, deal with one request and loop to “accept” the next,
This example consists of a server and a client. They do very little except show how a • this is a server so it must create a ServerSocket bound to a port number. The port
stream connection is set up. The server awaits (accept) a connection, reads from the number to use is provided as an argument. It then waits by calling accept,
21 Java Network Programming 22
• this network program reads and writes bytes into a buffer not single characters or lines. the line “hello” is read from the user, sent to the server returned by it, read from the
However it is worth noting how much will be read, consider: 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 finishes (by
rc = ins.read(buffer,0,4096);
typing control-d “^d” on Unix) the loop finishes and the program finishes.
with a network connection it will read all the bytes available into buffer starting at The “echo service” isn’t obviously very useful, however it is a real internet standard
offset 0 up to a maximum of 4096. If there are no bytes it will block, if there are more protocol, it’s main use is for testing. Also it is a useful example for introducing simple
than 4096 bytes it will read just 4096 this time. The number of bytes read is returned networking concepts. BUT notice about this version of the server EchoServer0.java:
and assigned to rc,
• there are two while loops, one nested in the other,
• it then loops reading from the client and writing back again. When it gets rc==0 from
read (which would be end of file for a file) it means the client closed the connection. • the outer one awaits and accepts a new client connection
Now the client, once again this is a cutdown, non-error checking one: • the inner one repeatedly reads and writes messages from the current client
public class EchoClient0 { • while it is dealing with one client it is waiting for messages it CANNOT also be waiting
public static void main(String[] args)throws Exception{ for other new client connections
System.setProperty( "java.net.preferIPv4Stack", "true" );
Socket sock=new Socket(args[0],Integer.parseInt(args[1])); • only when the current client closes the connection will the server break out of the loop
OutputStream outs=sock.getOutputStream(); and go rounf the outer loop to await a new client connection
InputStream ins=sock.getInputStream(); • it is a sequential server, it deals with clients one at a time
byte buffer[]=new byte[4096];
1. The first example is a server that needs to deal with more than one client transaction at class MyThread extends Thread {
a time, maybe an improved version of the EchoServer that will deal with many clients ...
at once, public MyThread(...) { ... }
public void run() {
2. the second example is the need for a program to send to and receive the network at the ...
same time, }
3. and the final one is the problem of receiving from the network and awaiting GUI ...
commands in one program, }
...
...
6.1.1 A server dealing with multiple clients
MyThread mythrd = new MyThread(...);
Consider the simple (stupid?) echo server EchoServer0.java, it creates a ServerSocket mythrd.start();
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 exe- Note:
cuting 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 • every thread must provide public void run(), this is the code that will be executed
same way, suppose that a client has a slow internet connection, the web server must read when the thread starts,
their request and write the required file (maybe a large MP3) to the slow connection, all • when a thread is created with new it doesn’t start immediately, the thread is a class
this time no other browser requests can be accepted. type so the constructor is executed in order to initialise the object but run doesn’t start
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 start a thread the code that created it must then call start, that causes the Java
to wait for input from its client it doesn’t affect the other threads. virtual machine to start the run() function in parallel.
Consider the first simple pair of programs SendBytesTCP0.java and ReceiveBytesTCP0.java, Another way to create a thread to execute a separate body of code is to write a class
why shouldn’t they be combined into a single version that could enable a proper dialogue that implements the interface Runnable, which really means it must provide a public
with another such program (a bit like a very simple messenger program)? Do do this there void run() function. This in itself is not a thread, a plain Thread must then be created
would need to be two threads because there would be a need to “wait” in two places: one separately and given an object of the Runnable to run. Here is the skeletal code:
thread would wait for user input and then write it to the network, and the other thread class MyRun implements Runnable {
would be waiting for bytes arriving from the network and writing them to the console. ...
public MyRun(...) { ... }
6.2 Threads in Java public void run() {
...
Most languages and operating systems provide threads, unfortunately each does it in dif- }
ferent 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???). ...
...
6.2.1 By inheritance MyRun runObj = new MyRun(...);
Thread thrd = new Thread(runObj);
One way is to write a new class that “inherits” from, or extends, the class Thread, in
thrd.start();
other words write your own thread class.
...
25 Java Network Programming 6.2 Threads in Java 26
Note: thread2.start();
}
• any class implementing Runnable must provide public void run(), this is the code }
that will be executed when the thread starts,
The program is in the file Threads1.java. When the threads are started they execute their
• a runnable object is created, in the example above it is runObj, it is not a thread but it run routine for ever repeatedly printing out their message. If this is compiled and run it
has a run function that could be used by a thread, can produce almost any output sequences depending on how the threads are scheduled,
which means how they are allocated a share of the CPU time. Here is part of one sequence:
• 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 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
separately executable thread will execute the run function in runObj, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
• but runObj cannot be scheduled for execution (even though it provides) the instruc- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
tions, it is the thread that must be started: thrd.start(). aaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Even with a multiprocessor computer the output will be affect by how Java buffers the
6.2.3 What is the difference? messages it writes. One thread may give a lot of output to the operating system which
Very little. Maybe the use of inheritance (“extending”) is a bit simpler. On the other hand will be printed before the output of another thread.
It is possible to force the scheduling of the threads to change and to be more inter-
because a class can only extend at most one other class you might implement Runnable
leaved by causing a thread to “sleep” briefly after every print statement:
so the class can extend some other class.
class Loopy extends Thread {
6.2.4 A two thread program (not doing networking) String message;
it doesn’t do much but it does allow the program to compile. Here is some output from System.out.println("Usage: EchoServer port");
this version: System.exit(1);
}
ababababababababababababababababababababababababababab try {
ababababababababababababababababababababababababababab ServerSocket serverSock=new ServerSocket(Integer.parseInt(args[0]));
ababababababababababababababababababababababababababab while(true) {
ababababababababababababababababababababababababababab Socket connSock = serverSock.accept();
ServiceThread serve = new ServiceThread(connSock);
6.3 A concurrent echo server serve.start();
}
If a server has to deal with a long transaction for a client, involving lots of waits for } catch (IOException e) {
reading and writing files and sockets, it will be unable to accept new requests. One System.err.println("EchoServer: error on socket");
simple solution is to change the server so that after the accept it creates a “child” thread. System.exit(1);
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. }
public ServiceThread(Socket c) { conn = c; } • the parent (main) thread loops to do accept again,
• the child thread runs and handles the client transaction, when this is finished it reaches
public void run() {
the end and terminates.
final int bufsiz=16384;
byte buff[] = new byte[bufsiz];
try { 6.4 Sending and receiving revisited
OutputStream out = conn.getOutputStream();
InputStream in = conn.getInputStream(); We can use threads to combine the first example programs SendBytesTCP0.java and
ReceiveBytesTCP0.java into a program that can both read from the keyboard and send
while (true) { to a remote program, and also read from the remote program and print on the console.
int rc = in.read(buff,0,bufsiz);
if(rc<=0) break;
6.4.1 The problem
out.write(buff,0,rc);
} This first attempt at a solution only adds one thread because all Java programs have one
out.close(); conn.close(); already. The new thread will read from the network blocking on every read, and then
} catch (IOException e) { write to the keyboard:
System.err.println("EchoServer: error reading socket");
System.exit(1); while (true) {
} int rc=ins.read(buf,0,1024);
} if(rc<=0) break;
} System.out.write(buf,0,rc);
public class EchoServerConc0 {
}
public static void main(String args[]) {
if( args.length != 1 ) {
29 Java Network Programming 6.4 Sending and receiving revisited 30
The other thread (the main program thread) will read from the keyboard, blocking each byte buf[]=new byte[1024];
time, and write to the network: while (true) {
int rc=System.in.read(buf,0,1024);
while (true) { if(rc<=0) break;
int rc=System.in.read(buf,0,1024); outs.write(buf,0,rc);
if(rc<=0) break; }
outs.write(buf,0,rc); outs.close();
} }
}
This solves the problem, the two asynchronous activities, each a read/write loop, are
handled by separately scheduled threads. Notice:
However there is still a decision to make: which end will execute accept and which • main starts and needs one command line argument which it uses as a port number, it
will do connect? In the earlier pair of example programs the “receive” program also creates a ServerSocket and executes accept, it blocks awaiting a connection. This
took on the role of “server”, or acceptor of the connect request, it executed accept and program must be started first.
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 • when it gets the connection it creates a separate thread FromRemote with the stream
should be able to right just one program that can both send and receive but we must from the network, it then starts the thread,
make them different just to make one accept and one connect. So we have two, both • the FromRemote thread loops reading from the network connection: ins.read(..)
with identical send and receive code but differing in how the connection is established. and writing to the console: System.out.write(..),
One is called SendAndReceiveAccept0.java, which will be examined first, and the other
SendAndReceiveConnect0.java. • in parallel with the new thread main executes the other loop reading the keyboard
System.in.read(..) and writing to the socket outs.write(..).
6.4.2 The “acceptor” SendAndReceiveAccept0.java
6.4.3 The “connector” SendAndReceiveConnect0.java
class FromRemote extends Thread {
InputStream ins; class FromRemote extends Thread {
public FromRemote(InputStream i){ ins=i; } // identical to FromRemote in SendAndReceiveAccept0.java
public void run() { ...
try { }
byte buf[]=new byte[1024]; public class SendAndReceiveConnect0 {
while (true) { public static void main(String[] args) throws Exception {
int rc=ins.read(buf,0,1024); Socket sock=new Socket(args[0],Integer.parseInt(args[1]));
if(rc<=0) break; OutputStream outs = sock.getOutputStream();
System.out.write(buf,0,rc);
} FromRemote fromRemote=new FromRemote(sock.getInputStream());
} catch (Exception e) { } fromRemote.start();
}
} byte buf[]=new byte[1024];
public class SendAndReceiveAccept0 { while (true) {
public static void main(String[] args) throws Exception { int rc=System.in.read(buf,0,1024);
ServerSocket ssock=new ServerSocket(Integer.parseInt(args[0])); if(rc<=0) break;
Socket sock=ssock.accept(); outs.write(buf,0,rc);
OutputStream outs = sock.getOutputStream(); }
FromRemote fromRemote=new FromRemote(sock.getInputStream()); outs.close();
fromRemote.start(); }
}
31 Java Network Programming 32
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();
}
}
Note:
• it takes a destination address and port as a command line arguments, it will use these
to send the datagram,