一、Socket基本概念

1. 什么是Socket?

Socket(套接字)是网络通信中应用程序进程之间进行双向通信的端点的抽象,本质上是通信端点。它提供了一种进程间通信机制,使不同计算机上的程序能够进行数据交换。可以简单理解为两个程序之间进行网络通信的“管道”或“电话机”。

2. Socket的标识方式

在TCP/IP协议中,一个Socket由IP地址和端口号唯一标识,表示为:socket = (IP地址:端口号)。其中:

  • IP地址:标识网络中的主机位置
  • 端口号:标识主机上的具体进程

3. 半相关与全相关

  • 半相关:一个三元组 (协议,本地地址,本地端口号) 可以在全局唯一标识一个进程,这称为半相关
  • 全相关:一个完整的网间进程通信需要一个五元组 (协议,本地地址,本地端口号,远地地址,远地端口号) 来标识,这称为全相关

二、Socket类型

Socket主要分为三种类型:

1. 流式套接字(SOCK_STREAM)

  • 使用TCP协议
  • 提供面向连接的、可靠的、双向的数据传输服务
  • 数据无差错、无重复地发送,且按发送顺序接收
  • 适合处理大量数据,如文件传输协议(FTP)

流式Socket的特点:

  • 数据在传输过程中不会消失
  • 数据按照顺序传输
  • 数据的发送和接收不是同步的(内部使用缓冲区)

2. 数据报套接字(SOCK_DGRAM)

  • 使用UDP协议
  • 提供无连接的、不可靠但高效的数据传输
  • 保留了消息边界,传输效率较高
  • 适用于对实时性要求高的音视频传输应用

数据报式Socket的特点:

  • 强调快速传输而非传输顺序
  • 传输的数据可能丢失或损毁
  • 限制每一次传输的大小(UDP数据报最大64KB)
  • 数据的发送和接收是同步的

3. 原始套接字(SOCK_RAW)

  • 允许对较低层协议(如IP、ICMP)进行直接访问
  • 常用于网络协议分析或测试
  • 例如ping、traceroute等工具使用此类型的套接字

三、客户端/服务器模式

TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户端/服务器(C/S)模式

服务器端流程:

  1. 调用socket()创建套接字
  2. 调用bind()绑定IP地址和端口号
  3. 调用listen()将套接字转为可接收连接状态
  4. 调用accept()受理连接请求
  5. 通过send()/recv()进行数据传输
  6. 调用close()关闭套接字

客户端流程:

  1. 调用socket()创建套接字
  2. 调用connect()向服务器发送连接请求
  3. 通过send()/recv()进行数据传输
  4. 调用close()关闭套接字

四、核心API函数详解

1. socket() - 创建套接字

int socket(int domain, int type, int protocol);
  • domain:指定通信域,如AF_INET表示IPv4协议
  • type:指定套接字类型,如SOCK_STREAM(TCP)或SOCK_DGRAM(UDP)
  • protocol:指定协议,通常设为0表示默认协议

2. bind() - 绑定地址

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

将套接字与特定的IP地址和端口号关联起来。服务器端通常需要调用此函数来绑定固定的网络地址和端口号。

3. listen() - 监听连接

int listen(int sockfd, int backlog);

用于面向连接的服务器,表明它愿意接收连接请求。backlog参数指定等待连接队列的最大长度。

4. accept() - 接受连接

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

当有连接请求到达时,接受该连接并返回一个新的套接字描述符用于与客户端通信。

5. connect() - 发起连接

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

客户端使用此函数向服务器发起连接请求。

6. send()/recv() - 数据传输

int send(SOCKET s, const char *buf, int len, int flags);
int recv(SOCKET s, char *buf, int len, int flags);

用于在已连接的套接字上发送和接收数据。

7. close() — 关闭套接字

int close(int fd);

将套接字描述符的引用计数减1,当引用计数为0时,触发TCP的断开连接过程(四次挥手)。

五、地址结构体

在Socket编程中,常用sockaddr_in结构体来表示IPv4地址:

struct sockaddr_in {
    short sin_family;        // 网络地址类型,必须设为AF_INET
    u_short sin_port;        // 端口号(网络字节序)
    struct in_addr sin_addr; // 32位IP地址
    char sin_zero[8];        // 填充字段
};

地址转换函数用于在“点分十进制”字符串和网络字节序整数之间转换:

函数 作用 说明
inet_addr() 字符串→32位整数 仅IPv4
inet_pton() 字符串→二进制 支持IPv4/IPv6,推荐使用
inet_ntoa() 二进制→字符串 仅IPv4,线程不安全
inet_ntop() 二进制→字符串 支持IPv4/IPv6,线程安全

六、网络字节序

TCP/IP协议规定,网络数据流应采用大端字节序(Big-Endian)。为确保程序在不同架构的计算机上都能正常运行,需要使用以下转换函数:

  • htonl():主机字节序转网络字节序(32位)
  • htons():主机字节序转网络字节序(16位)
  • ntohl():网络字节序转主机字节序(32位)
  • ntohs():网络字节序转主机字节序(16位)

七、Windows环境特殊处理

在Windows下进行Socket编程,需要额外的初始化和清理步骤:

  1. 初始化:调用WSAStartup()设置Winsock版本
  2. 清理:通信结束后调用WSACleanup()释放资源
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
// ... 进行Socket操作
WSACleanup();  // 最后清理

八、实际应用场景

Socket编程广泛应用于各种网络应用中:

  • Web服务器:HTTP服务
  • 即时通讯软件:实时消息传递
  • 多人在线游戏:实时交互
  • 物联网设备通信:传感器数据上传和控制指令下发
  • 音视频传输:视频流、音频流的实时传输

总结:Socket编程是网络编程的基础,掌握Socket的基本概念、编程模型和核心API函数,是进行任何网络应用开发的前提。无论是Web服务、即时通讯还是物联网应用,底层都离不开Socket的支持。建议初学者从简单的TCP/UDP通信示例入手,逐步理解Socket的工作机制和编程要点。

Logo

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

更多推荐