• Socket通信


    优质博文IT-BLOG-CN

    一、简介

    Socket套接字:描述了计算机的IP地址和端口,运行在计算机中的程序之间采用socket进行数据通信。通信的两端都有socket,它是一个通道,数据在两个socket之间进行传输。socket把复杂的TCP/IP协议族隐藏在socket接口后面,对程序员来说,只要用好socket相关的函数,就可以完成数据通信。

    TCP提供了流stream和数据报datagram两种通信机制,所以套接字也分为流套接字和数据报套接字:
    【1】流套接字的类型是SOCK_STREAM,它提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有出错后重新发送的机制。
    【2】数据报套接的类型是SOCK_DGRAM,它不需要建立和维持一个连接,采用UDP/IP协议实现。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它不需要建立和维持连接。

    常见面试题:TCP与UDP的区别

    TCP(传输控制协议)和UDP(用户数据报协议)是两种常见的互联网传输协议,它们在数据传输方面有以下区别:
    【1】连接性: TCP是面向连接的协议,而UDP是无连接的协议。TCP在传输数据之前需要先建立连接,而UDP则直接发送数据包。
    【2】可靠性: TCP提供可靠的数据传输,它使用确认、重传和流量控制等机制来确保数据的完整性和顺序性。而UDP不提供可靠性保证,它只是简单地将数据包发送出去,无法保证数据的到达和顺序。
    【3】速度: 由于TCP提供了可靠性保证,它的传输速度相对较慢。而UDP没有额外的机制,传输速度相对较快。
    【4】数据量限制: TCP没有固定的数据包大小限制,可以处理任意大小的数据。而UDP的数据包大小有限制,最大为64KB
    【5】适用场景: TCP适用于对数据传输可靠性要求较高的场景,例如文件传输、网页浏览等。而UDP适用于对实时性要求较高,但对数据可靠性要求相对较低的场景,例如音视频流媒体、实时游戏等。

    总结来说,TCP提供可靠的、面向连接的数据传输,适用于对数据完整性和顺序性要求较高的场景。而UDP提供快速的、无连接的数据传输,适用于对实时性要求较高的场景。

    二、Socket通信过程

    【1】服务端程序将一个套接字绑定到指定的ip地址和端口,并通过此套接字等待和监听客户的连接请求。
    【2】客户程序向服务端程序绑定的地址和端口发出连接请求。
    【3】服务端接受连接请求。
    【4】客户端和服务端通过读写套接字进行通信。

    TCP链接

    服务端工作流程:
    【1】创建服务端的socket
    【2)把服务端用于通信的地址和端口绑定到socket上。
    【3】把socket设置为监听模式。
    【4】接受客户端的连接。
    【5】与客户端通信,接收客户端发过来的报文后,回复处理结果。
    【6)不断的重复第5)步,直到客户端断开连接。
    【7】关闭socket,释放资源。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
     
    public class Server {
        public static void main(String[] args) {
            try {
                // 创建ServerSocket对象,指定监听的端口号
                ServerSocket serverSocket = new ServerSocket(45442);
     
                System.out.println("等待客户端连接...");
     
                // 监听客户端的连接请求
                Socket clientSocket = serverSocket.accept();
                System.out.println("客户端已连接");
     
                // 获取输入流和输出流 输入流和输出流是通过socket对象来进行数据传输的。
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
     
                String message;
     
                while (true) {
                    // 读取客户端发送的信息
                    message = in.readLine();
     
                    if (message.equalsIgnoreCase("exit")) {
                        // 如果接收到终止标志,退出循环
                        break;
                    }
     
                    System.out.println("收到客户端消息:" + message);
     
                    // 发送响应给客户端
                    out.println("已收到你的消息:" + message);
                }
     
                // 关闭连接
                clientSocket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    客户端的工作流程:
    【1】创建客户端的socket
    【2】向服务器发起连接请求。
    【3】与服务端通信,发送一个报文后等待回复,然后再发下一个报文。
    【4】不断的重复第3)步,直到全部的数据被发送完。
    【5】第4步:关闭socket,释放资源。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
     
    public class Client {
        public static void main(String[] args) {
            try {
                // 创建Socket对象,指定服务端的IP地址和端口号
                Socket socket = new Socket("127.0.0.1", 45442);
     
                // 获取输入流和输出流 输入流和输出流是通过socket对象来进行数据传输的。
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
     
                // 从控制台读取用户输入
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                String message;
     
                while (true) {
                    System.out.println("请输入要发送的信息(输入 'exit' 退出):");
                    message = reader.readLine();
     
                    if (message.equalsIgnoreCase("exit")) {
                        // 如果用户输入 'exit',发送终止标志给服务端并退出循环
                        out.println("exit");
                        break;
                    }
     
                    // 将用户输入的信息发送给服务端
                    out.println(message);
     
                    // 接收服务端的响应并打印
                    String response = in.readLine();
                    System.out.println("服务端响应:" + response);
                }
     
                // 关闭连接
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    测试数据:

    请输入要发送的信息(输入 'exit' 退出):
    Hello Word
    服务端响应:已收到你的消息:Hello Word
    
    • 1
    • 2
    • 3

    在运行程序之前,必须保证服务器的防火墙已经开通了网络访问策略

    socket是系统资源,操作系统打开的socket数量是有限的,在程序退出之前必须关闭已打开的socket,就像关闭文件指针一样,就像delete已分配的内存一样,极其重要。

    UDP链接

    使用UDP通信,使用java.net.DatagramSocket创建了一个DatagramSocket对象。

    【1】服务端

    package socket.UDP;
     
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.*;
     
    public class UDPClient {
        public static void main(String[] args) {
            try {
                // 创建DatagramSocket对象
                DatagramSocket socket = new DatagramSocket();
     
                InetAddress serverAddress = InetAddress.getByName("127.0.0.1");
                int serverPort = 45442;
     
                // 从控制台读取用户输入
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                String message;
     
                while (true) {
                    System.out.println("请输入要发送的信息(输入 'exit' 退出):");
                    message = reader.readLine();
     
                    if (message.equalsIgnoreCase("exit")) {
                        // 如果用户输入 'exit',退出循环
                        break;
                    }
     
                    byte[] sendData = message.getBytes();
     
                    // 创建发送数据的DatagramPacket对象
                    DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
     
                    // 发送数据
                    socket.send(sendPacket);
     
                    byte[] receiveData = new byte[1024];
     
                    // 创建接收数据的DatagramPacket对象
                    DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
     
                    // 接收服务端的响应
                    socket.receive(receivePacket);
     
                    // 将接收到的数据转换为字符串并打印
                    String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
                    System.out.println("服务端响应:" + response);
                }
     
                // 关闭Socket连接
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    【2】客户端

    package socket.UDP;
     
    import java.io.IOException;
    import java.net.*;
     
    public class UDPServer {
        public static void main(String[] args) {
            try {
                // 创建DatagramSocket对象,并指定监听的端口号
                DatagramSocket socket = new DatagramSocket(45442);
                System.out.println("等待客户端连接...");
     
                byte[] receiveData = new byte[1024];
     
                while (true) {
                    // 创建接收数据的DatagramPacket对象
                    DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
     
                    // 接收客户端发送的数据
                    socket.receive(receivePacket);
     
                    // 获取客户端的IP地址和端口号
                    InetAddress clientAddress = receivePacket.getAddress();
                    int clientPort = receivePacket.getPort();
     
                    // 将接收到的数据转换为字符串
                    String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                    System.out.println("收到客户端消息:" + message);
     
                    if (message.equalsIgnoreCase("exit")) {
                        // 如果接收到终止标志,退出循环
                        break;
                    }
     
                    // 构造发送数据的字节数组
                    String response = "已收到你的消息:" + message;
                    byte[] sendData = response.getBytes();
     
                    // 创建发送数据的DatagramPacket对象
                    DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientAddress, clientPort);
     
                    // 发送响应给客户端
                    socket.send(sendPacket);
                }
     
                // 关闭Socket连接
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    测试数据:

    请输入要发送的信息(输入 'exit' 退出):
    UPD Test
    服务端响应:已收到你的消息:UPD Test
    
    • 1
    • 2
    • 3
  • 相关阅读:
    【Linux】系统安装
    Leetcode 1758. 生成交替二进制字符串的最少操作数
    【精选】HTML5最全知识点集合
    用C语言实现状态机设计模式
    R语言计算方差分析的F值和P值
    Problem B: 排序二叉树
    java分布式的ACP是什么
    zookeeper本地部署和集群搭建
    『Halcon与C#混合编程』006_HObject、HOperatorSet、HTuple、HDevWindowStack
    《算法竞赛进阶指南》 双端队列
  • 原文地址:https://blog.csdn.net/zhengzhaoyang122/article/details/133521211