Network programming socket Socket

1. What is network programming

Network programming refers to the host on the network, through different processes , realizes network communication (or called network data transmission) in a programmed way .

2. Why implement network programming

We can obtain resources in the network through network programming, in essence, we can obtain the resources we need through the network.

3. How to do network programming

For network programming, the operating system provides a set of APIs for network programming, called Socket

Our program is at the application layer, and the operating system works at the transport layer. The socket socket is the API provided by the transport layer to the application layer. The most well-known protocols in the transport layer are TCP and UDP.

Four. Socket keyword

Socket keywords are divided into three categories for transport protocols:

①TCP transport protocol ②UDP transport protocol ③raw socket

The difference between Tcp transport protocol and UDP transport protocol:

There is a connection: it is equivalent to making a phone call, you must connect first before you can exchange data

No connection: It is equivalent to sending WeChat, no need to connect, you can send data directly

Reliable transmission: During the transmission process, the sender knows whether the receiver has received the data

Unreliable transmission: During the transmission process, the sender does not know whether the receiver has received the data

Byte stream oriented: transfer in bytes

Datagram-oriented: transmission in units of datagrams (a datagram will specify the size) a send/receive must be a complete datagram

Full duplex: one link, two-way communication

Half-duplex: one link, one-way communication

Size limit: Calls can be long or short, but there is a character limit for text messages.

4.1 APIs in UDP

It mainly involves two classes DatagramSocket (for network services started on the server, corresponding to the process used to receive user requests on the server) and DatagramPacket (for communication packets)

The process of sending and receiving data in network programming is mainly realized through the network card, receiving data is reading the network card, and writing to the network card when sending data.

Construction method:

Sending and closing methods:

Notes: The receive() method will enter the blocking state when there is no customer service access, and the send() method will send directly

Testing requirements:

Return a response according to the request: the response data is consistent with the request data

Server code:

import java.io.IOException;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

/**
 * @author tongchen
 * @create 2023-02-17 23:30
 */
public class UDPEchoServer {
    //创建socket变量
    private DatagramSocket socket;
    //通过构造方法指定端口号

    public UDPEchoServer(int port) throws SocketException {
        //检查端口号的合法性
        if(port<0||port>65535){
            throw new BindException("端口建议在合法范围内");
        }
        //创建socket
        this.socket=new DatagramSocket(port);
    }
    //启动服务
    public void start() throws IOException {
        //标识运行
        System.out.println("服务端开始启用.......");
        while(true){
            //使用datagrampacket进行数据的接收
            DatagramPacket datagramPacket = new DatagramPacket(new byte[1024], 1024);
            //使用socket接收数据
            socket.receive(datagramPacket);
            //解析接收到的数据
            String request=new String(datagramPacket.getData(), 0, datagramPacket.getData().length, "utf-8");
            //根据数据获取响应数据
            String response=pocess(request);
            //对响应数据进行封装
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),
                    response.getBytes().length,datagramPacket.getSocketAddress());
            //发送响应数据
            socket.send(responsePacket);
            System.out.printf("[%s:%d] request = %s, response = %s\n", datagramPacket.getAddress().toString(),
                    datagramPacket.getPort(), request, response);
        }

    }

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

    public static void main(String[] args) throws IOException {
        //创建端口号
        UDPEchoServer udpEchoServer = new UDPEchoServer(9090);
        udpEchoServer.start();
    }
}

Client code:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-18 11:00
 */
public class UDPchoClient {
    //定义服务器的地址和端口号
    private String address;
    private int port;
    private DatagramSocket socket;
    //初始化端口号和目标地址
    public UDPchoClient(String address, int port) throws SocketException {
        this.address = address;
        this.port = port;
        this.socket=new DatagramSocket();
    }
    //启动程序
    public void start() throws IOException {
        System.out.println("客户端启动......");
        //输入请求数据
        while(true){
            System.out.println("请输入请求数据");
            Scanner scanner=new Scanner(System.in);
            String request=scanner.nextLine();
            //与服务端建立连接
            InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);
            //进行数据的封装
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);
            //发送数据
            socket.send(requestPacket);
            //创建接收packet
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            //接收数据
            socket.receive(responsePacket);
            //解析数据
            String response=new String(responsePacket.getData(), 0, responsePacket.getLength(), "utf-8");
            //打印解析到的数据
            System.out.printf("request=%s,response=%s\n",request,response);
        }

    }

    public static void main(String[] args) throws IOException {
        //创建客户端
        UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9090);
        //启动客户端
        udPchoClient.start();

Analysis of test results

Test requirement 2: request an English word and return its Chinese translation

服务端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * @author tongchen
 * @create 2023-02-18 11:36
 */
public class UDPdicServer {
    //创建字典
    private HashMap<String,String>dic;
    //创建socket
   private DatagramSocket socket;
    //通过构造方法进行对所需内容的初始化

    public UDPdicServer(int port) throws SocketException {
        //创建新的hashmap
        this.dic=new HashMap<>();
        dic.put("tiger", "老虎");
        dic.put("cat", "小猫");
        dic.put("dog", "小狗");
        dic.put("pig", "小猪");
        //创建新的socket
        socket=new DatagramSocket(port);
    }

    //创建启动方法
    public void start() throws IOException {
        System.out.println("服务端启动.......");
        //创建循环
        while(true){
            //创建接收请求的packet
            DatagramPacket requestPacket=new DatagramPacket(new byte[1024],1024);
            //接收数据
            socket.receive(requestPacket);
            //对数据进行解析
            String request=new String(requestPacket.getData(), 0, requestPacket.getLength(), "utf-8");
            //根据请求获取响应
            String response=pocess(request);
            //封装数据
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(StandardCharsets.UTF_8), response.getBytes().length, requestPacket.getSocketAddress());
            //发送数据
            socket.send(responsePacket);
            //打印结果
            System.out.printf("[%s:%d] request = %s, response = %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }

    }

    private String pocess(String request) {
        return dic.getOrDefault(request, "查无此词");
    }

    public static void main(String[] args) throws IOException {
        //创建服务端
        UDPdicServer udPdicServer = new UDPdicServer(9091);
        udPdicServer.start();
    }
}

用户端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-18 11:00
 */
public class UDPchoClient {
    //定义服务器的地址和端口号
    private String address;
    private int port;
    private DatagramSocket socket;
    //初始化端口号和目标地址
    public UDPchoClient(String address, int port) throws SocketException {
        this.address = address;
        this.port = port;
        this.socket=new DatagramSocket();
    }
    //启动程序
    public void start() throws IOException {
        System.out.println("客户端启动......");
        //输入请求数据
        while(true){
            System.out.println("请输入请求数据");
            Scanner scanner=new Scanner(System.in);
            String request=scanner.nextLine();
            //与服务端建立连接
            InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);
            //进行数据的封装
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);
            //发送数据
            socket.send(requestPacket);
            //创建接收packet
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            //接收数据
            socket.receive(responsePacket);
            //解析数据
            String response=new String(responsePacket.getData(), 0, responsePacket.getLength(), "utf-8");
            //打印解析到的数据
            System.out.printf("request=%s,response=%s\n",request,response);
        }

    }

    public static void main(String[] args) throws IOException {
        //创建客户端
        UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9091);
        //启动客户端
        udPchoClient.start();
    }
}

结果分析:

4.2实现TCP协议的类

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法:

Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端

Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据

的。

Socket 构造方法:

我们通过实例对TCP协议进行讲解:客户端向服务端发送数据,服务端返回同样的数据

服务端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-20 10:50
 */
public class TcpEchoService {
    //通过创建构造方法指定端口号获取socket连接
    private ServerSocket socket;

    public TcpEchoService(int port) throws IOException {
        //判断端口的有效性
        if(port<1025||port>65535){
            throw new BindException("端口建议在1025和65535之间.......");
        }
        //通过指定端口号创建socket服务
        this.socket=new ServerSocket(port);
    }

    //获取客户端连接
    public void start() throws IOException {
        System.out.println("服务端端获取连接成功......");
        //不断循环接收用户的请求
        while(true){
            //获取与客户端的连接
            Socket clientSocket = socket.accept();
           //处理用户请求
            pocessRequset(clientSocket);

        }

    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        //打印客户端信息
        System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //通过clientSocket中获取输入和输出数据
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //循环从输入中获取数据
            while (true){
                Scanner scan=new Scanner(inputStream);
                //判断是否存在
                if(!scan.hasNextLine()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //获取数据并获得返回数据
                String request=scan.nextLine();
                String response=pocess(request);
                //发送数据
                PrintWriter printWriter = new PrintWriter(outputStream);
                //注意输入输出格式的一致性(用户和客户端)
                printWriter.println(response);
                //强制刷新缓存区
                printWriter.flush();
                //打印信息
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            clientSocket.close();

        }
    }

    private String pocess(String request) {
        return request;
    }
    //初始化服务器
    public static void main(String[] args) throws IOException {
        TcpEchoService tcpEchoService = new TcpEchoService(9090);
        tcpEchoService.start();
    }

}

用户端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author tongchen
 * @create 2023-02-20 11:17
 */
public class TcpEchoClient {
    //创建socket
    private Socket socket;
    //构造方法创建socket

    public TcpEchoClient(String address,int port) throws IOException {
        this.socket=new Socket(address, port);
    }
    //连接
    public void start(){
        System.out.printf("客户端启动成功....");
        try(InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream()){
            //循环输入
            while(true){
                System.out.println("->");
                Scanner scan=new Scanner (System.in);
                String request=scan.nextLine();
                //验证request的有效性
                if(request==null||request.equals("")){
                    System.out.println("输入的请求不能为空");
                    continue;
                }
                //将request输入到socket
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                //强制刷新
                printWriter.flush();
                //在socket中获取请求
                Scanner scanner = new Scanner(inputStream);
                String response = scanner.nextLine();
                System.out.printf("request = %s, response = %s\n", request, response);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {

        }
        //循环输入

    }
    //验证‘=
    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
        tcpEchoClient.start();
    }
}

一个客户端时可以正常运行,建立两个客户端时却出现了下面的情况:

我们去分析情况,是因为只有一个线程,客户端没有退出,服务端卡在第二个while循环中出不来

解决措施也很简单,开启多线程即可,为了线程频繁的开启和销毁所消耗的系统资源,我们使用线程池进行优化:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-20 12:18
 */
public class ThreadPoolTest {
    //通过创建构造方法指定端口号获取socket连接
    private ServerSocket socket;

    public ThreadPoolTest(int port) throws IOException {
        //判断端口的有效性
        if(port<1025||port>65535){
            throw new BindException("端口建议在1025和65535之间.......");
        }
        //通过指定端口号创建socket服务
        this.socket=new ServerSocket(port);
    }

    //获取客户端连接
    public void start() throws IOException {
        System.out.println("服务端端获取连接成功......");
        //不断循环接收用户的请求
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10));
        while(true){
            //获取与客户端的连接
            Socket clientSocket = socket.accept();
            threadPoolExecutor.submit(()->{
                try {
                    //处理用户请求
                    pocessRequset(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            });

        }

    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        //打印客户端信息
        System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //通过clientSocket中获取输入和输出数据
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //循环从输入中获取数据
            while (true){
                Scanner scan=new Scanner(inputStream);
                //判断是否存在
                if(!scan.hasNextLine()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //获取数据并获得返回数据
                String request=scan.nextLine();
                String response=pocess(request);
                //发送数据
                PrintWriter printWriter = new PrintWriter(outputStream);
                //注意输入输出格式的一致性(用户和客户端)
                printWriter.println(response);
                //强制刷新缓存区
                printWriter.flush();
                //打印信息
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            clientSocket.close();

        }
    }

    private String pocess(String request) {
        return request;
    }
    //初始化服务器
    public static void main(String[] args) throws IOException {
        ThreadPoolTest tcpEchoService = new ThreadPoolTest(9090);
        tcpEchoService.start();
    }
}

第二个任务:实现简单对话:

import javax.sound.sampled.Port;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-20 15:01
 */
public class TcpMsgServer {
    //创建socket
private ServerSocket socket;
    public TcpMsgServer(int port) throws IOException {
        this.socket=new ServerSocket(port);
    }
    //开启工作
    public void start() throws IOException {
        System.out.println("用户端开启成功......");
        //循环接收用户请求
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10));
        while(true){
            //接收用户请求
            Socket clientSocket = socket.accept();
            //处理用户请求(使用多线程)
            threadPoolExecutor.submit(()->{
                try {
                    pocessRequset(clientSocket) ;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    private void pocessRequset(Socket clientSocket) throws IOException {
        System.out.printf("%s|%d,客服端连接成功......\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //获取两个流
        try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){
            //多次获取用户请求
            while(true){
                //获取用户输入
                Scanner scan=new Scanner(inputStream);
                if(!scan.hasNext()){
                    System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                String request= scan.nextLine();
                //根据请求获取响应
                String response=pocess(request);
                //通过print发回
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                //打印效果
                printWriter.flush();
                System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }
    }

    private String pocess(String request) {
        System.out.println("请求为"+request);
        System.out.println("请输入你的响应->");
        Scanner scanner=new Scanner(System.in);
        String response=scanner.nextLine();
        return response;
    }
    //开启
    public static void main(String[] args) throws IOException {
        TcpMsgServer msgServer = new TcpMsgServer(9090);
        msgServer.start();
    }
}

Guess you like

Origin blog.csdn.net/m0_65431718/article/details/129070781