Application analysis of network communication based on Java review

  • 2020-04-01 01:54:02
  • OfStack

A TCP connection

The foundation of TCP is the Socket, in the TCP connection, we will use the ServerSocket and Socket, after the client and the server establish a connection, the remaining is basically the control of I/O.

Let's start with a simple TCP communication, which is divided into client side and server side.

The client code is as follows:


 simple TCP The client  
 import java.net.*;
 import java.io.*;
 public class SimpleTcpClient {

     public static void main(String[] args) throws IOException
     {
         Socket socket = null;
         BufferedReader br = null;
         PrintWriter pw = null;
         BufferedReader brTemp = null;
         try
         {
             socket = new Socket(InetAddress.getLocalHost(), 5678);
             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             pw = new PrintWriter(socket.getOutputStream());
             brTemp = new BufferedReader(new InputStreamReader(System.in));
             while(true)
             {
                 String line = brTemp.readLine();
                 pw.println(line);
                 pw.flush();
                 if (line.equals("end")) break;
                 System.out.println(br.readLine());
             }
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
         finally
         {
             if (socket != null) socket.close();
             if (br != null) br.close();
             if (brTemp != null) brTemp.close();
             if (pw != null) pw.close();
         }
     }
 }

The server-side code is as follows:

 Simple version TCP The server side 
 import java.net.*;
 import java.io.*;
 public class SimpleTcpServer {

     public static void main(String[] args) throws IOException
     {
         ServerSocket server = null;
         Socket client = null;
         BufferedReader br = null;
         PrintWriter pw = null;
         try
         {
             server = new ServerSocket(5678);
             client = server.accept();
             br = new BufferedReader(new InputStreamReader(client.getInputStream()));
             pw = new PrintWriter(client.getOutputStream());
             while(true)
             {
                 String line = br.readLine();
                 pw.println("Response:" + line);
                 pw.flush();
                 if (line.equals("end")) break;
             }
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
         finally
         {
             if (server != null) server.close();
             if (client != null) client.close();
             if (br != null) br.close();
             if (pw != null) pw.close();
         }
     }
 }

The function of the server here is very simple, it receives the message from the client, and then returns the message "intact" to the client. When the client sends "end", the communication ends.

The above code basically draw the outline of the TCP communications process, the client and the server main frame, we can find that the code above, the server at any time, can only handle a request from the client, it is a serial process, not parallel, this and our impression of the server processing way is not the same, we can for the server to add multi-threaded, when a client request to enter, we will create a thread, to deal with the corresponding request.

The improved server-side code is as follows:


 Multithreaded version TCP The server side 
 import java.net.*;
 import java.io.*;
 public class SmartTcpServer {
     public static void main(String[] args) throws IOException
     {
         ServerSocket server = new ServerSocket(5678);
         while(true)
         {
             Socket client = server.accept();
             Thread thread = new ServerThread(client);
             thread.start();
         }
     }
 }

 class ServerThread extends Thread
 {
     private Socket socket = null;

     public ServerThread(Socket socket)
     {
         this.socket = socket;
     }

     public void run() {
         BufferedReader br = null;
         PrintWriter pw = null;
         try
         {
             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             pw = new PrintWriter(socket.getOutputStream());
             while(true)
             {
                 String line = br.readLine();
                 pw.println("Response:" + line);
                 pw.flush();
                 if (line.equals("end")) break;
             }
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
         finally
         {
             if (socket != null)
                 try {
                     socket.close();
                 } catch (IOException e1) {
                     e1.printStackTrace();
                 }
             if (br != null)
                 try {
                     br.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             if (pw != null) pw.close();
         }
     }
 }

The modified server side can handle multiple requests from the client side at the same time.

In the process of programming, we will have the concept of "resource", for example, database connection is a typical resource, in order to improve performance, we usually do not directly destroy the database connection, but use the database connection pool to manage multiple database connections, has realized the purpose of reuse. It is also a resource for Socket connections, and when our program needs a large number of Socket connections, it would be very inefficient to re-establish each connection.

And database connection pool are similar, we can also design a TCP connection pool, the idea here is that we use an array to maintain multiple Socket connection, another state array to describe each Socket connection is being used, when the program needs a Socket connection, we traverse the state array, take out is not the first used Socket connection, if all the connections are in use, throw an exception. This is an intuitive and simple "scheduling policy," and many open source or commercial frameworks (Apache/Tomcat) have similar "resource pools."

The code of TCP connection pool is as follows:


 A simple TCP The connection pool 
 import java.net.*;
 import java.io.*;
 public class TcpConnectionPool {

     private InetAddress address = null;
     private int port;
     private Socket[] arrSockets = null;
     private boolean[] arrStatus = null;
     private int count;

     public TcpConnectionPool(InetAddress address, int port, int count)
     {
         this.address = address;
         this.port = port;
         this .count = count;
         arrSockets = new Socket[count];
         arrStatus = new boolean[count];

         init();
     }

     private void init()
     {
         try
         {
             for (int i = 0; i < count; i++)
             {
                 arrSockets[i] = new Socket(address.getHostAddress(), port);
                 arrStatus[i] = false;
             }
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
     }

     public Socket getConnection()
     {
         if (arrSockets == null) init();
         int i = 0;
         for(i = 0; i < count; i++)
         {
             if (arrStatus[i] == false) 
             {
                 arrStatus[i] = true;
                 break;
             }
         }
         if (i == count) throw new RuntimeException("have no connection availiable for now.");

         return arrSockets[i];
     }

     public void releaseConnection(Socket socket)
     {
         if (arrSockets == null) init();
         for (int i = 0; i < count; i++)
         {
             if (arrSockets[i] == socket)
             {
                 arrStatus[i] = false;
                 break;
             }
         }
     }

     public void reBuild()
     {
         init();
     }

     public void destory()
     {
         if (arrSockets == null) return;

         for(int i = 0; i < count; i++)
         {
             try
             {
                 arrSockets[i].close();
             }
             catch(Exception ex)
             {
                 System.err.println(ex.getMessage());
                 continue;
             }
         }
     }
 }

UDP connections.

UDP is a different kind of connection from TCP. It is usually used in situations where real-time requirement is high and alignment determination is not high, such as online video. UDP will have "packet loss", in TCP, if the Server is not started, the Client will send a message, will report an exception, but for UDP, there is no exception.

UDP communication USES two classes, DatagramSocket and DatagramPacket, the latter holding the contents of the communication.

The following is a simple UDP communication example. Like TCP, it is also divided into two parts: Client and Server. The Client side code is as follows:


UDP Communication client 
 import java.net.*;
 import java.io.*;
 public class UdpClient {

     public static void main(String[] args)
     {
         try
         {
             InetAddress host = InetAddress.getLocalHost();
             int port = 5678;
             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             while(true)
             {
                 String line = br.readLine();
                 byte[] message = line.getBytes();
                 DatagramPacket packet = new DatagramPacket(message, message.length, host, port);
                 DatagramSocket socket = new DatagramSocket();
                 socket.send(packet);
                 socket.close();
                 if (line.equals("end")) break;
             }
             br.close();
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
     }
 }

The Server code is as follows:

UDP Communication server 
 import java.net.*;
 import java.io.*;
 public class UdpServer {

     public static void main(String[] args)
     {
         try
         {
             int port = 5678;
             DatagramSocket dsSocket = new DatagramSocket(port);
             byte[] buffer = new byte[1024];
             DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
             while(true)
             {
                 dsSocket.receive(packet);
                 String message = new String(buffer, 0, packet.getLength());
                 System.out.println(packet.getAddress().getHostName() + ":" + message);
                 if (message.equals("end")) break;
                 packet.setLength(buffer.length);
             }
             dsSocket.close();
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
     }
 }

Here, we also assume that, just like TCP, when the Client sends an "end" message, the communication is considered to be over. However, such a design is not necessary, and the Client side can be disconnected at any time without any need to care about the state of the Server side.
Multicast

Multicast is similar to UDP in that it USES class D IP addresses, which range from 224.0.0.0 to 239.255.255.255, not 224.0.0.0, and the standard UDP port number.

The class that multicast will use is MulticastSocket, which has two methods to focus on: joinGroup and leaveGroup.

The following is an example of multicast. The Client side code is as follows:


 Multicast communication client 
 import java.net.*;
 import java.io.*;
 public class MulticastClient {

     public static void main(String[] args)
     {
         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
         try
         {
             InetAddress address = InetAddress.getByName("230.0.0.1");
             int port = 5678;
             while(true)
             {
                 String line = br.readLine();
                 byte[] message = line.getBytes();
                 DatagramPacket packet = new DatagramPacket(message, message.length, address, port);
                 MulticastSocket multicastSocket = new MulticastSocket();
                 multicastSocket.send(packet);
                 if (line.equals("end")) break;
             }
             br.close();
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
     }
 }

The server-side code is as follows:

 Multicast communication server side 
 import java.net.*;
 import java.io.*;
 public class MulticastServer {

     public static void main(String[] args)
     {
         int port = 5678;
         try
         {
             MulticastSocket multicastSocket = new MulticastSocket(port);
             InetAddress address = InetAddress.getByName("230.0.0.1");
             multicastSocket.joinGroup(address);
             byte[] buffer = new byte[1024];
             DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
             while(true)
             {
                 multicastSocket.receive(packet);
                 String message = new String(buffer, packet.getLength());
                 System.out.println(packet.getAddress().getHostName() + ":" + message);
                 if (message.equals("end")) break;
                 packet.setLength(buffer.length);
             }
             multicastSocket.close();
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
     }
 }

NIO of New IO.

NIO is a new IO API introduced in JDK1.4 with new designs for buffer management, network communications, file access, and character set operations. For network communications, NIO USES the concepts of buffers and channels.

Here is an example of NIO that is quite different from the code style we mentioned above.


NIO example 
 import java.io.*;
 import java.nio.*;
 import java.nio.channels.*;
 import java.nio.charset.*;
 import java.net.*;
 public class NewIOSample {

     public static void main(String[] args)
     {
         String host="127.0.0.1";
         int port = 5678;
         SocketChannel channel = null;
         try
         {
             InetSocketAddress address = new InetSocketAddress(host,port);
             Charset charset = Charset.forName("UTF-8");
             CharsetDecoder decoder = charset.newDecoder();
             CharsetEncoder encoder = charset.newEncoder();

             ByteBuffer buffer = ByteBuffer.allocate(1024);
             CharBuffer charBuffer = CharBuffer.allocate(1024);

             channel = SocketChannel.open();
             channel.connect(address);

             String request = "GET / rnrn";
             channel.write(encoder.encode(CharBuffer.wrap(request)));

             while((channel.read(buffer)) != -1)
             {
                 buffer.flip();
                 decoder.decode(buffer, charBuffer, false);
                 charBuffer.flip();
                 System.out.println(charBuffer);
                 buffer.clear();
                 charBuffer.clear();
             }
         }
         catch(Exception ex)
         {
             System.err.println(ex.getMessage());
         }
         finally
         {
             if (channel != null)
                 try {
                     channel.close();
                 } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
         }
     }
 }

The code above attempts to access a local url and then prints out its contents.


Related articles: