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
相关推荐
CaffeinePro22 分钟前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax1 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH1 小时前
Koa和Express的区别
后端
MariaH1 小时前
Koa框架的使用
后端
luckdewei2 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某3 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy3 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom4 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
唐青枫7 小时前
Java JDBC 实战指南:从 Connection 到事务和连接池
java
用户1474853079748 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端