Recently, I plan to deepen my knowledge of Java network programming (IO, NIO, Socket programming, Netty).

Java I/O streams

Java Network Programming

Java NIO: Buffers

Java NIO: Channels

Java NIO: selector

Java network programming mainly involves the use of sockets and ServerSockets

TCP protocol built on the network program

TCP features

  • TCP is a connection-oriented protocol. A connection must be established before communication

  • To provide reliable transmission, data transmitted over TCP is error-free, not lost, not duplicated, and arrives in sequence

  • Byte stream oriented (although the application interacts with TCP one data block at a time, TCP treats the data handed over by the application as just a series of unstructured byte streams)

  • Point-to-point full duplex communication

  • Congestion control & sliding Windows

When we use Java to build tcp-based network programs, we mainly care about two classes: client Socket and server ServerSocket

The client SOCKET

The life cycle of using client-side sockets: connect to remote servers –> send data, receive data… –> Close the connection

Connecting to the Remote Server

Join by constructor

If the connection fails, IOException or UnkonwnHostException will be thrown

public Socket(String host, int port)
public Socket(String host, int port, InetAddress localAddr,int localPort)
Copy the code

Manual connection

When using the no-argument constructor, connect needs to be manually called before communication (SOCKET options can also be set).

Socket so = new Socket();
SocketAddress address = new InetSocketAddress("www.baidu.com".80);
so.connect(address);
Copy the code

Send and receive data

Java I/O is built on streams, with input streams for reading data and output streams for writing data

The next code connects to the server on port 7001, reads a line of data and writes it back to the server.

 try (Socket so = new Socket("127.0.0.1".7001)) {
     BufferedReader reader = new BufferedReader(new InputStreamReader(so.getInputStream()));
     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(so.getOutputStream()));
     //read message from server
     String recvMsg = reader.readLine();
     //write back to sever.
     writer.write(recvMsg);
     writer.newLine();
     writer.flush();
  } catch (IOException e) {
     //ignore
  }
Copy the code

Big end model

In big-endian mode, the high bytes of data are stored in a low address in memory.

Close the connection

The Socket object must be closed after use to free up underlying system resources

The finally block closes the connection

Socket so = null;
try {
  so = new Socket("127.0.0.1".7001);
  //
}catch (Exception e){
	//
}finally {
  if(so ! =null) {try {
      so.close();
    } catch (IOException e) {
      //}}}Copy the code

The Try with resource syntax closes the connection automatically

Socket objects defined in the try block (and other objects that implement AutoCloseable) are automatically closed by Java

// The Socket object defined in the try (or any other object implementing AutoCloseable) is automatically closed by Java
try (Socket so = new Socket("127.0.0.1".7001)) {
		//do something
} catch(Exception e){
		//
}
Copy the code

The service side ServerSocket

Life cycle of using ServerSocket: Bind local port (service startup) –> listen for client connections –> Accept client connections –> communicate with clients through this client connection –> Listen for client connections –>….. (loop) –> Close the server

Binding a Local Port

Specify ports directly in the constructor to complete or manually bind

// The port specified in the constructor completes the binding
ServerSokect ss = new  ServerSocket(7001);

// Manually call the bind function to complete the binding
ServerSokect ss = new  ServerSocket();
ss.bind(new InetSocketAddress(7001));
Copy the code

Accept client connections

The Accept method returns a Socket object that represents a connection to the client

 ServerSokect ss = new ServerSocket(7001);  
 while(true) {// Block waiting for the connection to be established
 		Socket so = ss.accept();
    // do something.
 }
Copy the code

Communicates with the client

Through the Socket object after the connection is established, open the input stream and output stream to communicate with the client

Shut down the server

The same way client sockets are closed

Demo

The next piece of code the server sends one line of data to the client when the connection is established, and then reads another line of data returned by the client and compares whether the two lines are the same.

** The main thread only accepts client connections, and once the connection is established, communication with the client is done in a thread pool **

public class BaseServer {

    private static final String MESSAGE = "hello, i am server";
    private static ExecutorService threads = Executors.newFixedThreadPool(6);

    public static void main(String[] args) {
       // Try with resource to bind the local port
        try (ServerSocket socket = new ServerSocket(7001)) {
            while (true) {
              	// Accept client connections
                Socket so = socket.accept();
              	// The work of communicating with the client is put into the thread pool and executed asynchronouslythreads.submit(() -> handle(so)); }}catch (IOException e) {
            //}}public static void handle(Socket so) {
       // Try with resource opens the input/output stream
        try (InputStream in = so.getInputStream(); OutputStream out = so.getOutputStream()) {
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "utf-8"));
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            //send data to client.
            writer.write(MESSAGE);
            writer.newLine();
            writer.flush();

            //recv data from client.
            String clientResp = reader.readLine();
            System.out.println(MESSAGE.equals(clientResp));
        } catch (Exception e) {
            //ignore
        }finally {
          / / close the socket
            if(so ! =null) {try {
                    so.close();
                } catch (IOException e) {
                    //
                }
            }
        }
    }
}
Copy the code

The Socket option

TCP_NODELAY

By default TCP buffering is turned on, small packets are combined into larger packets before being sent, and the localhost waits for confirmation of the previous packet — the Nagle algorithm — before sending another packet

However, this buffering mode can cause some applications to respond too slowly (such as a simple typing program)

Tcp_nodelay set to true disables TCP buffering and all packets are sent as soon as they are ready

 public void setTcpNoDelay(boolean on) 
Copy the code

SO_LINGER(linger is slow to disappear, linger)

The so_linger option specifies how to handle unsent datagrams when the socket is closed. By default, the close() method returns immediately, but the system still sends the data

When Linger is set to 0, all unsent data is discarded when the socket is closed

If SO_LINGER is on and LINGER is positive, close() blocks for the specified number of seconds, waiting for data to be sent and confirmation to be received until the specified number of seconds has passed.

public void setSoLinger(boolean on, int linger)
Copy the code

SO_TIMEOUT

By default, when trying to read data from the socket, read() blocks for as long as possible to get enough bytes

So_timeout is used to set the duration of the block and throws an InterruptedException when the time expires.

public synchronized void setSoTimeout(int timeout)// milliseconds, default is 0 always blocking
Copy the code

SO_KEEPLIVE

After so_keeplive is enabled, the client sends a packet to the server periodically to ensure that the connection to the server is normal (heartbeat mechanism provided by TCP layer).

public void setKeepAlive(boolean on)
Copy the code

The SO_RCVBUF and SO_SNDBUF

Set TCP receive and send buffer sizes (kernel-level buffer sizes)

When transferring large blocks of data (HTTP, FTP), you can benefit from large buffers; Large buffers are not helpful for small data transfers in interactive sessions (Telnet and many games)

Maximum buffer size = bandwidth x delay (if the bandwidth is 2Mb/s and the delay is 500ms, the maximum buffer size is about 128KB)

Increase the buffer size appropriately if the application is not utilizing the full bandwidth, or decrease the buffer size if packet loss and congestion occur

UDP protocol to build network procedures

Features of UDP

  • No connection. There is no need to establish a connection before sending data, which saves the overhead of establishing a connection

  • Try your best to deliver. Datagrams may be lost and arrive out of order

  • Packet oriented (UDP does not merge or split the packets transmitted by the application layer, but retains the boundaries of the packets)

  • UDP has no congestion control

  • UDP supports one-to-one, one-to-many, many-to-one, and many-to-many interactive communication

  • The header of UDP has a small overhead of only 8 bytes, which is shorter than TCP’s 20-byte header.

    Build UDP protocol network procedures, we relationship DatagramSocket and DatagramPacket two classes

The datagram

UDP is packet transmission oriented and does not merge or split the packets handed over by the application layer. (TCP has the problem of unpacking and sticking packets.)

Datagrams care about two things: the underlying byte array that stores the message and the address of the communication peer (peer host and port)

// Send datagram Specifies the data to be sent and the peer address
DatagramPacket sendPacket = new DatagramPacket(new byte[0].0, InetAddress.getByName("127.0.0.1"), 7002);

To accept datagrams, you only need to specify the underlying byte array and its size
DatagramPacket recvPacket = new DatagramPacket(new byte[1024].1024);
Copy the code

UDP client

Since UDP is connectionless, only the local port needs to be specified when constructing DatagramSocket, and the remote host and port need not be specified

The host and port of the remote host are specified in the datagram, so UDP can implement one-to-one, one-to-many, and many-to-many transmission

 try (DatagramSocket so = new DatagramSocket(0)) {
   // Specify the peer address in the datagram (server address)
   DatagramPacket sendPacket = new DatagramPacket(new byte[0].0,
                                                  InetAddress.getByName("127.0.0.1"), 7002);
   // Send datagrams
   so.send(sendPacket);

   // Block receiving datagrams
   DatagramPacket recvPacket = new DatagramPacket(new byte[1024].1024);
   so.receive(recvPacket);
   // Prints the data returned by the peer
   System.out.println(new String(recvPacket.getData(), 0, recvPacket.getLength()));
 } catch (Exception e) {
   e.printStackTrace();
 }
Copy the code

UDP server.

The UDP server uses the same DatagramSocket as the client, but the local port of the packet needs to display the declaration

The following UDP server program accepts the client’s message, retrieves the requested host and port from the message, and returns the fixed data content “received”

byte[] data = "received".getBytes();
try (DatagramSocket so = new DatagramSocket(7002)) {
  while (true) {
    try {
      DatagramPacket recvPacket = new DatagramPacket(new byte[1024].1024);
      so.receive(recvPacket);
      DatagramPacket sendPacket = new DatagramPacket(data, data.length,
                                                     recvPacket.getAddress(), recvPacket.getPort());
      so.send(sendPacket);
    } catch (Exception e) {
      //}}}catch (SocketException e) {
  //
}
Copy the code

The connection

UDP is connectionless, but the DatagramSocket provides the connection function to restrict the communication to the other end (not really the connection).

After the connection, only the specified host and port can send datagrams. Otherwise, an exception will be thrown.

After the connection, only the datagrams sent by the specified host and port can be received. Other datagrams are discarded.

 public void connect(InetAddress address, int port)
 public void disconnect(a) 
Copy the code

conclusion

In Java, TCP programming depends on Socket and ServerSocket, while UDP programming depends on DatagramSocket and DatagramPacket