浪潮君 的学生作业:
服务端
#include // 标准输入输出头文件,包含 printf、perror 等
#include // 包含 exit、malloc 等函数
#include // 包含字符串处理函数,如 memset、strlen
#include // 包含 close、read、write 等 Unix/Linux 系统调用
#include // 包含 IP 地址转换函数,如 inet_ntop、htons
#include // 包含 socket 函数与结构体定义
#define SERVER_PORT 8890 // 服务端监听的端口号
#define BACKLOG 5 // 最大连接请求队列长度
#define BUFFER_SIZE 1024 // 缓冲区最大长度(接收/发送)
// 创建 TCP 服务器监听 socket,并绑定指定 IP 和端口
int tcp_socket_create(const char *ip, int port, int backlog) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建 IPv4、TCP 套接字
if (sockfd < 0) {
perror("socket failed"); // 创建失败时输出错误信息
return -1;
}
// 设置 socket 地址重用选项,防止 bind 报错 "Address already in use"
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); // 初始化结构体为 0
addr.sin_family = AF_INET; // 设置地址族为 IPv4
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有本地 IP 地址(0.0.0.0)
addr.sin_port = htons(SERVER_PORT); // 设置监听端口,使用网络字节序
// 绑定 socket 到本地地址与端口
if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind failed"); // 绑定失败
close(sockfd);
return -1;
}
// 启动监听,将 socket 设为被动连接模式
if (listen(sockfd, backlog) < 0) {
perror("listen failed"); // 监听失败
close(sockfd);
return -1;
}
return sockfd; // 返回监听套接字
}
// 处理每个客户端连接的函数
void handle_client(int conn_fd, struct sockaddr_in *client_addr) {
char buffer[BUFFER_SIZE]; // 接收客户端消息的缓冲区
char client_ip[INET_ADDRSTRLEN]; // 存储客户端 IP 字符串
// 将客户端 IP 地址转换为可读字符串格式
inet_ntop(AF_INET, &client_addr->sin_addr, client_ip, sizeof(client_ip));
// 将端口号从网络字节序转换为主机字节序
int client_port = ntohs(client_addr->sin_port);
printf("客户端已连接:%s:%d\n", client_ip, client_port);
// 循环接收客户端消息并回显
while (1) {
// 接收客户端消息
ssize_t len = recv(conn_fd, buffer, sizeof(buffer) - 1, 0);
if (len < 0) {
printf("客户端断开连接\n"); // 接收失败
break;
}
buffer[len] = '\0'; // 添加字符串结束符,构成合法 C 字符串
printf("收到消息:%s\n", buffer);
// 构造服务端回显响应内容
char reply[BUFFER_SIZE];
snprintf(reply, sizeof(reply), "服务端已收到: %s", buffer);
// 将回应内容发回客户端
send(conn_fd, reply, strlen(reply), 0);
}
// 关闭客户端连接 socket
close(conn_fd);
}
// 主函数:程序入口
int main(int argc, char *argv[]) {
// 创建监听 socket,并绑定本地端口
int listen_fd = tcp_socket_create("0.0.0.0", SERVER_PORT, BACKLOG);
if (listen_fd < 0) {
fprintf(stderr, "监听 socket 创建失败。退出\n");
return -1;
}
printf("服务端启动,监听端口 %d\n", SERVER_PORT);
// 主循环:接收并处理客户端连接
while (1) {
struct sockaddr_in client_addr; // 用于存储客户端地址信息
socklen_t client_len = sizeof(client_addr);
// 接受客户端连接请求,阻塞等待连接
int conn_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);
if (conn_fd < 0) {
perror("accept failed"); // 接收失败
continue;
}
// 处理客户端连接
handle_client(conn_fd, &client_addr);
}
// 关闭监听 socket
close(listen_fd);
return 0;
}
客户端
#include // 标准输入输出,如 printf、fprintf
#include // 包含 exit 函数
#include // 字符串处理,如 strlen、strcspn
#include // 包含 close 函数
#include // 网络地址转换函数,如 inet_pton
#include // socket API 函数
#define SERVER_IP "127.0.0.1" // 服务器 IP 地址(本地环回地址)
#define SERVER_PORT 8890 // 服务器端口号(需与服务端保持一致)
#define BUFFER_SIZE 1024 // 缓冲区最大长度
// 创建 TCP 客户端 socket 并连接服务器
int tcp_client_connect(const char *ip, int port) {
// 创建 socket(IPv4 + TCP)
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket 创建失败");
return -1;
}
// 配置服务器地址结构
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // 清零初始化
server_addr.sin_family = AF_INET; // IPv4
server_addr.sin_port = htons(port); // 设置端口(转为网络字节序)
// 将 IP 字符串转换为二进制形式
if (inet_pton(AF_INET, ip, &server_addr.sin_addr)