Java network programming (Socket, ServerSocket, DatagramPackage, DatagramSocket)

Directory

1. Network programming

2. Echo model (the server communicates with the client)

3. BIO processing model (realize multi-user access to the same server)

4. UDP program


1. Network programming

There are two communication models

C/S (Client/Server) is based on the client and the server, and the client and the server need to be implemented when implementing the code

B/S (Browser/Server) is based on Http web page and server side, only need to implement server side when implementing

2.Echo model (server and client communication)

The so-called Echo model refers to that the client sends a message to the server, the server receives the message and then sends the information received by the client back to the client. So you need to write two ports, one client and one server. Java can use the Socket class to implement the client (client) and ServerSocket class (server)

Both Socket and ServerSocket are based on TCP (connected, reliable transmission service)

Common methods of the Socket class (client):

Method Description
public Socket(String host , int port) Create a socket, connect to the specified host name and port number
public OutputStream getOutputStream() Returns the output stream of this socket, used to send data to the server, generally use PrintStream
public InputStream getInputStream () Returns the input stream of this socket for receiving data from the server

Common methods of the ServerSocket class (server side):

Method Description
ServerSocket(int port) Create a server socket, bind to the specified port number
Socket accept() Monitor and accept the connection request from the client, and return a new Socket object to communicate with the client
void close() Close the server socket and stop listening to client connection requests

Echo model implementation case code:
1. Server-side code implementation:

package Example1903;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;

//Service-Terminal
public class javaDemo {
    public static void main(String[] args) throws Exception{
        Date date = new Date();
        ServerSocket server = new ServerSocket(9999);
        System.out.println("Waiting for the client to run --------");
        Socket client = server. accept();

// Set the input and output stream of the server to ensure that it can receive information and input information
        Scanner scanner = new Scanner(client. getInputStream());
        PrintStream out = new PrintStream(client. getOutputStream());

// Receive information and pass information to out output stream
        boolean flag = true;
        scanner.useDelimiter("\\
");
        while (flag){
            if (scanner. hasNext()){
                String value = scanner. next(). trim();
                if ("byebye".equalsIgnoreCase(value)){
                    out.println("ByeBye");
                    flag = false;
                }else out.println(date + "[server] sent message:" + value);
            }
        }
// close all services
        server. close();
        client. close();
        scanner. close();
        out. close();
    }
}

2. Client code implementation:

package Example1904;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;

//client
public class javaDemo {
// input information via keyboard
    private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in));
    public static String getString(String promp) throws Exception{
        System.out.print(promp);
        String str = KEYBOARD_INPUT. readLine();
        return str;
    }
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        Socket client = new Socket("localhost",9999);

// Get the same input and output objects
        Scanner scanner = new Scanner(client. getInputStream());
        PrintStream out = new PrintStream(client. getOutputStream());

        scanner.useDelimiter("\\
");//use delimiter
        boolean flag = true;
        while (flag){
            String input = getString( date + "[Client] Please enter the information to be sent:").trim();
            out.println(input);//Put the input content into the PrintStream stream
            if (scanner. hasNext()){
                System.out.println(scanner.next());
            }
            if ("byebye".equalsIgnoreCase(input)){
                flag = false;
            }
        }
    }
}

Server-side running results

Client running results:

Code flow:

  1. The server starts and waits for connections: After the server starts, listen to the specified port (here 9999) through ServerSocket, and call the accept method to wait for the client to connect. Once a client is successfully connected, the server creates a new thread to handle communication with the client.

  2. The client connects to the server: the client connects to the specified server address (here localhost) and port number (here it is 9999) through the Socket constructor code>).

  3. The client sends a message: the client obtains the message input by the user through the getString method, and sends the message to the server through the println method of PrintStream.

  4. The server receives the message: the server reads the message sent by the client through the next method of Scanner in its own thread.

  5. Server processing message: The server performs corresponding processing according to the received message, which can be parsing, judging, calculating and other operations on the message. In this example, the server simply sends the received message back to the client as-is.

  6. The server returns a message: the server sends the processed message to the client through the println method of PrintStream.

  7. The client receives the message: the client reads the message returned by the server through the next method of Scanner and prints it to the console.

  8. Repeat steps 3-7 until the client sends “byebye”, indicating the end of the communication.

Q&A:

Question 1: How do the two codes communicate?

First of all, the communication between the two processes must receive the corresponding port (computer network application layer knowledge) to communicate, and then the two codes share the input and output streams (it can be understood as two people using the same telephone line), the client The terminal outputs information to the server through its output stream out. The external input of the server is the input stream, that is, the output stream becomes the input stream of the server. The server receives the information of the client’s output stream through the scanner input stream. Similarly, the server also passes Its output stream is output to the client, and the client outputs the information when it receives the information

Question 2: The scanner.useDelimiter(“\\
“); useDelimiter method knows to set the delimiter, but does it have any effect when paired with “\\
“?

scanner.useDelimiter("\\
");
sets the scanner’s delimiter to “\\
“, which is a newline character. What this does is make the scanner take each line as an input element instead of whitespace (the default) as a delimiter. In this example, the sending and receiving of messages is separated by a line break between the server and the client.

Question 3: Why use PrintStream’s normal output System.out.println instead?

You can use System.out.println instead of PrintStream, they are both used to output content to the console. The use of PrintStream in the sample code is to send the message sent by the client to the server, and to print the message returned by the server to the console of the client.

Question 4: It is understandable that the server uses Scanner(socket.InputStream) to obtain client data, but why does the client also use Scanner(socket.InputStream) to obtain client data?

Both client and server use Scanner(socket.getInputStream()) because they need to read data from the corresponding socket’s input stream. The client needs to read the message sent by the server, and the server needs to read the message sent by the client.

Question 5: Why is there a private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in)); code, can’t it be directly replaced by Scanner (System.in)?

private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in)); This code defines a static, read-only variable of type BufferedReader, through System.in associates the keyboard input stream with this variable. The purpose of this is to use KEYBOARD_INPUT.readLine() on the client side to get the string entered by the user. Since Scanner(System.in) uses a buffer to scan the input, it cannot meet the requirements of reading line by line, so you need to use BufferedReader to accomplish.

3.BIO processing model (realize multi-user access to the same server)

Because in the previous model, only one user can interact with the server for a period of time, and once the user enters byebye, the server will be closed, but if I want multiple users to interact with the server, how can I achieve it? So the BIO processing model was introduced.

As can be seen from the structure shown in the figure, the solution is to create multiple Socket instances inside ServeSocket, and encapsulate each Socket instance in a thread to realize multi-thread communication. When a user connects, an independent communication thread is created. Each user can close its own thread independently

So you only need to modify the code on the server side

Service-Terminal:

package Example1907;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

class EchoServer implements Runnable {
// initialization
    Socket client = null;
    Scanner scanner = null;
    PrintStream out = null;
    boolean flag = true;

// Incoming communication objects to create input and output streams and confirm client objects
    public EchoServer(Socket client){
        try {
            this.client = client;
            this.scanner = new Scanner(client.getInputStream());
            this.out = new PrintStream(client.getOutputStream());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
// implement communication
    @Override
    public void run() {
        while (this.flag){
            String value = scanner.nextLine().trim();
            if (value. equalsIgnoreCase("byebye")){
                this.flag = false;
                out.println("Bye Bye~");
            } else {
                out.println("[server side]:" + value);
            }
        }
// After the communication is completed, close all services
        try {
            scanner. close();
            out. close();
            client. close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}
//Service-Terminal
public class javaDemo {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9999);
        boolean flag = true;
        System.out.println("Waiting for client connection---------");
// Receive multiple clients through multiple threads
        while (flag){
            Socket client = server. accept();
            new Thread(new EchoServer(client)).start();
        }
    }

}

Client: still the same (here you can exercise yourself to rewrite the code)

package Example1908;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

//client
public class javaDemo {
    private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in));
    public static String getString(String promp) throws Exception{
        System.out.print(promp);
        String input = KEYBOARD_INPUT.readLine().trim();
        return input;
    }
    public static void main(String[] args) throws Exception{

        Socket client = new Socket("localhost",9999);
        Scanner scanner = new Scanner(client. getInputStream());
        PrintStream out = new PrintStream(client. getOutputStream());

        scanner.useDelimiter("\\
");
        boolean flag = true;

        while (flag){
            String msg = getString("[Client] Please enter the message you want to send").trim();
            out. println(msg);
            if (scanner. hasNext()){
                System.out.println(scanner.next().trim());
            }
            if ("byebye".equalsIgnoreCase(msg)){
                flag = false;
            }
        }
        scanner. close();
        out. close();
        client. close();

    }
}

Show results:

Service-Terminal:

The following javaDemo2 is user 1 and javaDemo4 is user 2

Client 1:

Client 2:

This model is BIO mode, but it can be found that there is no limit to the number of threads here, which means that once the number of users increases sharply, the performance will drop sharply, so it is necessary to add restrictions on threads, which is the real BIO (Blocking Io blocking Io mode)

4.UDP program

The difference between UDP transmission and Tcp is that it is oriented to connectionless and unreliable communication. If Tcp is to make a call, you must know the other party’s number to establish a connection before you can communicate. Then UDP is just shouting on the street. Some people You can hear what you have to say, and some people are too far away to hear you, but you don’t care.

This transmission method is often used in games. The so-called packet loss is caused by unreliable udp, but this transmission method is very fast and has low delay.

UDP commonly used classes are

Common methods of DatagramPackage:

Method name Description
public DatagramPacket(byte[] buf , int length) Construct a DatagramPacket object, use the specified byte array as data, and specify the length as the effective data length.
public byte[] getData() Get the data byte array in the DatagramPacket object.
public int getLength() Get the length of the data in the DatagramPacket object.

Common methods of DatagramSocket:

Method Name Description
DatagramSocket() Creates an unbound datagram socket.
DatagramSocket(int port) Creates a datagram socket bound to the specified port.
DatagramSocket(int port, InetAddress address) Creates a datagram socket bound to the specified port and the specified local IP address.
void send(DatagramPacket packet) Send the specified data packet to the destination.
void receive(DatagramPacket packet) Receive a data packet and store it in the specified data packet object

The following case implements a client sending a message to the server through udp and outputting the server return value

On the premise that the previous server is open:

package Example1910;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;

public class javaDemo {
    public static void main(String[] args) throws Exception {
// input data via keyboard
        System.out.println("Please enter the data you want to send");
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        String data = input. readLine();
// send packet
        DatagramSocket client = new DatagramSocket(9999);
        DatagramPacket packet = new DatagramPacket(data.getBytes(StandardCharsets.UTF_8),0,data.length(), InetAddress.getByName("localhost"),9999);
        client. send(packet);
// receive packet
        byte redata[] = new byte[1024];
        DatagramPacket repacket = new DatagramPacket(redata,0,redata.length);
        client.receive(repacket);
        System.out.println("The received data is" + new String(redata,0,redata.length,StandardCharsets.UTF_8));

        client. close();
    }
}