Sie sind auf Seite 1von 9

Java is no exception. Java facilitates socket programming through its java.net package.

And true to its philosophy, it abstracts out most of the low-level "nitty-gritty" associated with sockets and provides a clean object-oriented API to work with. In this article I will discuss how to use the java.net package to net enable any application (CLI or GUI based) with TCP based sockets. The first section will cover what sockets are and how they have been supported in Java. In the second section, I will enumerate the steps you need to take to use sockets within an application. In the last two sections I will develop a real world application using sockets. That sets the course for this discussion. Sockets: what are they? If one looks up the definition, the most common one would be "A socket is one endpoint of a two-way communication link between two programs running on the network." To put it differently, it is through sockets that applications access the network and transmit data. The types of sockets are as varied as the purposes and platforms of applications. There are three types of sockets: 1. 2. 3. Unix Domain Sockets Internet Domain Sockets NS Domain Sockets Of these only Internet Domain Sockets are supported across all platforms. So to maintain the cross-platform characteristic intact, Java supports only Internet Domain Sockets. The next question that arises is what are the characteristics of an Internet Domain Socket and what protocols are supported by it? Here are the answers. Internet Domain Sockets By definition "An Internet socket (or commonly, a socket or network socket), is a communication end-point unique to a machine communicating on an Internet Protocol-based network, such as the Internet." All applications communicating through the Internet use a network socket. The feature that distinguishes a network sockets from other sockets is the protocols that it supports. The supported protocols are: 1. 2. 3. TCP UDP Raw IP The difference between them is based on whether the protocol is connection oriented or not. Here are the details. TCP is one of the core protocols of the Internet protocol suite. The protocol guarantees reliable and in-order (correct order of packets) delivery of data from sender to receiver. To put it simply, it's reliable. The second aspect of TCP is that it is connection oriented. That means TCP requires that a connection be made between the sender and receiver before data is sent. The socket associated with TCP is known as the Stream Socket. UDP, like TCP, is one of the core protocols of the IP suite. However, unlike TCP, it neither guarantees inorder delivery of data nor does it requires a connection to be established for sending the data. To put it simply, UDP is an unreliable and connectionless protocol. Sockets associated with UDP are known as Datagram Sockets. Raw IP is a non-formatted protocol, unlike TCP and UDP. It works at network and transport layers. A socket associated with Raw IP is known as a Raw Socket. UDP and TCP sockets just receive the payload or the data, whereas Raw Sockets receive the header info of the packet along with the data. The downside of Raw Sockets is that they are tightly coupled with the implementation provided by the underlying host operating system. Next let's see how Java places the different types of sockets in its libraries.

Sockets in Java Like all other functionalities provided by Java, functionalities to work with sockets are also "packaged" as a package and its classes. The following are the package and its main classes that help in accessing sockets: 1. 2. 3. java.net package ServerSocket Socket Among the above, Java abstracts out most of the low-level aspects of socket programming. Here are the details. The java.net package contains all the classes required to create network enabled applications. ServerSocket and Socket are also part of this package. Apart from these classes, it also contains classes to connect to the web server, create secured sockets, and so forth. The ServerSocket class provides server sockets or sockets at server side. Such sockets wait for requests over the network. Once such requests arrive, a server socket performs operations based on the request and may return a result. The ServerSocket class wraps most of the options required to create server-side sockets. The Socket class provides client-side sockets or simply sockets. They are at the client side connecting to the server, sending the request to the server and accepting the returned result. Just as ServerSocket exposes only the compulsory parameters required to create a server-side socket, similarly, Socket asks the user to provide only those parameters that are most necessary. That covers sockets and Java. In the next section, I will discuss the steps involved in creating socket-based applications. Any net enabled application has two important parts: the code that executes at client-side and the code that executes at server-side. So using the functionality of sockets can be partitioned into two major steps: 1. 2. The server or the server-side code The client or the client-side code The multi-threaded nature of former can always be guaranteed whereas the later may or may not be multithreaded. The Server The server's main function is to wait for incoming requests, and to service them when they come in. So the code to implement the server can be further broken down to the following steps: 1. Establish a server that monitors a particular port. This is done by creating an instance of the ServerSocket class. There are four different ways to create an instance of ServerSocket. They are: a. ServerSocket(), which simply sets the implementation that means everything is taken as default values. b. ServerSocket(int port), which creates a server-side socket and binds the socket to the given port number. c. ServerSocket(int port, int backlog), which not only binds the created socket to the port but also create a queue of length specified by the number passed as the backlog parameter. d. ServerSocket(int port, int backlog, InetAddress bindAddr), which creates a server-side socket that is bound to the specified port number with the queue of length specified by the backlog and bound to the address specified by the bindAddr argument.

So to create a socket bound to port number 8888 with a backlog queue of size 5 and bound with address of local host the statement would be: ServerSocket server = new ServerSocket(8888, 5, InetAddress.getLocalHost() ); One point to keep in mind is that the above mentioned constructors return TCP sockets and not UDP sockets. 2. The next step is to tell the newly created server socket to listen indefinitely and accept incoming requests. This is done by using theaccept() method of ServerSocket class. When a request comes, accept() returns a Socket object representing the connection. In code it would be: Socket incoming = server.accept(); 3. Communicating with the socket, which means reading from and writing to the Socket object. To communicate with a Socket object, two tasks have to be performed. First the Input and Output stream corresponding to the Socket object has to be obtained. That can be done by using the getInputStream() and getOutputStream() methods of Socket class. In code it would be: BufferedReader in = new BufferedReader (new InputStreamReader(incoming.getInputStream())); PrintWriter out = new PrintWriter (incoming.getOutputStream(), true /* autoFlush */); The second task is to read from and write to the Socket object. Since the communication has to continue until the client breaks the connection, the reading from and writing to is done within a loop, like this: boolean done = false; while (!done) { String line = in.readLine(); if (line == null) done = true; else { out.println("Echo: " + line); if (line.trim().equals("BYE")) done = true; } } The actual syntax for reading and writing is not different from the I/O done for simple files. 4. Once the client breaks the connection or stops sending the request, the Socket object representing the client has to be closed. This can be done by calling close() method on the Socket object. The statement would be: incoming.close(); That's how a server is coded. The next section deals with creating a client. The Client The main purpose of the client is to connect to the server and communicate with it using the connection. So coding a client requires the following steps: 1. Connect to the server. Connecting to the server can be accomplished in two steps:

a. Creating a Socket object. The socket at client side just needs to know the host name (the name of the machine where server is running) and the port where the server is listening. To create a Socket object, there are seven constructors provided by the Socket class, of which the most commonly used are:

y y y

Socket(), which creates a new Socket instance without connecting to host. Socket(InetAddress address, int port), which creates a new Socket object and connects to the port specified at the given address. Socket(java.lang.String host, int port), which works the same way as Socket(), except that instead of an address, the host name is used. So to create a Socket object that connects to 'localhost' at 8888, the statement would be: Socket s=new Socket("localhost",8888); b. Connecting to the server comes into picture if no argument constructor is used. It takes the object of the SocketAddress object as an argument. So to connect to localhost at port 8888, the code would be: Socket s= new Socket(); s.connect(new SocketAddress("localhost",8888)); 2. Communicating with the server. Communicating with the server using a socket at client side is no different from how the server communicates with the client. First, the input and output streams connected with the Socket object are to be retrieved thus: BufferedReader in = new BufferedReader (new InputStreamReader(s.getInputStream())); PrintWriter out = new PrintWriter (s.getOutputStream(), true /* autoFlush */); Then you read and write using the corresponding streams. For example, if the client just waits for the data sent by the server, the code would be boolean more = true; while (more) { String line = in.readLine(); if (line == null) more = false; else System.out.println(line); } That covers all the steps involved in creating a network enabled application. In the next section I will be developing a file server application with multi-threading to handle multiple clients. It's time to put theory to practice. The file server to be developed will provide the following services:

1. 2. 3.

List the files that can be downloaded. Send the selected file. Process each request in a separate thread.

This example is from the solution to an exercise from Professor David Ecks on-line textbook, published under an open content license at http://math.hws.edu/eck/cs124/javanotes4/c10/ex-10-4-

answer.html.

There are two classes that form the server:

1. 2.

FileServer - sets up the server. ConnectionHandler - services the requests for sending files to clients. Let's look at the implementation. First comes the FileServer class. It does the following tasks:

1. 2. 3.

Checks the existence of the directory name specified. Sets up the server. Delegates the requests to be handled to an object of the ConnectionHandler class. the following is the implementation of the class: import java.net.*; import java.io.*; public class FileServer { static final int LISTENING_PORT = 3210; public static void main(String[] args) { File directory; // The directory from which the // gets the files that it serves. ServerSocket listener; // Listens for connection // requests. Socket connection; // A socket for communicating // with a client. /* Check that there is a command-line argument. If not, print a usage message and end. */ if (args.length == 0) { System.out.println("Usage: java FileServer <directory>"); return; } /* Get the directory name from the command line, and make it into a file object. Check that the file exists and is in fact a directory. */ directory = new File(args[0]); if ( ! directory.exists() ) { System.out.println("Specified directory does not exist."); return; } if (! directory.isDirectory() ) { System.out.println("The specified file is not a directory."); return; }

/* Listen for connection requests from clients. For each connection, create a separate Thread of type ConnectionHandler to process it. The ConnectionHandler class is defined below. The server runs until the program is terminated, for example by a CONTROL-C. */ try { listener = new ServerSocket(LISTENING_PORT); System.out.println("Listening on port " + LISTENING_PORT); while (true) { connection = listener.accept(); new ConnectionHandler(directory,connection); } } catch (Exception e) { System.out.println("Server shut down unexpectedly."); System.out.println("Error: " + e); return; } } // end main() : : }

Next comes the ConnectionHandler class. The important aspect of this class is that it is a static inner class of the FileServer class. The tasks carried out by this class are: 1. 2. 3. 4. Starting the thread; this is done in the constructor of the class. Send the list of downloadable files; the sendIndex() method contains the logic for sending the list. Sending the selected file, which is performed by the sendFile() method Communicating with the client, which is done in the run() method. This method also calls other methods as required. The following is the implementation. The class is: import java.net.*; import java.io.*; public class FileServer { static final int LISTENING_PORT = 3210; public static void main(String[] args) { File directory; // The directory from which the // gets the files that it serves. ServerSocket listener; // Listens for connection requests. Socket connection; // A socket for communicating with // a client. /* Check that there is a command-line argument. If not, print a usage message and end. */

if (args.length == 0) { System.out.println("Usage: java FileServer <directory>"); return; } /* Get the directory name from the command line, and make it into a file object. Check that the file exists and is in fact a directory. */ directory = new File(args[0]); if ( ! directory.exists() ) { System.out.println("Specified directory does not exist."); return; } if (! directory.isDirectory() ) { System.out.println("The specified file is not a directory."); return; } /* Listen for connection requests from clients. For each connection, create a separate Thread of type ConnectionHandler to process it. The ConnectionHandler class is defined below. The server runs until the program is terminated, for example by a CONTROL-C. */ try { listener = new ServerSocket(LISTENING_PORT); System.out.println("Listening on port " + LISTENING_PORT); while (true) { connection = listener.accept(); new ConnectionHandler(directory,connection); } } catch (Exception e) { System.out.println("Server shut down unexpectedly."); System.out.println("Error: " + e); return; } } // end main() static class ConnectionHandler extends Thread { // An object of this class is a thread that will // process the connection with one client. The // thread starts itself in the constructor. File directory; // The directory from which files are served Socket connection; // A connection to the client. TextReader incoming; // For reading data from the client. PrintWriter outgoing; // For transmitting data to the client. ConnectionHandler(File dir, Socket conn) { // Constructor. Record the connection and // the directory and start the thread running. directory = dir; connection = conn; start(); } void sendIndex() throws Exception { // This is called by the run() method in response // to an "index" command. Send the list of files

// in the directory. String[] fileList = directory.list(); for (int i = 0; i < fileList.length; i++) outgoing.println(fileList[i]); outgoing.flush(); outgoing.close(); if (outgoing.checkError()) throw new Exception("Error while transmitting data."); } void sendFile(String fileName) throws Exception { // This is called by the run() command in response // to "get <fileName>" command. If the file doesn't // exist, send the message "error". Otherwise, // send the message "ok" followed by the contents // of the file. File file = new File(directory,fileName); if ( (! file.exists()) || file.isDirectory() ) { // (Note: Don't try to send a directory, which // shouldn't be there anyway.) outgoing.println("error"); } else { outgoing.println("ok"); TextReader fileIn = new TextReader( new FileReader(file) ); while (fileIn.peek() != '') { // Read and send lines from the file until // an end-of-file is encountered. String line = fileIn.getln(); outgoing.println(line); } } outgoing.flush(); outgoing.close(); if (outgoing.checkError()) throw new Exception("Error while transmitting data."); } public void run() { // This is the method that is executed by the thread. // It creates streams for communicating with the client, // reads a command from the client, and carries out that // command. The connection is logged to standard output. // An output beginning with ERROR indicates that a network // error occurred. A line beginning with OK means that // there was no network error, but does not imply that the // command from the client was a legal command. String command = "Command not read"; try { incoming = new TextReader( connection.getInputStream() ); outgoing = new PrintWriter( connection.getOutputStream() ); command = incoming.getln(); if (command.equals("index")) { sendIndex(); } else if (command.startsWith("get")){ String fileName = command.substring(3).trim(); sendFile(fileName); } else { outgoing.println("unknown command"); outgoing.flush(); } System.out.println("OK " + connection.getInetAddress() + " " + command); }

catch (Exception e) { System.out.println("ERROR " + connection.getInetAddress() + " " + command + " " + e); } finally { try { connection.close(); } catch (IOException e) { } } } } // end nested class ConnectionHandler } That completes the implementation of the File Server.

Das könnte Ihnen auch gefallen