An introduction to network programming with java download


















Brings together a large number of important network programming concepts and techniques, usually only covered adequately across a number of texts Copious use of examples, with those examples 'stripped down' to their bare essentials in order to avoid confusion End-of-chapter exercises, with full model solutions available from a complementary website Contains full Java 5 coverage Request lecturer material: sn.

Front Matter Pages i-xi. Basic Concepts, Protocols and Terminology. Pages Starting Network Programming in Java. Multithreading and Multiplexing. File Handling. Introduction to Enterprise JavaBeans.

If the operation that each thread is trying to execute were an atomic operation i. Though this might at first appear to be the case, this is not so. The problem is that the sub-operations from the two updates may overlap each other. In order to avoid this problem in Java, we can require a thread to obtain a lock on the object that is to be updated.

Any other updating thread must wait until the lock has been released. Once the first thread has finished its updating, it should release the lock, making it available to other such threads. Note that threads requiring read-only access do not need to obtain a lock. One unfortunate possibility with this system, however, is that deadlock may occur.

A state of deadlock occurs when threads are waiting for events that will never happen. Consider the example illustrated in Fig. Here, thread1 has a lock on resource res1, but needs to obtain a lock on res2 in order to complete its processing so that it can release its lock on res1. At the same time, however, thread2 has a lock on res2, but needs to obtain a lock on res1 in order to complete its processing. Unfortunately, only good design can avoid such situations. In the next section, we consider how locks are implemented in Java.

All other threads attempting to invoke this method must wait. Once the method has finished execu- tion, the lock is released and made available to other threads. If an object has more than one synchronized method associated with it, then only one may be active at any given time. A call to wait may lead to an InterruptedException, which must either be caught or declared to be thrown by the containing synchronized method.

Since there is no way of specifying which thread is to be woken, this is only really appropriate if there is only one waiting thread. If all threads waiting for a lock on a given object are to be woken, then we use notifyAll. However, there is still no way of determining which thread gets control of the object. The JVM will make this decision. Methods wait, notify and notifyAll may only be called when the current thread has a lock on the object i.

If any of these methods is called from elsewhere, an IllegalMonitorStateException is thrown. The resource will be modelled by a Resource class, while the producer and consumer will be modelled by a Producer class and a ConsumerClient class respec- tively. The Producer class will be a thread class, extending class Thread. The server program, ResourceServer, will create a Resource object and then a Producer thread, passing the constructor for this thread a reference to the Resource object.

The server will then start the thread running and begin accepting connections from ConsumerClients. As each client makes connection, the server will create an instance of ClientThread another Thread class , which will be responsible for handling all subsequent dialogue with the client.

The code for ResourceServer is shown below. Both of these methods will return the new resource level. Since each of these meth- ods will modify the resource level, they must both be declared with the keyword synchronized. The code for the Producer class is shown below. As in previous examples, a randomising feature has been included. In this simple example, the resource level will not be allowed to exceed 5. Once the resource level has reached 5, production must be suspended. This is done from method addOne by calling wait from within a loop that continuously checks whether the resource level is still at maximum.

The calling of wait suspends the Producer thread and releases the lock on the shared resource level variable, allow- ing any ConsumerClient to obtain it. Thus, if the resource level is at zero when method takeOne is executed, wait is called from within a loop that continuously checks that the level is still at zero.

The calling of wait suspends the ConsumerClient thread and releases the lock on the shared resource level variable, allowing any Producer to obtain it. The code for class Resource is shown below. Note that ResourceServer must have access to the code for both Producer and Resource. The code for ClientThread is shown below.

Just as for classes Producer and Resource, this code must be accessible by ResourceServer. However, the required code for this class is very similar in structure to that of MultiEchoClient from Sect. In the meantime, the screenshots in Figs. The signature for this method is as follows: int available throws IOException For an InputStream object attached to a network connection, this method returns the number of bytes received via that connection and now in memory , but not yet read.

J2SE 1. This API is implemented by package java. Essentially, rather than being byte-orientated, as Java streams are, channels are block-orientated. This means that data can be transferred in large blocks, rather than as individual bytes, leading to significant speed gains. As will be seen shortly, each channel is associated with a buffer, which provides the storage area for data that is written to or read from a particular channel.

It is even possible to make use of what are called direct buffers, which avoid the use of intermediate Java buffers wherever possible, allowing system level operations to be performed directly, leading to even greater speed gains. Of greater relevance to the title of this section, though, is the mechanism for handling multiple clients. Instead of allocating an individual thread to each client, NIO uses multiplexing the handling of multiple connections simultaneously by a single entity.

This is based on the use of a selector the single entity to monitor both new connections and data transmissions from existing connections. Each of our channels simply registers with the selector the type s of event in which it is interested. It is possible to use channels in either blocking or non-blocking mode, but we shall be using them in non-blocking mode.

The use of a selector to monitor events means that, instead of having a separate thread allocated to each connection, we can have one thread or more, if we wish monitoring several channels at once. This avoids problems such as operating system limits, deadlocks and thread safety violations that may occur with the one thread per connection approach.

Though the multiplexing approach offers significant advantages over the multi- threaded one, its implementation is notably more complex. If greater speed is required, though, it will be necessary to employ NIO directly. The next sub-section provides the necessary detail to allow you to do this.

By default, the sockets associated with such channels will operate in blocking mode, but may be configured as non-blocking sockets by calling method configureBlocking with an argument of false. This method is a method of the channel classes and needs to be called on a channel object before the associated socket is created. Once this has been done, the socket itself may be generated by calling method socket on the chan- nel socket. The code below shows these steps. In this code and elsewhere in this section, the prior declaration of Socket, SocketChannel, ServerSocket and ServerSocketChannel objects with names socket, socketChannel, serverSocket and serverSocketChannel respectively is assumed.

Note that a ServerSocketChannel object is created not via a constructor, but via static method open of the ServerSocketChannel class. This generates an instance of a platform-specific sub- class that is hidden from the programmer. The lines required to create the InetSocketAddress object and bind the ServerSocket object to the port are shown below.

The pre-declaration of a constant PORT holding the port number is assumed. It is now appropriate to create an instance of class Selector, which is another of the classes in package java. This object will be responsible for monitor- ing both new connections and the transmission of data from and to existing connec- tions.

Each channel whether SocketChannel or ServerSocketChannel must register with the Selector object the type of event in which the channel is interested via method register. There are four static constants of class SelectionKey package java. The two most commonly required con- stants and the ones that we shall be using are SelectionKey.

These will allow us to monitor new connections and data transmissions from existing connections respectively. The first will be of interest to our ServerSocketChannel object, of course, while the second will be of interest to our SocketChannel object. The code for creating the Selector object and registering the respective interests of our two channel objects is shown below. Note that, as with the ServerSocketChannel object, a Selector object is created not via a constructor, but via static method open that again creates an instance of a platform-specific sub-class that is hidden from the programmer.

Here and elsewhere in this section, the pre-declaration of a Selector object called selector is assumed. This class is easily the most commonly used and is the type that we shall be using. It has at its heart an array for storing the data and we can specify the size of this array via method allocate, a static method of each of the Buffer classes. The code below shows how this may be done. A 2 KB buffer allocation has been chosen for the example and the pre-declaration of a ByteBuffer called buffer has been assumed.

This attempts to allocate the required memory as direct memory, so that data does not need to be copied to an intermediate buffer before being written to disc. Whether the use of direct buffers is appropriate or desirable and there will be a cost associated with the use of them, in terms of system resources depends upon the needs of the particular application and the characteristics of the underlying operating system.

In practice, multiple buffers and multiple threads in thread pools will be needed for heavily used servers. Once all of the above preparatory steps have been executed, the server will enter a traditional do…while true loop that accepts connecting clients and pro- cesses their data.

The first step within this loop is a call to method select on the Selector object. This returns the number of events of the type s that are being moni- tored and have occurred.

This method is very efficient and appears to be based on the Unix system call of the same name. For each event that is detected on a particular call to select, an object of class SelectionKey package java. The set of SelectionKeys created by a given call to select is called the selected set. The selected set is generated by a call to method selectedKeys of the Selector object and is placed into a Java Set object. The lines to generate the selected set and its iterator are shown below.

As we retrieve each SelectionKey from the set, we need to typecast from type Object which is how each key is held within the Set object into type SelectionKey. Here is the code required for detection and retrieval of each key: while keyCycler. To find this out, we need to retrieve the set of ready operations for the cur- rent key by calling the SelectionKey method readyOps. This method returns the set of operations as a bit pattern held in an int.

By AND-ing this integer with specific SelectionKey operation constants, we can determine whether those particular events have been generated. For our program, of course, the only two event types of interest are SelectionKey.

If the former is detected, we shall process a new connection, whilst detection of the latter will lead to the pro- cessing of incoming data. The code for determination of event type and the initiation of processing but not the details of such processing just yet appears below. This is effected by calling method remove on the selected set, a reference to which may be obtained by calling method selectedKeys again. The remove method will have the SelectionKey as its single argument, of course: selector.

Buffer method clear the purpose of which is self-evident should be called before each fresh reading of data into the buffer from its associated channel. A reference to the channel is obtained by calling method channel on the current SelectionKey and again typecasting the Object reference that is returned.

The reading itself is carried out by method read of the SocketChannel class. This method takes the buffer as its single argument and returns an integer that indicates the number of bytes read.

In the example at the end of this section which will contain all the code accumulated within the section , the data received will simply be echoed back to the client. Since it may not be possible to send the entire contents of the buffer in one operation, a while loop will be used, with Buffer method remaining being called to determine whether there are any bytes still to be sent. Since an IOException may be generated, this code will need to be contained within a try block, but the basic code without the try is shown below.

Note also that reading and writing is specified with respect to the channel. It can be very easy at first viewing to interpret socketChannel. The link between client and server can break down, of course, possibly because the connection has been closed at the client end or possibly because of some error situation. Whatever the reason, this must be taken into account when attempting to read from the SocketChannel.

When this happens, the registration of the current SelectionKey with the Selector object must be rescinded. This is done by calling method cancel on the SelectionKey object. The socket associated with the client should also be closed. Before this can be done, it is necessary to get a reference to the Socket object by calling method socket on the SocketChannel object. The code for the equivalent client is not shown, since this of course will be identical to that shown for MultiEchoClient.

As before, the server simply echoes back all transmissions from the client s. ServerSocket will have a ServerSocketChannel only if latter is created first. They are certainly the only methods not already covered that will be needed for implementation of the chat server in Exercise 3.

In fact, of the six new methods mentioned below, only four are NIO meth- ods. The other two are methods of the String class. In all the examples within this section, buffer is assumed to be a pre-declared ByteBuffer. This is particularly so when the programmer wishes to place particular values into a buffer or to remove all or part of the data from the buffer in order to carry out further processing on that data possibly prior to re-writing the processed data back to the buffer. Sometimes, it is desirable to access the contents of this array directly.

Method array of class ByteBuffer allows us to do just this by returning the array of bytes holding the data. Obviously, we need to know how many bytes of data there are in the array. However, this will not work, since it will simply show the size that was allocated to the ByteBuffer by the programmer, not the number of bytes that have been used. Another method of the String class that can be very useful when processing data within a ByteBuffer does the opposite of the above.

Method getBytes converts a specified String into an array of bytes, which may then be writ- ten to the buffer. Examine the code and then compile and run the program, observing the results.

Note that it will NOT be the main class that implements the Runnable interface, but each of the two subsidiary classes. Using this file as a template, modify the code so that the program acts as a cli- ent of ResourceServer, as shown in the screenshots at the end of Sect.

Ensure that the user can pass only 0 or 1 to the server. Test the operation of the server with two clients. Note that exercises 3. Both server and client will need to be implemented and brief details of these programs are provided below.

The multithreaded chat server must broadcast each message it receives to all the connected clients, of course. It should also maintain a dynamic list of Socket references associated with those clients. Though you could use an array to hold the list with an appropriate over-allocation of array cells, to cater for a poten- tially large number of connections , the use of a Vector object would be much more realistic. If you are unfamiliar with Vectors, then refer to Sect. A separate thread will be required to receive messages from the server and add them cumulatively to a text area.

All other messages should be sent via a text area and associated button. As a simplification, assume that no two clients will select the same nickname. You can also now get rid of the code dealing with any NoSuchElementException. Signatures for the first and last of these are shown below. With all our programs so far, there has been a very fundamental limitation: all data accepted is held only for as long as the program remains active.

As soon as the program finishes execution, any data that has been entered and the results of processing such data are thrown away. Of course, for very many real-life applica- tions banking, stock control, financial accounting, etc. These applications demand persistent data storage. That is to say, data must be maintained in a permanent state, such that it is available for subsequent further processing.

The most common way of providing such persistent storage is to use disc files. Java provides such a facility, with the access to such files being either serial or random. The following sections explain the use of these two file access methods, firstly for non-GUI applications and later for GUI applications.

In addition, the important and often neglected topic of serialisation is covered. A sequential file is a serial file in which the data are stored in some particular order e. A sequential file is a serial file, but a serial file is not necessarily a sequential file. The internal structure of a serial file can be either binary i. The former stores data more efficiently, but the latter is much more convenient for human beings. Coverage here will be devoted exclusively to text files.

As of Java 5, we can often use just a File object for either input or output though not for both at the same time. The File constructor takes a String argument that specifies the name of the file as it appears in a directory listing. If a string literal is used e. In particular, it is common practice to denote text data files by a suffix of. Class File is contained within package java. Likewise, it was necessary to wrap a PrintWriter object around a FileWriter object in order to write to the file.

Now we can wrap a Scanner object around a File object for input and a PrintWriter object around a File object for output. The PrintWriter class is also within package java. Another point worth noting is that we may choose to create anonymous File objects, as in the examples above, or we may choose to create named File objects.

For example, we can test whether an input file actually exists. Programs that depend upon the existence of such a file in order to carry out their processing must use named File objects. When the processing of a file has been completed, the file should be closed via the close method, which is a member of both the Scanner class and the PrintWriter class. For example: input. Since file output is buffered, it is not until the output buffer is full that data will normally be written to disc.

If a program crash occurs, then any data still in the buffer will not have been written to disc. Consequently, it is good practice to close a file explicitly if you have finished writing to it or if your program does not need to write to the file for anything more than a very short amount of time. Closing the file causes the output buffer to be flushed and any data in the buffer to be written to disc.

No such precaution is rele- vant for a file used for input purposes only, of course. Note that we cannot move from reading mode to writing mode or vice versa without first closing our Scanner object or PrintWriter object and then opening a PrintWriter object or Scanner object respectively and associating it with the file. If the file already existed, its initial contents will have been overwritten.

This may or may not have been your intention, so take care! In addition, we may also wish to allow the user to enter a name for the file. The next example illustrates both of these features.

Since there may be a significant delay between consecutive file output operations while awaiting input from the user, it is good programming practice to use File method flush to empty the file output buffer. Remember that, if the program crashes and there is still data in the file output buf- fer, that data will be lost! When reading data from any text file, we should not depend upon being able to read a specific number of values, so we should read until the end of the file is reached.

Programming languages differ fundamentally in how they detect an end-of-file situ- ation. With some, a program crash will result if an attempt is made to read beyond the end of a file; with others, you must attempt to read beyond the end of the file in order for end-of-file to be detected. As of Java 5, we must not attempt to read beyond the end-of-file if we wish to avoid the generation of a NoSuchElementException.

Instead, we have to check ahead to see whether there is more data to be read. Note that File objects can refer to ordinary files or to directories. The following example illustrates the use of some of these methods. This can be extremely useful when debugging a pro- gram that requires anything more than a couple of items of data from the user.

Instead of re-entering the data each time we run the program, we simply create a text file holding our items of data on separate lines using a text editor or wordprocessor and then re-direct input to come from our text file.

This can save a great deal of time-consuming, tedious and error-prone re-entry of data when debugging a program. However, whenever it encounters a file input statement via Scanner method next, nextLine, nextInt, etc. We can use redirection of both input and output with the same program, as the example below shows.

These values are called command line parameters and are values that the program may make use of. Such values are received by method main as an array of Strings. If this argument is called arg [Singular used here, since individual elements of the array will now be referenced], then the elements may be referred to as arg[0], arg[1], arg[2], etc. Rather than prompting the user to enter the names of the files which would be perfectly feasible, of course , the program may allow the user to specify the names of the two files as command line parameters: java Copy source.

Method main would then access the file names through arg[0] and arg[1]: import java. However, they do have two distinct disadvantages, as noted below. In order to access a particular record, it is necessary to physically read past all the preceding records. For applications containing thousands of records, this is simply not feasible. The whole file would have to be re-created!

Random access files probably more meaningfully called direct access files overcome both of these problems, but do have some disadvantages of their own… i In common usage, all the logical records in a particular file must be of the same length. However, the speed and flexibility of random access files often greatly outweigh the above disadvantages.

Indeed, for many real-life applications, there is no realistic alternative to some form of direct access. To create a random access file in Java, we create a RandomAccessFile object. We do this by calling method seek, which requires a single argument specifying the byte position within the file. Note that the first byte in a file is byte 0. For example: ranFile. The second of these two factors will usually involve some kind of hashing func- tion that is applied to the key field.

However, we still need to calculate the record size. Obviously, we can decide upon the size of each String field ourselves. For numeric fields, though, the byte allocations are fixed by Java in a platform-independent fashion and are as shown below. In addition, it provides a method called writeChars for writing a variable- length string. When calculating the number of bytes for a String field, do not make the mistake of allocating only one byte per character.

Remember that Java is based on the unicode character set, in which each character occupies two bytes. This means that each surname will be allo- cated 30 i. Consequently, we shall store records starting at byte positions 0, 48, 96, etc. The formula for calculating the position of any record on the file is then: Record No. Then the code to locate the record with account number 5 is: ranAccts. Now for the code… import java. The following screenshot demonstrates the operation of this program Fig.

The next example shows how this can be done for our accounts file. Spelling Serialization] 4. Spelling Serialization] As seen in the preceding sections, transferring data of the primitive types to and from disc files is reasonably straightforward.

Transferring string data presents a little more of a challenge, but even this is not a particularly onerous task. However, how do we go about transferring objects of classes?

String is a class, of course, but it is treated rather differently from other classes. One way of saving an object to a file would be to decompose the object into its constituent fields strings and num- bers and write those individual data members to the file. Then, when reading the values back from the file, we could re-create the original objects by supplying those values to the appropriate constructors.

However, this is a rather tedious and long- winded method. In addition, since the data members of an object may themselves include other objects some of whose data members may include further objects, some of whose members… , this method would not be generally applicable.

Unlike other common O-O languages, Java provides an inbuilt solution: seriali- sation. Objects of any class that implements the Serializable interface may be trans- ferred to and from disc files as whole objects, with no need for decomposition of those objects. The Serializable interface is, in fact, nothing more than a marker to tell Java that objects of this class may be transferred on an object stream to and from files. Implementation of the Serializable interface need involve no implementation of methods.

Class ObjectOutputStream is used to save entire objects directly to disc, while class ObjectInputStream is used to read them back from disc. For output, we wrap an object of class ObjectOutputStream around an object of class FileOutputStream, which itself is wrapped around a File object or file name. Similarly, input requires us to wrap an ObjectInputStream object around a FileInputStream object, which in turn is wrapped around a File object or file name.

We could simply use a for loop to read back the number of objects we believe that the file holds, but this would be very bad practice in general especially as we may often not know how many objects a particular file holds.

The only viable option there appears to be is to catch the EOFException that is generated when we read past the end of the file. This author feels rather uneasy about having to use this technique, since it conflicts with the fundamental ethos of exception handling.

Exception handling as the term implies is designed to cater for exceptional and erroneous situations that we do not expect to happen if all goes well and processing proceeds as planned. Here, however, we are going to be using exception handling to detect something that we not only know will happen eventu- ally, but also are dependent upon happening if processing is to reach a successful conclusion.

Unfortunately, there does not appear to be any alternative to this technique. Example This example creates three objects of a class called Personnel and writes them to disc file as objects. We must, of course, ensure that class Personnel implements the Serializable interface which involves nothing more than including the phrase implements Serializable. In a real- life application, class Personnel would be defined in a separate file, but it has been included in the main application file below simply for convenience.

Spelling Serialization] outStream. The reader will almost certainly have used file handling applications that provide such an interface. By employ- ing Swing class JFileChooser, we can display a dialogue box that will allow the user to do just that.

Each of these methods takes a single argument and returns an integer result. For example: fileChooser. If null is passed as an argument, then the dialogue box appears in the centre of the screen.

The integer value returned may be compared with either of the following inbuilt constants: JFileChooser. If a file has been selected, then method getSelectedFile returns the corresponding File object. Of course, since we are now dealing with a GUI, we need to implement ActionListener, in order to process our button selections.

Example This example is a simple application for reading a file of examination marks and displaying the results of individual students, one at a time. Once a file has been selected, our program will open the file, read the first logical record and display its contents within the text fields of a panel we have set up.

This panel will also contain two buttons, one to allow the user to open another file and the other to allow the user to move on to the next record in the file. Before looking at the code, it is probably useful to look ahead and see what the intended output should look like.

In order that the JFileChooser object may be viewed as well, the screenshot in Fig. If the reader wishes to create a serial file for testing this program, this may be done very easily by using any text editor to enter the required three fields for each of a series of students each field being followed by a carriage return.

Consequently, any exceptions that do arise must be handled explicitly either by these methods themselves or by methods called by them as with method closeFile. This restriction can be circumvented by specifying the base type to be Object, the ultimate superclass, if a heterogeneous collection of objects is truly required.

Class ArrayList is contained within package java. Constructor overloading allows us to specify the initial size if we wish, but the simplest form of the constructor takes no arguments and assumes an initial capacity of ten.

Since the elements of an ArrayList and of any other collection class are stored as Object references i. An object may be added at a specific position within an ArrayList via an over- loaded form of the add method that takes two arguments, the first of which specifies the position at which the element is to be added.

Example nameList. Placing a series of objects into a single ArrayList is a very neat way of packaging and transferring our objects. Without this, we have the considerable disadvantage of being restricted to serial access only. Example This example creates three objects of class Personnel as featured in the example at the end of Sect. We could use the same ArrayList object for sending objects out to the file and for receiving them back from the file, but two ArrayList objects have been used below simply to demonstrate beyond any doubt that the values have been read back in and are not simply the original values, still held in the ArrayList object.

The only difference is that, instead of sending a series of strings from the server to the client s , we shall now be passing an ArrayList. Consequently, we shall not be making use of a PrintWriter object in our server. Instead, we shall need to create an ObjectOutputStream object. Example Suppose that the Socket object is called socket and the output object is called out.

The code for the server PersonnelServer. You will find that the code for the server is an amalgamation of the first half of MessageServer. As with earlier cases, this example is unrealistically simple, but serves to illus- trate all the required steps of a socket-based client—server application for transmit- ting whole objects, without overwhelming the reader with unnecessary detail.

Simply ignore this warning. Serializable in the header line since it is no longer subject to the import of package java. Serializable Figure 4. The only significant difference is that, instead of using method get to retrieve an object from a Vector, we need to use method elementAt.

The answer is… it depends! The ArrayList is faster, but is not thread- safe, whereas the Vector is thread-safe. Consequently, if thread-safety is important to your program, you should use a Vector. If, on the other hand, thread-safety is not a factor, then you should use an ArrayList. Then use redirection to feed the values from your payroll text file into your program displaying the contents as before.

For example: type names. For example: Smith 47 Jones 63 … … By extending the code given below, create a random access file called results. Each record should comprise a student surname and examination mark. When all records have been written, reposition the file pointer to the start of the file and then read each record in turn, displaying its contents on the screen.

When displaying the names and marks that have been read, of course, you must make use of the methods of class Result.

Once again, redirect initial input to come from your text file. Within the constructor for the class, declare and initialise an array of three Personnel objects as in program ArrayListSerialise. The name and location of the file should be chosen by the user via a JFileChooser object. Note that you will need to close down the empty application window by clicking on the window close box. Use the JFileChooser object to read from the file created above and get your program to use method getSurname of class Personnel to display the surnames of all the staff whose details were saved.

This service is more advanced with JavaScript available. Authors view affiliations Jan Graba. Revised third edition has been fully updated to incorporate Java 7 compatibility Includes many end-of-chapter exercises Features numerous code examples Includes supplementary material: sn. Front Matter Pages i-xii. Basic Concepts, Protocols and Terminology. Pages Starting Network Programming in Java.



0コメント

  • 1000 / 1000