JaVa
   

Creating a Blocking Server

Let's now look at a simple blocking server using NIO; when a connection is made, it will send a predefined string of text to the client. Here is the complete code for the server and the client:

Code Listing 18-2: BlockingServer.java
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
import java.net.*;
public class BlockingServer
{
 public static void main(String[] args)
 {
 try
 {
 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 ServerSocket serverSocket = serverSocketChannel.socket();
 serverSocket.bind(new InetSocketAddress(9000));
 while(true)
 {
 System.out.println("Awaiting Client Connection...");
 SocketChannel socketChannel = serverSocketChannel.accept();
 System.out.println("Got Client Connection...");
 // Create a byte buffer...
 String stringToSend = "This is a message";
 int length = stringToSend.length() * 2; // * 2 due to size of each char
 ByteBuffer lengthInBytes = ByteBuffer.allocate(4);
 // 4 = size of an 'int'
 lengthInBytes.putInt(length);
 lengthInBytes.rewind();
 ByteBuffer dataToSend = ByteBuffer.allocate(length);
 dataToSend.asCharBuffer().put(stringToSend);
 ByteBuffer sendArray[] = {lengthInBytes, dataToSend};
 socketChannel.write(sendArray);
 System.out.println("Sent Message to Client...");
 }
 }
 catch(IOException e)
 {
 System.out.println(e);
 }
 }
}



Java End example
Code Listing 18-3: BlockingClient.java
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
import java.net.*;
public class BlockingClient
{
 public static void main(String[] args)
 {
 try
 {
 SocketChannel socketChannel = SocketChannel.open();
 socketChannel.connect(new InetSocketAddress
 ("127.0.0.1", 9000));
 // wait for the message from the server...
 ByteBuffer incomingLengthInBytes = ByteBuffer.allocate(4);
 // size of an 'int'
 socketChannel.read(incomingLengthInBytes);
 incomingLengthInBytes.rewind();
 int incomingLength = incomingLengthInBytes.getInt();
 System.out.println("Got Incoming Length as:
 "+incomingLength+" bytes, "+incomingLength+" / "+2+" = "+incomingLength/2+" characters");
 // now allocate the correct size for the message...
 ByteBuffer incomingData = ByteBuffer.allocate(incomingLength);
 socketChannel.read(incomingData);
 incomingData.rewind();
 String string = incomingData.asCharBuffer().toString();
 // Finally print the received message...
 System.out.println("Received: "+string);
 }
 catch(IOException e)
 {
 System.out.println(e);
 }
 } }


Java End example

When we run the server and then a client, we should see the following output:

Java Click To expand
Screenshot-2: Blocking server (after a client has connected) Java Click To expand
Screenshot-3: Blocking client

So it works, but let's see how. First we will look at the server. We import all the required packages and declare our class and main method. Then we open a ServerSocketChannel by calling the static open method, which is a member of the ServerSocketChannel class. This can be seen in the following line of code:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();


Then we obtain the ServerSocket from the channel by calling the socket method, which can be seen in the following line of code:

ServerSocket serverSocket = serverSocketChannel.socket();


Next we bind the socket to port 9000, so the server can begin listening on that port. This can be seen here:

serverSocket.bind(new InetSocketAddress(9000));


Now we enter an infinite while loop and call the accept method of the serverSocketChannel, which will block until a connection is accepted. This is on par with the networking that we saw in the last chapter, which also blocked waiting for a connection. This can be seen here:

while(true)
{
 SocketChannel socketChannel = serverSocketChannel.accept();


So when a connection is accepted, it is then stored in the socketChannel reference. We next create a String object, which we are going to send to the client that has just connected. This can be seen in the following line of code:

String stringToSend = "This is a message";


Once we have assigned this string value, we then retrieve its length in bytes by multiplying the actual length of the string by two, as each (Unicode) character takes up two bytes in memory. We do this and then store the result in a variable called length.

int length = stringToSend.length() * 2;


Now we create a ByteBuffer to hold the size of the message that we are going to send to the client. The size will be stored as an integer within the ByteBuffer, so we need to allocate four bytes to it to hold the integer value. This can be seen in the following line of code:

ByteBuffer lengthInBytes = ByteBuffer.allocate(4);


We then place the length of the string in bytes into the lengthInBytes ByteBuffer by means of the putInt method that we saw in the previous example when we looked at ByteBuffers. Then, after we have placed the length value in the lengthInBytes ByteBuffer, we rewind the ByteBuffer so we are ready to send it. Next we allocate space to our data ByteBuffer, which will be used to hold the actual data that we wish to send to the client (i.e., the actual string data). This will be the length of our string multiplied by two to take into account the size of the char data type (which we have previously stored in the variable length). This can be seen in the following line of code:

ByteBuffer dataToSend = ByteBuffer.allocate(length);


Next we place the string into the dataToSend ByteBuffer using the following line of code:

dataToSend.asCharBuffer().put(stringToSend);


We place the string in the ByteBuffer by first viewing it as a CharBuffer, which then has the put method that takes a string as a parameter. Next we create an array to store the ByteBuffers that we are sending. This can be seen here:

ByteBuffer sendArray[] = {lengthInBytes, dataToSend};


Finally we send the ByteBuffer array using the following line of code:

socketChannel.write(sendArray);


Note that this could also be written as:

socketChannel.write(lengthInBytes);
socketChannel.write(dataToSend);


It really makes no difference which way you do it; it's just a little neater sending an array if you have many ByteBuffers to send at once. Well, that's the server; let's now take a look at the client code. In the client, we first open a SocketChannel by calling the static open method of the SocketChannel class, which can be seen in the following line of code:

SocketChannel socketChannel = SocketChannel.open();


Then we attempt to connect to the server by calling the connect method, which in turn is passed an InetSocketAddress that is passed the IP address and port of the server. This can be seen here:

socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000));


Once we have a connection, we then allocate a ByteBuffer called incomingLengthInBytes to hold four bytes storing the size of the string message that we need to read in next; this is because we do not know what length the data will be, as it could be a string of any number of characters.

ByteBuffer incomingLengthInBytes = ByteBuffer.allocate(4);


Next we call the read method of the socketChannel, which will actually read the four bytes into the incomingLengthInBytes ByteBuffer. This can be seen here:

socketChannel.read(incomingLengthInBytes);


Once it is read in, we then need to rewind the ByteBuffer using the following line of code:

incomingLengthInBytes.rewind();


Then we can call the getInt method to get the length of the string message that is about to be read in. This can be seen in the following line of code:

int incomingLength = incomingLengthInBytes.getInt();


So now that we know how much memory to allocate, we allocate it using the following line of code:

ByteBuffer incomingData = ByteBuffer.allocate(incomingLength);


We then proceed by calling the read method to read in the data:

socketChannel.read(incomingData);


Then, as with all ByteBuffers, we need to rewind it before we can access the string contained within it. This can be seen here:

incomingData.rewind();


Then finally, we can get the string out of the ByteBuffer and print it to the console window using the following two final lines of code:

String string = incomingData.asCharBuffer().toString();
System.out.println("Received: "+string);


JaVa
   
Comments