Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

modern-multithreading-c-java

.pdf
Скачиваний:
18
Добавлен:
22.05.2015
Размер:
4.77 Mб
Скачать

TCP SOCKETS

315

than a specified number of milliseconds. To set a 1-second timeout, call the setSoTimeout() method:

socket.setSoTimeout(1000);

// set a 1-second timeout

When the timeout expires, a java.net.SocketTimeoutException is raised and the Socket is still valid.

Class ServerSocket The server begins by creating a ServerSocket :

int serverPort = 2020; ServerSocket listen;

try { listen = new ServerSocket(serverPort); } catch (IOException e) { ... }

The server then calls the accept() method of the ServerSocket in order to listen for connection requests from clients. Method accept() waits until a client requests a connection; then it returns a Socket that connects the client to the server. The server then gets input and output streams from the Socket and uses them to communicate with the client. When the interaction ends, the client, server, or both, close the connection, and the server waits for a connection request from another client.

try {

 

listen = new ServerSocket(serverPort);

 

while (true) {

 

Socket socket = listen.accept();

// wait for a connection request

 

// from a client

toClient = new PrintWriter(socket.getOutputStream(),true);

fromClient = new BufferedReader(new InputStreamReader(socket.

getInputStream()));

 

String line = fromClient.readLine();

// receive a message from the client

System.out.println("Server received "+ line);

toClient.println("Good-bye");

// send a reply to the client

}

 

}

catch (IOException e) { ... }

finally { if (listen != null) try { listen.close();} catch (IOException e) { e.printStackTrace(); }}

If the client–server interaction is not short, the server can create a separate thread to handle the client’s requests. Here we construct a clientHandler Thread to handle the socket returned by method accept():

Socket socket = listen.accept();

clientHandler c = new clientHandler(socket); // class clientHandler extends Thread c.start();

316

MESSAGE PASSING IN DISTRIBUTED PROGRAMS

The run() method of class clientHandler uses input and output streams obtained from the Socket to communicate with the client, exactly as shown above.

Listing 6.2 shows a client and server program using TCP sockets. Message objects are serialized and passed between the client and the server. If the client sends a Message containing the integer n, the server replies with a message containing n2. For example, if the client program is executed using “java Client 2,” the client will send the value 2 to the server, receive the value 4 from the server, and display the message “2 × 2 = 4.”

import java.net.*; import java.io.*;

public final class Client {

public static void main (String args[]) {

int serverPort = 2020; Socket socket = null;

ObjectOutputStream toServer = null; ObjectInputStream fromServer=null; try {

if (args.length != 1) {

System.out.println("need 1 argument"); System.exit(1);

}

int number = Integer.parseInt(args[0]);

//client and server run on the same machine, known as the Local Host InetAddress serverHost = InetAddress.getLocalHost();

socket = new Socket(serverHost,serverPort);

//send a value to the server

toServer = new ObjectOutputStream(new BufferedOutputStream (socket.getOutputStream()));

Message msgToSend = new Message(number); toServer.writeObject(msgToSend); toServer.flush();

//This will block until the corresponding ObjectOutputStream in the

//server has written an object and flushed the header.

fromServer = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));

Message msgFromReply = (Message) fromServer.readObject(); System.out.println (number + "x "+ number + "= "+

msgFromReply.number);

}

catch (IOException e) { e.printStackTrace(); System.exit(1); }

catch (ClassNotFoundException e) {e.printStackTrace(); System.exit(1); } finally { if (socket != null) try { socket.close();} catch (IOException e) {

e.printStackTrace(); }}

}

}

public final class Server {

Listing 6.2 Client and server using TCP sockets.

JAVA TCP CHANNEL CLASSES

317

public static void main (String args[]) {

int serverPort = 2020; ServerSocket listen=null; ObjectOutputStream toClient; ObjectInputStream fromClient; try {

listen = new ServerSocket(serverPort); while (true) {

Socket socket = listen.accept();

toClient = new ObjectOutputStream(new BufferedOutputStream (socket.getOutputStream()));

//This will block until the corresponding ObjectOutputStream in the

//client has written an object and flushed the header.

fromClient = new ObjectInputStream(new BufferedInputStream (socket.getInputStream()));

Message msgRequest = (Message) fromClient.readObject(); // compute a reply and send it back to the client

int number = msgRequest.number; toClient.writeObject(new Message(number*number)); toClient.flush();

}

}

catch (IOException e) {e.printStackTrace(); System.exit(1); }

catch (ClassNotFoundException e) {e.printStackTrace(); System.exit(1); } finally { if (listen != null) try { listen.close();} catch (IOException e) {

e.printStackTrace(); }}

}

}

public final class Message implements Serializable { public int number;

Message(int number) {this.number = number; }

}

Listing 6.2 (continued )

6.2 JAVA TCP CHANNEL CLASSES

We have created three pairs of channel classes for TCP-based communication. These classes provide asynchronous and synchronous message passing and can be used with the selective wait statement presented in Chapter 5. There are many possible ways to design a set of channel classes. Our main objective was to create some easy-to-use classes that hid the complexity of using TCP and enabled us to illustrate how deterministic testing and debugging techniques can be used in a distributed environment. We also tried to be consistent with the channel classes in Chapter 5 so that much of what we covered there would carry over to this chapter. As such, our classes are not built on a client–server paradigm. Instead, they are based on the notion of a mailbox that holds messages deposited by senders and

318

MESSAGE PASSING IN DISTRIBUTED PROGRAMS

withdrawn by receivers. Channel classes for client–server interactions are left as an exercise (Exercise 6.9).

6.2.1 Classes TCPSender and TCPMailbox

The first pair of channel classes consists of classes TCPSender and TCPMailbox. Classes TCPSender and TCPMailbox provide asynchronous message passing using a buffer-blocking send() method and a blocking receive() method, respectively. Listing 6.3 illustrates the use of these classes in a distributed solution to the bounded-buffer problem. Program Producer creates a TCPSender object named deposit for sending items to program Buffer. Program Buffer creates a TCPMailbox object named deposit for receiving items from the Producer, and a TCPSender object named withdraw for sending items to the Consumer. Program Buffer acts as a one-slot bounded buffer, receiving items from the Producer and forwarding them to the Consumer. Program Consumer has a TCPMailbox object named withdraw to receive the messages sent by Buffer.

import java.net.*; import java.io.*;

public final class Producer {

public static void main (String args[]) {

final int bufferPort = 2020; String bufferHost = null; try {

bufferHost = InetAddress.getLocalHost().getHostName(); TCPSender deposit = new TCPSender(bufferHost,bufferPort); deposit.connect();

for (int i=0; i<3; i++) { System.out.println("Producing"+ i);

messageParts msg = new messageParts(new Message(i)); deposit.send(msg);

}

deposit.close();

}

catch (UnknownHostException e) {e.printStackTrace();} catch (TCPChannelException e) {e.printStackTrace();}

}

}

public final class Buffer {

public static void main (String args[]) {

final int bufferPort = 2020; final int consumerPort = 2022; try {

String consumerHost = InetAddress.getLocalHost().getHostName(); TCPMailbox deposit = new TCPMailbox(bufferPort,"deposit"); TCPSender withdraw = new TCPSender(consumerHost,consumerPort);

Listing 6.3 Distributed bounded buffer using TCPSender and TCPMailbox.

JAVA TCP CHANNEL CLASSES

319

withdraw.connect(); for (int i=0; i<3; i++) {

messageParts m = (messageParts) deposit.receive(); withdraw.send(m);

}

withdraw.close(); deposit.close();

}

catch (UnknownHostException e) {e.printStackTrace();} catch (TCPChannelException e) {e.printStackTrace();}

}

}

public final class Consumer {

public static void main (String args[]) { final int consumerPort = 2022;

try {

TCPMailbox withdraw = new TCPMailbox(consumerPort,"withdraw"); for (int i=0; i<3; i++) {

messageParts m = (messageParts) withdraw.receive(); Message msg = (Message) m.obj; System.out.println("Consumed "+ msg.number);

}

withdraw.close();

}

catch (TCPChannelException e) {e.printStackTrace();}

}

}

public final class messageParts implements Serializable {

public Object obj;

// message to be sent

public String host;

// host address of the sender

public int port;

// port where sender will wait for a reply, if any

messageParts(Object obj, String host, int port) { this.obj = obj; this.host = host; this.port = port;

}

// no return address

messageParts(Object obj) {this.obj = obj; this.host = ""; this.port = 0;}

}

Listing 6.3 (continued )

Method connect() must be called before a TCPSender can be used to send messages. A call to connect() opens a TCP connection using the host and port number specified when the TCPSender object is constructed. When there are no more messages to send, method close() is called and the TCP connection is closed. Methods send() and receive() both operate on messageParts objects. A messageParts object packages the message to be sent with the return address

320

MESSAGE PASSING IN DISTRIBUTED PROGRAMS

of the sender. (The return address is optional.) The return address information includes the sender’s host address and a port number that the sender will use to wait for a reply. A thread that calls receive() receives a messageParts object. The return address in this object can be saved and used later to reply to the sender. If no return address is needed, a messageParts object can be constructed without a return address.

Listing 6.4 shows classes TCPSender and TCPMailbox. Class TCPSender provides method send(), which simply encapsulates the code for sending messages using TCP. A TCPSender object is used to send messages to a particular TCPMailbox. The host address and port number of the destination TCPMailbox are specified when the TCPSender object is constructed. Method connect() is used to connect the TCPSender to the TCPMailbox. Once the connection is made, each call to send() uses the same connection. The connection is closed with a call to method close().

A single TCPMailbox object may receive connection requests from any number of TCPSenders. A TCPMailbox objects begins listening for connection requests when the TCPMailbox object is constructed. If multiple TCPSenders connect to the same TCPMailbox, the connections are handled concurrently. When method close() is called on a TCPMailbox object, it stops listening for new connection requests. (This is not to be confused with a call to method close() on a TCPSender object, which closes the TCPSender object’s connection with its TCPMailbox.)

Method receive() of class TCPMailbox returns a messageParts object. The messageParts object returned by receive() is withdrawn from a messageBuffer called buffer. A messageBuffer is simply a bounded buffer of messagePart objects implemented as an SU monitor (see Listing 6.4). The interesting thing about TCPMailbox is the way in which messagePart objects are deposited into buffer. A TCPMailbox object is an active object —during construction it automatically starts an internal thread to receive messages:

public class TCPMailbox implements Runnable {

...

public TCPMailbox( int port, String channelName ) throws TCPChannelException {

this.port = port;

this.channelName = channelName;

try {listen = new ServerSocket( port ); }

catch (IOException e) { e.printStackTrace(); throw new TCPChannelException(e.getMessage());}

buffer = new messageBuffer(100); Thread internal = new Thread(this);

internal.start(); // internal thread executes method run()

}

...

}

JAVA TCP CHANNEL CLASSES

321

import java.net.*;

 

import java.io.*;

 

import java.util.*;

 

public class TCPSender {

 

String destinationHostname = null;

// destination host of this channel

int destinationPort;

// destination port of this channel

Socket socket = null;

 

ObjectOutputStream to = null;

 

Object lock = new Object();

public TCPSender(String destinationHostname, int destinationPort ) { this.destinationHostname = destinationHostname; this.destinationPort = destinationPort;

}

public void connect() throws TCPChannelException { try {

socket = new Socket( destinationHostname, destinationPort ); to = new ObjectOutputStream(socket.getOutputStream());

} catch (Exception e) { e.printStackTrace(); throw new TCPChannelException(e.getMessage());}

}

public void send( messageParts message) throws TCPChannelException{ try {synchronized (lock) {to.writeObject(message); to.flush();}}

catch (NullPointerException e) { e.printStackTrace();

throw new TCPChannelException

("null stream - call connect() before sending messages");

}

catch (Exception e) { e.printStackTrace(); throw new TCPChannelException (e.getMessage());}

}

public void send( ) throws TCPChannelException{ send(new messageParts(new nullObject())); } public void close() throws TCPChannelException {

try {

if (to != null) to.close(); if (socket != null) socket.close(); } catch (Exception e) { e.printStackTrace(); throw new

TCPChannelException(e.getMessage());}

}

}

public class TCPMailbox implements Runnable { private ServerSocket listen = null;

private messageBuffer buffer = null; Socket socket = null;

int port;

Listing 6.4 Classes TCPSender and TCPMailbox.

322

MESSAGE PASSING IN DISTRIBUTED PROGRAMS

String channelName = ""; ObjectInputStream from = null; private boolean closed = false; Object lock = new Object();

public TCPMailbox( int port) throws TCPChannelException {this(port,"");} public TCPMailbox( int port, String channelName ) throws

TCPChannelException { this.port = port;

this.channelName = channelName;

try {listen = new ServerSocket( port ); }

catch (IOException e) { e.printStackTrace(); throw new TCPChannelException(e.getMessage());}

buffer = new messageBuffer(100); Thread internal = new Thread(this);

internal.start(); // internal thread executes method run()

}

public messageParts receive( ) throws TCPChannelException { try {

synchronized(lock) {

messageParts msg = buffer.withdraw(); return msg;

}// end synchronized lock

}catch (Exception e) {e.printStackTrace(); throw new TCPChannelException(e.getMessage());}

}

public synchronized void close() throws TCPChannelException { try {if (listen != null) {closed = true;listen.close();}}

catch (Exception e) {e.printStackTrace(); throw new TCPChannelException(e.getMessage());}

}

public void run() { try {

while (true) {

// listen for connection requests from senders Socket socket = listen.accept();

(new connectionHandler(socket)).start();

}

}

catch (SocketException e) { if (!closed) e.printStackTrace(); } catch (Exception e) {e.printStackTrace();}

finally {

try { if (listen != null) listen.close(); }

catch (IOException e2) { e2.printStackTrace(); }

}

}

Listing 6.4 (continued )

JAVA TCP CHANNEL CLASSES

323

class connectionHandler extends Thread { //handle client connections private ObjectInputStream from;

private Socket socket;

connectionHandler(Socket socket) throws IOException { this.socket = socket;

from = new ObjectInputStream(socket.getInputStream());

}

public void run() { try { // run

messageParts closeMessage = null;

while (true) { // read objects until get EOF messageParts msg = null;

try {

msg = (messageParts) from.readObject(); // read message buffer.deposit(msg);

}

catch (EOFException e) {break; } // end catch } // end while true

// issue passive close (after active close by sender) from.close(); socket.close();

} // end try run

catch (SocketException e) { if (!closed) e.printStackTrace(); } catch (Exception e) {e.printStackTrace();}

finally {

try { if (from != null) from.close(); }

catch (IOException e2) { e2.printStackTrace(); } try { if (socket != null) socket.close(); }

catch (IOException e3) { e3.printStackTrace(); }

}

}// run

}// connectionHandler

}

class messageBuffer extends monitor { // SU monitor

private int fullSlots = 0; // # of full slots in the buffer private int capacity = 0;

private conditionVariable notFull = new conditionVariable(); private conditionVariable notEmpty = new conditionVariable(); private LinkedList buffer;

public messageBuffer( int capacity ) { this.capacity = capacity;

buffer = new LinkedList();

Listing 6.4 (continued )

324

MESSAGE PASSING IN DISTRIBUTED PROGRAMS

}

public void deposit( messageParts msg ) { enterMonitor();

if (fullSlots == capacity) notFull.waitC();

buffer.addLast(msg);

++fullSlots; notEmpty.signalC_and_exitMonitor();

}

public messageParts withdraw() { enterMonitor();

if (fullSlots == 0) notEmpty.waitC();

messageParts msg = (messageParts) buffer.getFirst(); buffer.removeFirst();

--fullSlots; notFull.signalC_and_exitMonitor(); return(msg);

}

}

Listing 6.4 (continued )

The run() method of TCPMailbox accepts connection requests from TCPSender objects and starts a connectionHandler thread to handle the connection:

public void run() { while (true) {

Socket socket = listen.accept(); // listen for connection requests from // senders

(new connectionHandler(socket)).start();

}

}

The connectionHandler thread obtains an input stream from the socket :

connectionHandler (Socket socket) throws IOException { this.socket = socket;

from = new ObjectInputStream(socket.getInputStream());

}

and then uses the input stream to receive messageParts objects. The messagePart objects are deposited into the messageBuffer :

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]