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,释放资源。

java 复制代码
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】创建客户端的socket

【2】向服务器发起连接请求。

【3】与服务端通信,发送一个报文后等待回复,然后再发下一个报文。

【4】不断的重复第3)步,直到全部的数据被发送完。

【5】第4步:关闭socket,释放资源。

java 复制代码
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();
        }
    }
}

测试数据:

java 复制代码
请输入要发送的信息(输入 'exit' 退出):
Hello Word
服务端响应:已收到你的消息:Hello Word

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

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

UDP链接

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

【1】服务端

java 复制代码
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();
        }
    }
}

【2】客户端

java 复制代码
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();
        }
    }
}  

测试数据:

java 复制代码
请输入要发送的信息(输入 'exit' 退出):
UPD Test
服务端响应:已收到你的消息:UPD Test
相关推荐
一元咖啡36 分钟前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
儿时可乖了42 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
ruleslol44 分钟前
java基础概念37:正则表达式2-爬虫
java
Iced_Sheep1 小时前
干掉 if else 之策略模式
后端·设计模式
xmh-sxh-13141 小时前
jdk各个版本介绍
java
XINGTECODE1 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
天天扭码1 小时前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露