Linux C语言中,TCP服务器通常使用`select()`函数来管理多个套接字连接,因为`select()`可以高效地阻塞和唤醒进程,适用于非阻塞I/O。下面是一个简单的TCP服务器示例,包括了详细的注释:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define MAX_CLIENTS 5
#define BUFFER_SIZE 1024
// 创建一个新的socket
int create_socket(int family, int type, int protocol) {
int sock = socket(family, type, protocol);
if (sock == -1) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
return sock;
}
// 绑定并监听socket
void bind_and_listen(int sock, const char *ip, int port) {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // 将port转换为网络字节序
inet_pton(AF_INET, ip, &addr.sin_addr);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error binding to address");
exit(EXIT_FAILURE);
}
listen(sock, MAX_CLIENTS);
printf("Server listening on %s:%d...\n", ip, port);
}
// 接受新的客户端连接
int accept_new_client(int server_sock) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int new_sock = accept(server_sock, (struct sockaddr *)&client_addr, &addr_len);
if (new_sock == -1) {
perror("Error accepting client connection");
exit(EXIT_FAILURE);
}
printf("Accepted connection from %s\n", inet_ntoa(client_addr.sin_addr));
return new_sock;
}
// 主循环处理客户端请求
void handle_client(int client_sock) {
char buffer[BUFFER_SIZE];
ssize_t bytes_received;
while ((bytes_received = recv(client_sock, buffer, BUFFER_SIZE, 0)) > 0) {
buffer[bytes_received] = '\0'; // 结束字符串
printf("Received message from client: %s\n", buffer);
// 发送响应给客户端(这里仅简单打印信息)
send(client_sock, "Message received!", strlen("Message received!"), 0);
}
if (bytes_received == -1) {
perror("Error receiving data from client");
} else if (bytes_received == 0) { // 客户端断开连接
printf("Client disconnected.\n");
close(client_sock);
}
}
int main() {
int server_sock = create_socket(AF_INET, SOCK_STREAM, 0);
// 绑定和监听
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 静态IP地址0.0.0.0表示所有网络接口
server_addr.sin_port = htons(8080);
bind_and_listen(server_sock, inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
fd_set fds; // 用于存储文件描述符集合
FD_ZERO(&fds); // 清空描述符集合
FD_SET(server_sock, &fds); // 添加服务器套接字到集合
struct timeval timeout;
timeout.tv_sec = 10; // 设置超时时间,10秒
timeout.tv_usec = 0;
while (true) {
int max_fd = server_sock;
select(max_fd + 1, &fds, NULL, NULL, &timeout); // 等待活动
if (FD_ISSET(server_sock, &fds)) {
// 新的连接或已存在的连接有数据可读
int new_client_sock = accept_new_client(server_sock);
if (new_client_sock != -1) {
FD_SET(new_client_sock, &fds); // 添加新连接到描述符集合
max_fd = new_client_sock;
}
} else {
break; // 没有任何活动,退出循环
}
for (int i = 0; i <= max_fd; ++i) {
if (FD_ISSET(i, &fds)) {
handle_client(i);
}
}
}
close(server_sock);
printf("Server stopped.\n");
return 0;
}
```