TCP服务器如何使用select处理多客户连接

TCP是一种面向连接的通信方式,一个TCP服务器难免会遇到同时处理多个用户的连接请求的问题,本文用一个简化的实例说明如何在一个TCP服务器程序中,使用select处理同时出现的多个客户连接,文章给出了程序源代码,本文假定读者已经具备了基本的socket编程知识,熟悉基本的服务器/客户端模型架构。

1. 基本思路

  • TCP服务器端程序,对于每一个连接请求,可以使用多线程的方式为每一个连接启动一个线程处理该连接的通信,但使用多线程的方式,通常认为有如下缺点:
    1. 多线程编程和调试相对都比较难,而且有时会出现无法预知的问题;
    2. 上下文切换的开销较大;
    3. 对于巨大量的连接,可扩展性不足;
    4. 可能发生死锁。
  • 使用select处理多连接的基本思路
    1. 建立一个用于侦听的socket,叫做master_socket;
    int master_socket = socket(AF_INET , SOCK_STREAM , 0);
    
    1. 建立一个sockets数组,用于存储已经与master_socket建立连接的socket,叫做client_socket,初始化时全部清0,数组的长度即为程序允许的最大连接数;
    #define MAX_CLIENTS     30
    
    int client_socket[MAX_CLIENTS];
    int i;
    for (i &#
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 下面是一个简单的TCP服务器代码,使用select模型: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define MAX_CLIENT 10 #define BUF_SIZE 1024 int main(int argc, char *argv[]) { int server_sockfd, client_sockfd; int server_len, client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; int result; fd_set readfds, testfds; char buf[BUF_SIZE]; server_sockfd = socket(AF_INET, SOCK_STREAM, 0); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(9734); server_len = sizeof(server_address); bind(server_sockfd, (struct sockaddr *)&server_address, server_len); listen(server_sockfd, 5); FD_ZERO(&readfds); FD_SET(server_sockfd, &readfds); while (1) { int fd; int nread; testfds = readfds; printf("server waiting\n"); result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0); if (result < 1) { perror("server5"); exit(1); } for (fd = 0; fd < FD_SETSIZE; fd++) { if (FD_ISSET(fd, &testfds)) { if (fd == server_sockfd) { client_len = sizeof(client_address); client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len); FD_SET(client_sockfd, &readfds); printf("adding client on fd %d\n", client_sockfd); } else { ioctl(fd, FIONREAD, &nread); if (nread == 0) { close(fd); FD_CLR(fd, &readfds); printf("removing client on fd %d\n", fd); } else { read(fd, buf, nread); write(fd, buf, nread); } } } } } ### 回答2: 使用C语言编写一个TCP服务器,并使用select模型可以实现多路复用,同时处理多个客户连接。 首先,我们需要包含相关的头文件: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> ``` 接下来,我们定义服务器的IP地址和端口号,并创建一个存储客户端套接字的文件描述符集合: ```c #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 #define MAX_CLIENTS 10 int main() { int server_sock, client_sock, max_sock, activity, i, valread; int client_sockets[MAX_CLIENTS] = {0}; fd_set read_fds; // 创建服务器套接字 if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置服务器套接字选项 int opt = 1; if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { perror("setsockopt failed"); exit(EXIT_FAILURE); } ``` 然后,我们将服务器的IP地址和端口号绑定到服务器套接字上,并开始监听客户连接: ```c // 绑定服务器地址和端口号 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))<0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听客户连接 if (listen(server_sock, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } ``` 接下来,我们需要创建一个消息缓冲区,并设置主循环以侦听客户连接处理连接客户端的数据传输: ```c char buffer[1024]; while (1) { // 清空文件描述符集合 FD_ZERO(&read_fds); // 添加服务器套接字到集合 FD_SET(server_sock, &read_fds); max_sock = server_sock; // 添加客户端套接字到集合 for (i = 0; i < MAX_CLIENTS; i++) { client_sock = client_sockets[i]; if (client_sock > 0) FD_SET(client_sock, &read_fds); if (client_sock > max_sock) max_sock = client_sock; } // 多路复用,等待活动发生 activity = select(max_sock + 1, &read_fds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { perror("select failed"); exit(EXIT_FAILURE); } // 处理新的客户连接 if (FD_ISSET(server_sock, &read_fds)) { if ((client_sock = accept(server_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } // 将客户端套接字添加到列表中 for (i = 0; i < MAX_CLIENTS; i++) { if (client_sockets[i] == 0) { client_sockets[i] = client_sock; break; } } } // 处理连接客户端的数据传输 for (i = 0; i < MAX_CLIENTS; i++) { client_sock = client_sockets[i]; if (FD_ISSET(client_sock, &read_fds)) { if ((valread = read(client_sock, buffer, 1024)) == 0) { // 客户端关闭连接 close(client_sock); client_sockets[i] = 0; } else { // 处理客户端的数据 // ... } } } } return 0; } ``` 以上是一个使用C语言编写的TCP服务器使用select模型实现多路复用,可以同时处理多个客户连接。该服务器可以监听客户端的连接请求,并接受新的客户连接。在主循环中,通过多路复用等待活动发生,一旦有新的连接或已连接客户端有数据传输,就进行相应的处理处理新的客户连接时,将客户端套接字添加到列表中,处理连接客户端的数据传输时,可以根据需要进行相应的操作。 ### 回答3: TCP服务器使用select模型可以实现同时处理多个连接请求。下面是一个使用C语言编写的简单TCP服务器的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #define MAX_CLIENTS 10 int main() { int server_fd, client_fds[MAX_CLIENTS]; fd_set read_fds, active_fds; struct sockaddr_in server_addr, client_addr; socklen_t addr_len = sizeof(client_addr); int max_fd, num_clients = 0, i; char buffer[1024]; // 创建服务器端socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); } server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(8888); // 绑定服务器地址 if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Bind failed"); exit(EXIT_FAILURE); } // 监听连接请求 if (listen(server_fd, 10) < 0) { perror("Listen failed"); exit(EXIT_FAILURE); } // 初始化文件描述符集合 FD_ZERO(&active_fds); FD_SET(server_fd, &active_fds); max_fd = server_fd; printf("Server started, waiting for connections...\n"); while (1) { read_fds = active_fds; // 通过select等待读事件发生 if (select(max_fd + 1, &read_fds, NULL, NULL, NULL) < 0) { perror("Select failed"); exit(EXIT_FAILURE); } // 检查是否有新的连接请求 if (FD_ISSET(server_fd, &read_fds)) { int new_client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len); // 将新的客户端socket加入到文件描述符集合中 FD_SET(new_client_fd, &active_fds); if (new_client_fd > max_fd) { max_fd = new_client_fd; } printf("New connection, socket fd is %d, IP is: %s, port is: %d\n", new_client_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); num_clients++; if (num_clients == MAX_CLIENTS) { printf("Maximum number of clients reached, connection rejected.\n"); close(new_client_fd); } } // 检查其他已经连接客户端是否有数据到达 for (i = 0; i <= max_fd; i++) { if (FD_ISSET(i, &read_fds)) { if (recv(i, buffer, sizeof(buffer), 0) == 0) { // 客户端关闭连接 printf("Client %d closed the connection.\n", i); close(i); FD_CLR(i, &active_fds); num_clients--; } else { // 处理客户端发送的数据 printf("Received data from client %d: %s\n", i, buffer); // 向客户端回复数据 send(i, "Server received your message.", strlen("Server received your message."), 0); memset(buffer, 0, sizeof(buffer)); } } } } return 0; } ``` 该服务器使用select模型实现同时处理多个连接请求。服务器首先创建一个socket,然后绑定服务器地址,并开始监听连接请求。在事件循环中,通过select等待读事件发生,若有新的连接请求,将新的客户端socket加入文件描述符集合中,并以此处理其他已经连接客户端的数据。服务器收到客户端发送的数据后可进行相应的处理,并向客户端回复数据。 以上是一个简单的TCP服务器的实现示例,可作为参考和起点进行进一步的开发和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

whowin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值