Socket 详细描述

Socket(套接字)​ 是计算机网络通信中的一个抽象概念,它是应用层与传输层(TCP/IP协议栈)之间的编程接口(API)。简单来说,Socket 是网络上两个应用程序进行双向通信的端点,它构成了网络通信的基础。


一、Socket 的本质与定义

从不同的角度看,Socket 有不同的含义:

  1. 编程接口:对于程序员来说,Socket 是一组操作系统提供的 API(如 socket(), bind(), connect() 等)。它屏蔽了底层复杂的 TCP/IP 协议细节,让开发者能像读写文件一样通过网络发送和接收数据。

  2. 通信端点:在网络层面,Socket 是一个五元组(或更简单的三元组/二元组)定义的连接终点。

    • 通用五元组(协议, 源IP, 源端口, 目的IP, 目的端口)

    • 这个五元组唯一标识了网络中的一个连接。

  3. 文件描述符:在类 Unix 系统(Linux/Mac)中,Socket 是一种特殊的文件。当它被创建后,会返回一个整数形式的文件描述符(File Descriptor),可以用 read/write 等操作来处理网络数据。


二、Socket 的核心组成要素

一个 Socket 绑定了以下四个核心要素(即其地址):

  1. 协议族(Protocol Family):指定通信使用的网络协议。

    • AF_INET:IPv4 互联网协议(最常用)。

    • AF_INET6:IPv6 互联网协议。

    • AF_UNIX / AF_LOCAL:用于同一台机器上的进程间通信(IPC)。

  2. Socket 类型(Type):指定通信的语义(数据传输方式)。

    • SOCK_STREAM(流式套接字):面向连接的、可靠的、基于字节流的通信。对应 TCP 协议

    • SOCK_DGRAM(数据报套接字):无连接的、不可靠的、固定长度的数据报通信。对应 UDP 协议

    • SOCK_RAW(原始套接字):允许直接访问底层协议(如 IP 或 ICMP),常用于开发网络工具(如 ping、traceroute)。

  3. 本地 IP 地址:本机在网络中的地址。

  4. 本地端口号(Port):用于区分本机上不同的网络应用程序(0-65535)。


三、Socket 的类型详解

1. 流式套接字 (TCP)

  • 特点

    • 面向连接:通信前必须先建立连接(三次握手)。

    • 可靠传输:数据保证送达,按序到达,且有重传机制。

    • 字节流:没有消息边界,发送方多次写入可能被接收方一次读取(粘包问题需应用层自己处理)。

    • 全双工:双方可以同时发送和接收数据。

  • 应用场景:Web 浏览(HTTP/HTTPS)、文件传输(FTP)、邮件(SMTP)、SSH 登录等需要数据完整性的场景。

2. 数据报套接字 (UDP)

  • 特点

    • 无连接:不需要建立连接,直接发送数据。

    • 不可靠:不保证数据到达,不保证顺序,也没有重传。

    • 数据报:保留了消息边界,发送一次,接收方就读取一次。

    • 低延迟:头部开销小,没有拥塞控制,速度快。

  • 应用场景:视频直播、语音通话(VoIP)、在线游戏、DNS 查询等对实时性要求高、能容忍少量丢包的场景。

3. 原始套接字 (Raw Socket)

  • 特点:绕过传输层(TCP/UDP),直接操作网络层数据包。

  • 应用场景:网络扫描、协议分析(如 Wireshark)、自定义协议开发。通常需要 Root/Administrator 权限。


四、Socket 通信流程(TCP 示例)

TCP 通信采用经典的 Client-Server(客户端-服务器)模型

服务器端 (Server) 流程:

  1. 创建 Socket (socket())

    创建一个套接字对象,指定协议族和类型。

  2. 绑定地址 (bind())

    将套接字与本机的 IP 地址和端口号绑定。

  3. 监听 (listen())

    开始监听来自客户端的连接请求,设置等待队列的长度。

  4. 接受连接 (accept())

    阻塞等待客户端连接。一旦有连接到来,返回一个新的套接字(已连接套接字),专门用于与该客户端通信。原来的监听套接字继续负责迎接新客人。

  5. 收发数据 (recv() / send())

    使用返回的新的套接字与客户端进行数据交换。

  6. 关闭连接 (close())

    通信结束后关闭套接字。

客户端 (Client) 流程:

  1. 创建 Socket (socket())

  2. 发起连接 (connect())

    向服务器的 IP 和端口发起连接请求(触发三次握手)。

  3. 收发数据 (send() / recv())

  4. 关闭连接 (close())

(注:UDP 不需要 listenaccept,也没有 connect 的概念,直接通过 sendtorecvfrom 向指定地址发送/接收数据报)


五、关键概念解析

1. 阻塞与非阻塞 (Blocking vs Non-blocking)

  • 阻塞:默认模式。调用 recv() 时如果没有数据,程序会一直停在那里等待,直到有数据到来;调用 accept() 时没有连接,也会一直等待。

  • 非阻塞:设置后,如果调用 recv() 没有数据,函数会立即返回一个错误(如 EWOULDBLOCK),程序可以继续执行其他任务。常用于高并发服务器。

2. I/O 多路复用 (IO Multiplexing)

为了解决大量 Socket 连接的管理问题,引入了 select, poll, epoll (Linux) / kqueue (BSD) 等机制。

  • 原理:一个线程监视多个 Socket。当某个 Socket 就绪(可读/可写/异常)时,通知程序进行处理。

  • 优势:单线程即可处理成千上万的连接,是高性能网络服务器(如 Nginx, Redis)的基石。

3. 粘包与拆包 (TCP Sticky Problem)

由于 TCP 是字节流,内核为了效率可能会将多次 send 的数据合并成一次发送(粘包),也可能将一个大数据包拆分成多个小包发送(拆包)。

  • 解决方案(应用层自己定义规则):

    • 定长消息。

    • 使用特殊分隔符(如 HTTP 的 \r\n)。

    • 在消息头中声明消息体长度(TLV 格式,最常用)。


六、常用 Socket API 函数速查

函数

作用

备注

socket()

创建套接字

返回文件描述符

bind()

绑定IP和端口

服务端必备

listen()

监听端口

服务端必备

accept()

接受连接

返回新的连接fd

connect()

发起连接

客户端必备

send() / write()

发送数据

recv() / read()

接收数据

close() / shutdown()

关闭连接

setsockopt()

设置套接字选项

如设置重用地址 SO_REUSEADDR


七、总结

Socket 是网络编程的入口。它不仅仅是代码层面的 API,更是计算机网络中端到端通信这一抽象概念的具象化体现。

  • 如果你需要可靠的文件传输,请选择 TCP Socket

  • 如果你需要极低的延迟(如视频通话),请选择 UDP Socket

  • 如果你想构建高性能服务器,必须掌握 非阻塞 I/O​ 和 I/O 多路复用技术

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐