<think>我们之前已经提供了完整的Socket通信代码(TCP)并逐行分析了代码。现在,用户要求详细解释每一行的变量以及对应的参数意义。由于代码较长,我们将对关键部分进行详细解释,包括函数参数和变量的具体含义。
由于代码量较大,我们将选择一些关键函数和代码段进行详细解释。如果全部逐行解释会非常冗长,因此我们重点解释核心部分。
### 服务器端关键代码详解
#### 1. 创建套接字
```c
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
```
- **函数**: `socket(int domain, int type, int protocol)`
- `domain`: 地址族,`AF_INET` 表示IPv4协议。
- `type`: 套接字类型,`SOCK_STREAM` 表示面向连接的流套接字(TCP)。
- `protocol`: 协议,0表示默认协议(对于流套接字是TCP)。
- **返回值**: 成功返回非负文件描述符(`server_fd`),失败返回-1。
#### 2. 配置服务器地址结构
```c
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
```
- `struct sockaddr_in`: IPv4地址结构,包含:
- `sin_family`: 地址族(同socket的domain)。
- `sin_addr.s_addr`: IP地址,`INADDR_ANY`表示绑定到所有本地接口。
- `sin_port`: 端口号,使用`htons`将主机字节序(小端)转换为网络字节序(大端)。
#### 3. 绑定套接字
```c
bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
```
- **函数**: `bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)`
- `sockfd`: 套接字描述符(`server_fd`)。
- `addr`: 指向地址结构的指针,这里将`sockaddr_in`强制转换为`sockaddr`。
- `addrlen`: 地址结构的大小(`sizeof(server_addr)`)。
#### 4. 监听连接
```c
listen(server_fd, 1);
```
- **函数**: `listen(int sockfd, int backlog)`
- `sockfd`: 套接字描述符。
- `backlog`: 等待连接队列的最大长度,这里设置为1(只能有一个客户端连接)。
#### 5. 接受连接
```c
int client_socket = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
```
- **函数**: `accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)`
- `sockfd`: 监听套接字描述符(`server_fd`)。
- `addr`: 用于存放客户端地址信息的结构体指针(`client_addr`)。
- `addrlen`: 指向地址结构长度的指针,调用前需初始化为`client_addr`的大小,返回时是实际长度。
- **返回值**: 成功返回一个新的套接字描述符(`client_socket`),用于与客户端通信。
#### 6. 创建接收线程
```c
pthread_create(&recv_thread, NULL, receive_handler, NULL);
```
- **函数**: `pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)`
- `thread`: 指向线程标识符的指针(`recv_thread`)。
- `attr`: 线程属性,NULL表示默认属性。
- `start_routine`: 线程函数入口(`receive_handler`)。
- `arg`: 传递给线程函数的参数,这里为NULL。
#### 7. 接收线程函数
```c
int bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
```
- **函数**: `recv(int sockfd, void *buf, size_t len, int flags)`
- `sockfd`: 套接字描述符(`client_socket`)。
- `buf`: 接收缓冲区指针(`buffer`)。
- `len`: 缓冲区大小(`BUFFER_SIZE`)。
- `flags`: 通常为0,表示阻塞接收。
- **返回值**: 接收到的字节数(>0),0表示连接关闭,<0表示错误。
#### 8. 发送消息
```c
send(client_socket, message, strlen(message), 0);
```
- **函数**: `send(int sockfd, const void *buf, size_t len, int flags)`
- `sockfd`: 套接字描述符(`client_socket`)。
- `buf`: 发送缓冲区指针(`message`)。
- `len`: 发送数据的长度(`strlen(message)`)。
- `flags`: 通常为0。
### 客户端关键代码详解(与服务器类似部分不再重复)
#### 1. 连接服务器
```c
connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
```
- **函数**: `connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)`
- `sockfd`: 套接字描述符(`sock`)。
- `addr`: 服务器地址结构指针(`serv_addr`)。
- `addrlen`: 服务器地址结构的大小。
### 变量详解
#### 服务器端
- `int server_fd`: 服务器监听套接字描述符。
- `struct sockaddr_in server_addr, client_addr`: 服务器和客户端的地址结构。
- `socklen_t addr_len`: 客户端地址结构长度。
- `pthread_t recv_thread`: 接收线程的标识符。
- `int client_socket`: 与客户端通信的套接字描述符。
- `char buffer[BUFFER_SIZE]`: 接收消息的缓冲区。
- `char message[BUFFER_SIZE]`: 发送消息的缓冲区。
#### 客户端
- `int sock`: 连接服务器的套接字描述符。
- `struct sockaddr_in serv_addr`: 服务器地址结构。
- `pthread_t recv_thread`: 接收线程标识符。
- `char buffer[BUFFER_SIZE]`: 接收消息的缓冲区。
- `char message[BUFFER_SIZE]`: 发送消息的缓冲区。
### 完整代码(已注释每一行)
由于用户要求详细解释每一行,我们将对之前的完整代码添加详细注释。但请注意,由于代码较长,注释会非常详细,可能会显得冗长。我们选择服务器端代码为例进行注释,客户端类似。
#### 服务器端代码(带详细注释)
```c
#include <stdio.h> // 标准输入输出函数,如printf, fgets
#include <stdlib.h> // 标准库函数,如exit, malloc
#include <string.h> // 字符串处理函数,如memset, strcspn
#include <unistd.h> // POSIX系统调用,如close, read, write
#include <arpa/inet.h> // 网络地址转换函数,如inet_ntoa, htons
#include <pthread.h> // 线程支持库
#define PORT 8080 // 定义服务器监听的端口号
#define BUFFER_SIZE 1024 // 定义缓冲区大小
int client_socket = -1; // 全局变量,用于存储与客户端通信的套接字描述符,初始为-1表示无效
// 接收消息的线程函数
void *receive_handler(void *arg) {
char buffer[BUFFER_SIZE]; // 定义接收缓冲区
while (1) { // 循环接收消息
memset(buffer, 0, BUFFER_SIZE); // 将缓冲区清零,避免之前的数据残留
// 从client_socket接收数据,存放到buffer中,最大长度为BUFFER_SIZE
int bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (bytes_received <= 0) { // 如果接收字节数<=0,表示连接出错或关闭
perror("Connection closed or error"); // 打印错误信息
close(client_socket); // 关闭客户端套接字
exit(EXIT_FAILURE); // 退出程序(整个进程)
}
// 打印接收到的客户端消息,前面加换行是为了与输入提示分开
printf("\n[Client]: %s\n", buffer);
// 打印服务器输入提示,注意这里没有换行,等待用户输入
printf("[Server] Enter message: ");
// 刷新标准输出,确保提示信息立即显示
fflush(stdout);
}
return NULL; // 线程结束(实际上由于无限循环,不会执行到这里)
}
int main() {
int server_fd; // 服务器监听套接字描述符
struct sockaddr_in server_addr, client_addr; // 定义服务器和客户端的地址结构
socklen_t addr_len = sizeof(client_addr); // 客户端地址结构长度
pthread_t recv_thread; // 接收线程的标识符
// 创建服务器套接字,使用IPv4协议,流式套接字(TCP)
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed"); // 如果创建失败,打印错误
exit(EXIT_FAILURE); // 退出程序
}
// 初始化服务器地址结构为0
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // 使用IPv4
server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有本地接口
server_addr.sin_port = htons(PORT); // 设置端口号,并转换为网络字节序
// 将套接字绑定到服务器地址
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed"); // 绑定失败
close(server_fd); // 关闭套接字
exit(EXIT_FAILURE); // 退出
}
// 开始监听,最大等待队列为1(即同时只能有一个客户端连接)
if (listen(server_fd, 1) < 0) {
perror("Listen failed"); // 监听失败
close(server_fd); // 关闭套接字
exit(EXIT_FAILURE); // 退出
}
printf("Server listening on port %d...\n", PORT); // 打印监听信息
// 接受客户端连接,client_addr将存储客户端地址,addr_len是地址结构长度
if ((client_socket = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len)) < 0) {
perror("Accept failed"); // 接受失败
close(server_fd); // 关闭监听套接字
exit(EXIT_FAILURE); // 退出
}
// 打印客户端IP地址(使用inet_ntoa将二进制IP转换为点分十进制字符串)
printf("Client connected! IP: %s\n", inet_ntoa(client_addr.sin_addr));
// 创建接收线程,线程函数为receive_handler,参数为NULL
if (pthread_create(&recv_thread, NULL, receive_handler, NULL) != 0) {
perror("Thread creation failed"); // 线程创建失败
close(client_socket); // 关闭客户端套接字
close(server_fd); // 关闭监听套接字
exit(EXIT_FAILURE); // 退出
}
char message[BUFFER_SIZE]; // 发送消息缓冲区
while (1) { // 循环发送消息
printf("[Server] Enter message: "); // 提示输入
fgets(message, BUFFER_SIZE, stdin); // 从标准输入读取一行(包括换行符)
// 移除换行符:查找第一个换行符位置,并将其替换为字符串结束符
message[strcspn(message, "\n")] = '\0';
// 发送消息到客户端套接字
if (send(client_socket, message, strlen(message), 0) < 0) {
perror("Send failed"); // 发送失败
break; // 退出循环
}
}
// 关闭套接字(实际由于无限循环,这里不会执行到)
close(client_socket);
close(server_fd);
return 0;
}
```
### 客户端代码(带详细注释)
由于篇幅原因,客户端的详细注释类似,主要区别在于连接服务器而不是监听。这里只给出关键部分注释:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define SERVER_IP "127.0.0.1" // 服务器IP地址(本地回环地址)
#define PORT 8080 // 服务器端口
#define BUFFER_SIZE 1024 // 缓冲区大小
int sock = -1; // 连接服务器的套接字描述符
void *receive_handler(void *arg) {
char buffer[BUFFER_SIZE]; // 接收缓冲区
while (1) {
memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区
int bytes_received = recv(sock, buffer, BUFFER_SIZE, 0); // 接收数据
if (bytes_received <= 0) {
perror("Connection closed or error");
close(sock);
exit(EXIT_FAILURE);
}
printf("\n[Server]: %s\n", buffer);
printf("[Client] Enter message: ");
fflush(stdout);
}
return NULL;
}
int main() {
struct sockaddr_in serv_addr; // 服务器地址结构
pthread_t recv_thread; // 接收线程标识符
// 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&serv_addr, 0, sizeof(serv_addr)); // 初始化服务器地址结构
serv_addr.sin_family = AF_INET; // IPv4
serv_addr.sin_port = htons(PORT); // 端口转换
// 将字符串形式的IP地址转换为二进制形式,存入serv_addr.sin_addr
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("Invalid address/Address not supported");
close(sock);
exit(EXIT_FAILURE);
}
// 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("Connected to server at %s:%d\n", SERVER_IP, PORT);
// 创建接收线程
if (pthread_create(&recv_thread, NULL, receive_handler, NULL) != 0) {
perror("Thread creation failed");
close(sock);
exit(EXIT_FAILURE);
}
char message[BUFFER_SIZE];
while (1) {
printf("[Client] Enter message: ");
fgets(message, BUFFER_SIZE, stdin);
message[strcspn(message, "\n")] = '\0'; // 去掉换行符
if (send(sock, message, strlen(message), 0) < 0) {
perror("Send failed");
break;
}
}
close(sock);
return 0;
}
```
### 注意事项
1. **线程安全**: 在多线程环境下,多个线程同时访问标准输出(`printf`)可能导致输出混乱。但在本示例中,由于接收线程和主线程的输出内容不同(一个输出接收到的消息,一个输出输入提示),且我们使用了`fflush`,所以基本可用。但在高并发情况下,需要更严格的同步机制。
2. **错误处理**: 代码中多处使用了`perror`和`exit`,实际应用中可能需要更细致的错误恢复或重试机制。
3. **资源释放**: 在错误退出时,我们关闭了已打开的套接字,但正常退出时(按Ctrl+C中断)资源也会被操作系统回收。但在更复杂的程序中,应设计优雅退出的方式。
4. **多客户端支持**: 当前服务器只能同时处理一个客户端连接。要支持多客户端,需要使用多进程、多线程(每个客户端一个线程)或I/O多路复用(select/poll/epoll)。
### §§相关问题§§:
1. 在Socket编程中,`INADDR_ANY`的作用是什么?为什么服务器通常使用这个地址?
2. `recv`函数返回0时表示什么情况?如何处理这种情况?
3. 在多线程程序中,为什么需要调用`fflush(stdout)`?如果不调用会有什么问题?
4. 如何修改上述代码,使得服务器能够同时处理多个客户端连接?</think>### 详细解释:Socket通信代码中的变量和参数意义
以下是服务器端和客户端代码的逐行详细解释,包括所有变量和参数的含义:
#### 服务器端代码详解
```c
#include <stdio.h> // 标准输入输出函数(printf, fgets, fflush)
#include <stdlib.h> // 标准库函数(exit, malloc, free)
#include <string.h> // 字符串处理函数(memset, strlen, strcspn)
#include <unistd.h> // POSIX系统调用(close, read, write)
#include <arpa/inet.h> // 网络地址处理函数(inet_ntoa, htons)
#include <pthread.h> // 线程支持库(pthread_create)
#define PORT 8080 // 服务器监听的端口号
#define BUFFER_SIZE 1024 // 消息缓冲区大小(字节)
int client_socket = -1; // 客户端套接字描述符,初始化为无效值(-1)
// 接收消息的线程函数
void *receive_handler(void *arg) { // arg: 线程参数(此处未使用)
char buffer[BUFFER_SIZE]; // 消息接收缓冲区
while (1) { // 无限循环,持续接收消息
// 清空缓冲区(防止旧数据残留)
memset(buffer, 0, BUFFER_SIZE);
/* recv() 函数参数详解:
* client_socket: 客户端套接字描述符
* buffer: 接收数据缓冲区指针
* BUFFER_SIZE: 缓冲区大小
* 0: 标志位(0表示默认阻塞模式)
* 返回值: >0表示接收字节数,=0表示连接关闭,<0表示错误
*/
int bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
// 检查接收状态
if (bytes_received <= 0) {
perror("Connection closed or error"); // 打印错误信息
close(client_socket); // 关闭客户端套接字
exit(EXIT_FAILURE); // 退出程序(错误状态)
}
// 打印接收到的消息
printf("\n[Client]: %s\n", buffer); // 显示客户端消息
printf("[Server] Enter message: "); // 提示用户输入
fflush(stdout); // 强制刷新输出缓冲区(确保提示立即显示)
}
return NULL; // 线程退出(实际不会执行到这里)
}
int main() {
int server_fd; // 服务器套接字描述符
// 服务器和客户端地址结构(sockaddr_in用于IPv4)
struct sockaddr_in server_addr, client_addr;
socklen_t addr_len = sizeof(client_addr); // 客户端地址结构长度
pthread_t recv_thread; // 接收线程标识符
/* 1. 创建服务器套接字
* socket() 参数:
* AF_INET: IPv4地址族
* SOCK_STREAM: 流式套接字(TCP)
* 0: 协议(0表示默认协议)
* 返回值: 成功返回文件描述符,失败返回-1
*/
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed"); // 打印错误信息
exit(EXIT_FAILURE); // 退出程序
}
/* 2. 配置服务器地址 */
memset(&server_addr, 0, sizeof(server_addr)); // 清空地址结构
server_addr.sin_family = AF_INET; // IPv4地址族
server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有网络接口
server_addr.sin_port = htons(PORT); // 端口号(主机序转网络序)
/* 3. 绑定套接字到地址
* bind() 参数:
* server_fd: 服务器套接字描述符
* (struct sockaddr *)&server_addr: 地址结构指针(类型转换)
* sizeof(server_addr): 地址结构大小
*/
if (bind(server_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_fd); // 关闭套接字
exit(EXIT_FAILURE);
}
/* 4. 监听连接
* listen() 参数:
* server_fd: 服务器套接字描述符
* 1: 等待连接队列的最大长度
*/
if (listen(server_fd, 1) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
/* 5. 接受客户端连接
* accept() 参数:
* server_fd: 服务器套接字描述符
* (struct sockaddr *)&client_addr: 客户端地址结构指针
* &addr_len: 地址结构长度指针(输入/输出参数)
* 返回值: 新的套接字描述符(用于与客户端通信)
*/
if ((client_socket = accept(server_fd,
(struct sockaddr *)&client_addr,
&addr_len)) < 0) {
perror("Accept failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 打印客户端IP地址(inet_ntoa将二进制IP转换为点分十进制)
printf("Client connected! IP: %s\n", inet_ntoa(client_addr.sin_addr));
/* 创建接收线程
* pthread_create() 参数:
* &recv_thread: 线程标识符指针
* NULL: 线程属性(默认)
* receive_handler: 线程函数
* NULL: 传递给线程函数的参数
*/
if (pthread_create(&recv_thread, NULL, receive_handler, NULL) != 0) {
perror("Thread creation failed");
close(client_socket);
close(server_fd);
exit(EXIT_FAILURE);
}
char message[BUFFER_SIZE]; // 发送消息缓冲区
/* 6. 主线程处理消息发送 */
while (1) {
printf("[Server] Enter message: "); // 输入提示
// 从标准输入读取消息(包含换行符)
fgets(message, BUFFER_SIZE, stdin);
/* 移除换行符
* strcspn(message, "\n"): 查找第一个换行符的位置
* 将该位置字符设为'\0'(字符串结束符)
*/
message[strcspn(message, "\n")] = '\0';
/* 发送消息到客户端
* send() 参数:
* client_socket: 客户端套接字描述符
* message: 发送数据缓冲区指针
* strlen(message): 数据长度
* 0: 标志位
*/
if (send(client_socket, message, strlen(message), 0) < 0) {
perror("Send failed");
break; // 退出循环
}
}
// 清理资源(实际不会执行到这里)
close(client_socket);
close(server_fd);
return 0;
}
```
#### 客户端代码详解
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define SERVER_IP "127.0.0.1" // 服务器IP地址(本地回环)
#define PORT 8080 // 服务器端口
#define BUFFER_SIZE 1024 // 消息缓冲区大小
int sock = -1; // 连接服务器的套接字描述符
// 接收消息的线程函数
void *receive_handler(void *arg) {
char buffer[BUFFER_SIZE]; // 接收缓冲区
while (1) {
memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区
// 接收服务器消息
int bytes_received = recv(sock, buffer, BUFFER_SIZE, 0);
if (bytes_received <= 0) {
perror("Connection closed or error");
close(sock);
exit(EXIT_FAILURE);
}
printf("\n[Server]: %s\n", buffer); // 显示服务器消息
printf("[Client] Enter message: "); // 输入提示
fflush(stdout); // 刷新输出缓冲区
}
return NULL;
}
int main() {
struct sockaddr_in serv_addr; // 服务器地址结构
pthread_t recv_thread; // 接收线程标识符
// 1. 创建套接字(与服务器端相同)
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 配置服务器地址
memset(&serv_addr, 0, sizeof(serv_addr)); // 清空地址结构
serv_addr.sin_family = AF_INET; // IPv4地址族
serv_addr.sin_port = htons(PORT); // 端口号(主机序转网络序)
/* 将IP地址从文本转换为二进制格式
* inet_pton() 参数:
* AF_INET: IPv4地址族
* SERVER_IP: 点分十进制IP字符串
* &serv_addr.sin_addr: 存储转换结果的地址
* 返回值: 1-成功,0-无效地址,-1-错误
*/
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("Invalid address/Address not supported");
close(sock);
exit(EXIT_FAILURE);
}
/* 3. 连接服务器
* connect() 参数:
* sock: 客户端套接字描述符
* (struct sockaddr *)&serv_addr: 服务器地址结构指针
* sizeof(serv_addr): 地址结构大小
*/
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("Connected to server at %s:%d\n", SERVER_IP, PORT);
// 创建接收线程(与服务器端相同)
if (pthread_create(&recv_thread, NULL, receive_handler, NULL) != 0) {
perror("Thread creation failed");
close(sock);
exit(EXIT_FAILURE);
}
char message[BUFFER_SIZE]; // 发送消息缓冲区
// 4. 主线程处理消息发送
while (1) {
printf("[Client] Enter message: ");
fgets(message, BUFFER_SIZE, stdin);
message[strcspn(message, "\n")] = '\0'; // 移除换行符
// 发送消息到服务器
if (send(sock, message, strlen(message), 0) < 0) {
perror("Send failed");
break;
}
}
close(sock); // 关闭套接字
return 0;
}
```
### 关键参数详解表
#### 套接字函数参数
| 函数名 | 参数 | 类型 | 说明 |
|--------|------|------|------|
| `socket()` | domain | int | 地址族:`AF_INET`(IPv4), `AF_INET6`(IPv6) |
| | type | int | 套接字类型:`SOCK_STREAM`(TCP), `SOCK_DGRAM`(UDP) |
| | protocol | int | 协议:0表示默认协议 |
| `bind()` | sockfd | int | 套接字描述符 |
| | addr | const struct sockaddr * | 地址结构指针 |
| | addrlen | socklen_t | 地址结构长度 |
| `listen()` | sockfd | int | 套接字描述符 |
| | backlog | int | 等待连接队列的最大长度 |
| `accept()` | sockfd | int | 监听套接字描述符 |
| | addr | struct sockaddr * | 客户端地址信息(输出参数) |
| | addrlen | socklen_t * | 地址长度指针(输入/输出) |
| `connect()` | sockfd | int | 客户端套接字描述符 |
| | servaddr | const struct sockaddr * | 服务器地址结构 |
| | addrlen | socklen_t | 地址结构长度 |
| `send()` | sockfd | int | 已连接套接字描述符 |
| | buf | const void * | 发送数据缓冲区 |
| | len | size_t | 数据长度 |
| | flags | int | 发送标志(通常为0) |
| `recv()` | sockfd | int | 套接字描述符 |
| | buf | void * | 接收数据缓冲区 |
| | len | size_t | 缓冲区大小 |
| | flags | int | 接收标志(通常为0) |
#### 地址结构关键字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `sin_family` | sa_family_t | 地址族(`AF_INET`) |
| `sin_port` | in_port_t | 端口号(网络字节序) |
| `sin_addr.s_addr` | in_addr_t | IP地址(网络字节序) |
| `sin_zero` | char[8] | 填充字段(未使用) |
#### 字节序转换函数
| 函数 | 原型 | 说明 |
|------|------|------|
| `htons()` | `uint16_t htons(uint16_t hostshort)` | 主机序→网络序(16位) |
| `htonl()` | `uint32_t htonl(uint32_t hostlong)` | 主机序→网络序(32位) |
| `ntohs()` | `uint16_t ntohs(uint16_t netshort)` | 网络序→主机序(16位) |
| `ntohl()` | `uint32_t ntohl(uint32_t netlong)` | 网络序→主机序(32位) |
#### IP地址转换函数
| 函数 | 原型 | 说明 |
|------|------|------|
| `inet_pton()` | `int inet_pton(int af, const char *src, void *dst)` | 点分十进制→二进制 |
| `inet_ntop()` | `const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)` | 二进制→点分十进制 |
### 关键设计决策解释
1. **多线程架构**:
- 主线程处理用户输入和消息发送
- 独立线程处理消息接收(避免`recv()`阻塞用户输入)
- 使用`pthread`库实现跨平台线程支持
2. **缓冲区管理**:
- 固定大小缓冲区(1024字节)平衡性能和内存使用
- `memset()`清空缓冲区防止旧数据残留
- `strcspn()`移除换行符保证消息格式正确
3. **网络字节序处理**:
- `htons(PORT)`转换端口号(主机序→网络序)
- `inet_pton()`转换IP地址(文本→二进制)
- `inet_ntoa()`转换IP地址(二进制→文本)
4. **错误处理机制**:
- 所有关键系统调用检查返回值
- `perror()`输出可读的错误信息
- 错误时关闭套接字并退出程序
5. **资源清理**:
- `close()`关闭套接字描述符
- 程序退出前释放所有系统资源
###