1. TCP回显服务器
任务说明
本案例要求实现一个TCP回显服务器:服务器监听指定端口,接收客户端发送的任意文本消息,并将消息原样返回给客户端。通过该最小可运行示例,掌握Linux下socket编程的基本流程:创建socket、绑定地址、监听、接受连接、收发数据、关闭连接。
实现方案
服务器端使用socket()创建IPv4 TCP套接字,通过bind()绑定到本地回环地址的指定端口,listen()进入监听状态。主循环中调用accept()接受客户端连接,为每个连接创建子进程处理回显逻辑,父进程继续等待新连接。客户端使用connect()发起连接,通过send()/recv()完成一次消息发送与接收,验证服务器返回内容与发送内容一致。
代码实现
以下为服务器端完整代码(echo_server.c):
// echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
// 1. 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 2. 绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
close(server_fd);
exit(EXIT_FAILURE);
}
// 3. 监听
if (listen(server_fd, 5) < 0) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Echo server listening on port %d...\n", PORT);
// 4. 接受连接并处理
while (1) {
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
continue;
}
// 子进程处理回显
pid_t pid = fork();
if (pid == 0) {
close(server_fd); // 子进程关闭监听socket
ssize_t n;
while ((n = recv(client_fd, buffer, BUFFER_SIZE, 0)) > 0) {
send(client_fd, buffer, n, 0); // 原样返回
}
close(client_fd);
exit(0);
}
close(client_fd); // 父进程关闭已连接socket
}
close(server_fd);
return 0;
}
以下为客户端完整代码(echo_client.c):
// echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <message>\n", argv[0]);
exit(EXIT_FAILURE);
}
int sock;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
// 1. 创建socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 2. 连接服务器
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
perror("inet_pton");
close(sock);
exit(EXIT_FAILURE);
}
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
close(sock);
exit(EXIT_FAILURE);
}
// 3. 发送消息并接收回显
const char *msg = argv[1];
send(sock, msg, strlen(msg), 0);
ssize_t n = recv(sock, buffer, BUFFER_SIZE, 0);
if (n > 0) {
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
// 4. 关闭连接
close(sock);
return 0;
}
编译与运行
在Linux终端中执行以下命令:
gcc -o echo_server echo_server.c
gcc -o echo_client echo_client.c
先启动服务器(终端1):
./echo_server
再运行客户端(终端2):
./echo_client "Hello, TCP!"
验证方法
观察客户端输出,若打印内容为Received: Hello, TCP!,且与发送的消息完全一致,则说明回显功能正常。服务器终端会持续显示监听状态,不会输出每条消息内容。可通过Ctrl+C终止服务器进程。
预期输出
客户端执行后,终端显示:
Received: Hello, TCP!
该输出由代码逻辑直接推出:客户端发送argv[1]内容,服务器recv()后原样send()返回,客户端recv()接收并打印。若发送其他消息,输出内容将对应变化。