Linux C 套接字编程是网络开发中的核心技术,它基于 TCP/IP 协议栈,为应用程序提供了跨网络的通信能力,通过套接字,开发者可以实现客户端与服务器之间的数据传输,构建分布式系统、网络服务等多种应用,本文将系统介绍 Linux C 套接字的核心概念、编程步骤及关键函数,帮助读者掌握这一技术。
套接字基础概念
套接字(Socket)是通信的端点,它通过 IP 地址和端口号标识网络中的进程,在 Linux 中,套接字被描述为一个文件描述符,允许使用文件 I/O 函数(如 read、write)进行数据传输,根据通信类型,套接字主要分为三类:
- 流式套接字(SOCK_STREAM):基于 TCP 协议,提供面向连接、可靠的数据传输,保证数据按序且无丢失。
- 数据报套接字(SOCK_DGRAM):基于 UDP 协议,无连接、不可靠,但传输效率高,适用于实时性要求高的场景。
- 原始套接字(SOCK_RAW):直接操作 IP 层数据,常用于网络协议分析和自定义协议开发。
TCP 套接字编程步骤
TCP 套接字编程采用客户端-服务器模型,双方需分别执行初始化、绑定、监听、连接等操作,以下是完整流程:
服务器端步骤
-
创建套接字
使用socket()
函数创建套接字,需指定地址族(如 AF_INET 表示 IPv4)、套接字类型(SOCK_STREAM)和协议(0 表示自动选择)。int server_fd = socket(AF_INET, SOCK_STREAM, 0);
-
绑定地址与端口
通过bind()
将套接字与 IP 地址和端口号绑定,客户端通过该地址发起连接。struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口 server_addr.sin_port = htons(8080); // 端口号需转换为网络字节序 bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
-
监听连接
调用listen()
使套接字进入监听状态,参数backlog
指定最大待连接队列长度。listen(server_fd, 5);
-
接受连接
使用accept()
阻塞等待客户端连接,返回新的套接字描述符用于后续通信。struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
-
数据传输
通过read()
和write()
函数与客户端交换数据。char buffer[1024] = {0}; read(client_fd, buffer, 1024); write(client_fd, "Hello, Client!", 14);
-
关闭套接字
通信结束后,使用close()
关闭套接字。close(client_fd); close(server_fd);
客户端步骤
-
创建套接字
与服务器端相同,调用socket()
创建套接字。int client_fd = socket(AF_INET, SOCK_STREAM, 0);
-
连接服务器
使用connect()
向服务器发起连接,需指定服务器的 IP 地址和端口号。struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
-
数据传输
向服务器发送数据并接收响应。write(client_fd, "Hello, Server!", 14); char buffer[1024] = {0}; read(client_fd, buffer, 1024);
-
关闭套接字
close(client_fd);
UDP 套接字编程特点
UDP 套接字无需建立连接,编程流程更简单,主要区别如下:
- 无需监听与连接:直接使用
sendto()
和recvfrom()
发送和接收数据,需指定对方地址。 - 无连接性:每次发送数据需提供目标地址,接收方需明确数据来源。
示例代码片段:
// 发送数据 struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &dest_addr.sin_addr); sendto(sockfd, "Hello, UDP!", 11, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)); // 接收数据 struct sockaddr_in src_addr; socklen_t src_len = sizeof(src_addr); char buffer[1024] = {0}; int n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr*)&src_addr, &src_len);
关键函数与错误处理
Linux C 套接字编程依赖以下核心函数,需注意错误处理(如检查返回值 -1 并通过 perror()
打印错误信息):
函数名 | 功能描述 | 返回值 |
---|---|---|
socket() |
创建套接字 | 成功返回套接字描述符,失败返回 -1 |
bind() |
绑定地址与端口 | 成功返回 0,失败返回 -1 |
listen() |
开始监听连接 | 成功返回 0,失败返回 -1 |
accept() |
接受客户端连接 | 成功返回新套接字,失败返回 -1 |
connect() |
向服务器发起连接 | 成功返回 0,失败返回 -1 |
send() /write() |
发送数据 | 成功返回发送字节数,失败返回 -1 |
recv() /read() |
接收数据 | 成功返回接收字节数,失败返回 -1 |
close() |
关闭套接字 | 成功返回 0,失败返回 -1 |
字节序与地址转换
网络通信中需统一字节序(大端序),Linux 提供以下转换函数:
htons()
:16 位主机字节序转网络字节序htonl()
:32 位主机字节序转网络字节序ntohs()
:16 位网络字节序转主机字节序ntohl()
:32 位网络字节序转主机字节序
IP 地址转换函数:
inet_pton()
:将点分十进制 IP 转换为网络字节序二进制地址inet_ntop()
:将网络字节序二进制地址转换为点分十进制 IP
开发者可快速上手 Linux C 套接字编程,构建可靠的网络应用,实际开发中还需注意并发处理(如多线程、多进程)、数据分包与粘包处理等细节问题,以确保程序的稳定性和性能。