[Javaweb] Network programming foundation

content

1. Socket socket

1. Concept

2. Classification

Second, the Java datagram socket communication model

3. Java Stream Socket Communication Model

Fourth, UDP datagram socket programming

1,DatagramSocket API

2,DatagramPacket API

3. Sample code

Five, TCP stream socket programming

1,ServerSocket API

2,Socket API

3. Sample code


1. Socket socket

1. Concept

Socket is a technology provided by the system for network communication, and is the basic operation unit of network communication based on the TCP/IP protocol. Socket-based network program development is network programming.

2. Classification

Stream sockets : use the transport layer TCP protocol

TCP, the Transmission Control Protocol (Transmission Control Protocol), transport layer protocol.

The following are the characteristics of TCP

        connected

        reliable transmission

        byte stream

        There are receive buffers and send buffers

        Any size

Datagram sockets : use the transport layer UDP protocol

UDP, the User Datagram Protocol, is a transport layer protocol.

The following are the characteristics of UDP

        not connected

        unreliable transmission

        datagram oriented

        With receive buffer, no send buffer

        Size limited: transfer up to 64k at a time

Second, the Java datagram socket communication model

3. Java Stream Socket Communication Model

Fourth, UDP datagram socket programming

1,DatagramSocket API

DatagramSocket is a UDP Socket for sending and receiving UDP datagrams.

DatagramSocket method

method signature Method description
void receive(DatagramPacket p) Receive a datagram from this socket (if no datagram is received, the method blocks waiting)
void send(DatagramPacket p) Send datagram packets from this socket (do not block waiting, send directly)
void close() close this datagram socket

DatagramSocket constructor

method signature Method description
DatagramSocket() Create a Socket for a UDP datagram socket and bind it to any random port on the machine (usually used for clients)
DatagramSocket(int port) Create a Socket of a UDP datagram socket and bind it to the port specified by the machine (usually used for the server)

2,DatagramPacket API

DatagramPacket is the data sent and received by UDP Socket

DatagramPacket method

method signature Method description
InetAddress getAddress() Obtain the IP address of the sender's host from the received datagram; or obtain the IP address of the receiver's host from the sent datagram
int getPort() Obtain the port number of the sender host from the received datagram; or obtain the port number of the receiver host from the sent datagram
byte[] getData() Get the data in the datagram

DatagramPacket constructor

method signature Method description
DatagramPacket(byte[] buf, int length) Construct a DatagramPacket to receive datagrams, the received data is stored in a byte array (the first parameter buf), and the specified length is received (the second parameter length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) Constructs a DatagramPacket to send a datagram. The data to be sent is a byte array (the first parameter buf), from 0 to the specified length (the second parameter length). address specifies the IP and port number of the destination host

3. Sample code

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {

    //对于一个服务器来说,核心分成两部
    //进入初始化操作(实例化Socket对象)
    //进入主循环,接受请求
    //读取响应
    //根据请求计算响应
    //把响应写回客户端
    private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //读取解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(),0, requestPacket.getLength()).trim();
            //根据请求计算响应
            String response = process(request);
            //把响应写回客户端,响应数据就是response,需要包装成一个对象
            DatagramPacket datagramPacket = new DatagramPacket(response.getBytes(),response.getBytes().length
            ,requestPacket.getSocketAddress());
            socket.send(requestPacket);
            System.out.printf("[%s:%d] req: %s; resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }

}
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {

    //1,读客户端的数据
    //2,构造请求发送给客户端
    //3,从服务器读取响应
    //4,把响应写回客户端

    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    //需要在启动客户端的时候来指定那个服务器
    public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();
    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true){
            //读取用户数据
            System.out.println("->");
            String request = scanner.nextLine();
            if (request.equals("exit")){
                break;
            }
            //构造请求发送给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length, InetAddress.getByName(serverIp),serverPort);
            socket.send(requestPacket);
            //从服务器读取响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength()).trim();
            //显示数据
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start();
    }
    
}

Five, TCP stream socket programming

1,ServerSocket API

ServerSocket is an API for creating a TCP server socket

ServerSocket constructor

method signature Method description
ServerSocket(int port) Create a server stream socket Socket and bind it to the specified port

ServerSocket method

method signature Method description
Socket accept() Start listening on the specified port (the port bound at creation), after a client connection, return a server Socket object, and establish a connection with the client based on the Socket, otherwise block and wait
void close() close this socket

2,Socket API

Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket构造方法

方法签名 方法说明
Socket(String host, int port) Socket(String host, int port)

Socket方法

方法签名 方法说明
InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输入流
OutputStream getOutputStream() 返回此套接字的输出流

3,示例代码

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpEchoServer {
    //1,初始化服务器
    //2,进入主循环
    //先从内核中获取到一个TCP连接
    //处理这个TCP连接
    //读取请求并解析
    //根据请求计算响应
    //把响应写回客户端

    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //先从内核中获取一个TCP连接
            Socket clientSocket = serverSocket.accept();
            //处理这个连接
            processConnection(clientSocket);
        }
    }

    private void processConnection(Socket clientSocket) {

        System.out.printf("[%s:%d]客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //通过clientSocket来与客户端交互
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {
            //此处建立一个连接过程,处理多个请求和响应
            while (true){
                //读取请求响应
                String request = bufferedReader.readLine();
                //根据请求计算响应
                String response = process(request);
                //把响应写回客户端
                bufferedWriter.write(response + "\n");
                bufferedWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
            System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress().toString(),
                    clientSocket.getPort());
        }

    }

    private String process(String request) {
        return request;
    }


    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
    
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class TcpClientServer {
    //启动客户端
    //进入主循环
    //读取用户内容
    //构造一个请求发送给服务器
    //读取服务器的响应的数据
    //把响应写到界面
    private Socket socket = null;

    public TcpClientServer(String serverIp,int serverPort) throws IOException {
        //实例化Socket,建立TCP连接
        socket = new Socket(serverIp,serverPort);
    }

    public void start(){

        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))){

            while (true){
                //读取用户输入内容
                System.out.println("->");
                String request = scanner.nextLine();
                if (request.equals("exit")){
                    break;
                }
                //构造请求并发送
                bufferedWriter.write(request + "\n");
                bufferedWriter.flush();
                //读取响应数据
                String response = bufferedReader.readLine();
                //把响应写到界面
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpClientServer tcpClientServer = new TcpClientServer("127.0.0.1",9090);
        tcpClientServer.start();
    }
    
}

 getOutputStream得到一个流对象,进一步封装成一个BufferedWriter

代码调用BufferedWriter.write方法的时候,先把数据放在缓冲区,此时wrtite操作并没有往内核中写socket文件中的数据。

调用flush方法,把内存缓冲区中的内容写入Socket文件中

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpThreadEchoServer {
    private ServerSocket serverSocket = null;

    public TcpThreadEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            Socket clientSocket = serverSocket.accept();
            Thread t = new Thread(){
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            };
            t.start();
        }
    }

    public void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {
            while (true) {
                // 1. 读取请求并解析
                String request = bufferedReader.readLine();
                // 2. 根据请求计算响应
                String response = process(request);
                // 3. 把响应写回到客户端
                bufferedWriter.write(response + "\n");
                bufferedWriter.flush();

                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            // e.printStackTrace();
            System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
                    clientSocket.getPort());
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpThreadEchoServer server = new TcpThreadEchoServer(9090);
        server.start();
    }
}

 主线程专门负责accept,其他线程负责和客户端沟通

 public void start() throws IOException {
        System.out.println("服务器启动");
        //先创建一个线程池实例
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true){
            //针对这个连接,单独创建一个线程池来负责
            Socket clientSocket = serverSocket.accept();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

虽然使用多线程解决了BUG,但还有很多问题,每次来一个客户端,都要分配一个线程对于一个服务器来说,随时可能会来大量的客户端,随时也会有大量的客户端断开连接,服务器需要频繁的创建和销毁线程,所以可以用线程池

Guess you like

Origin blog.csdn.net/qq_50156012/article/details/123261275