Select服务器

本文介绍了一个基于C语言实现的select机制的服务器程序,并对比了select机制与多进程/多线程服务器的特点。通过代码示例详细解释了select的工作原理及其在网络通信中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

int array_fds[1024];

static void Usage(const char *proc)
{
    printf("Usage: %s [local_ip] [local_port]\n",proc);
}

int startup(char *_ip, int _port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("sock");
        exit(2);
    }

    int flag = 1;
    setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = inet_addr(_ip);

    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        exit(3);
    }

    if(listen(sock,10)<0)
    {
        perror("listen");
        exit(4);
    }

    return sock;
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }

    int listenSock = startup(argv[1],atoi(argv[2]));
    int maxfd = 0;

    fd_set rfds;

    int array_size = sizeof(array_fds)/sizeof(array_fds[0]);
    array_fds[0] = listenSock;
    int i = 1;
    for(;i < array_size;i++)
    {
        array_fds[i] = -1;
    }

    while(1){
        struct timeval _timeout = {0,0};
        FD_ZERO(&rfds);
        maxfd = -1;
        for(i = 0;i<array_size;i++){
            if(array_fds[i] > 0){
                FD_SET(array_fds[i],&rfds);
                if(array_fds[i] > maxfd){
                    maxfd = array_fds[i];
                }
            }
        }

        switch(select(maxfd+1,&rfds,NULL,NULL,NULL/*&_timeout*/)){
            case 0:
                printf("timeout...\n");
                break;
            case -1:
                perror("select");
                break;
            default:
                {
                    int j = 0;
                    for(;j < array_size;j++){
                        if(array_fds[j]<0){
                            continue;
                        }
                        if(j == 0&&FD_ISSET(array_fds[j],&rfds)){
                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);
                            int new_fd = accept(array_fds[j],\
                                    (struct sockaddr*)&client,&len);
                            if(new_fd < 0){
                                perror("accept");
                                continue;
                            }
                            else{
                                printf("get a new client: (%s:%d)\n",\
                                        inet_ntoa(client.sin_addr),\
                                        ntohs(client.sin_port));
                                int k = 1;
                                for(;k < array_size;k++){
                                    if(array_fds[k] < 0){
                                        array_fds[k] = new_fd;
                                        break;
                                    }
                                }
                                if(k == array_size){
                                    close(new_fd);
                                }
                            }
                        }
                        else if(j != 0&&\
                                FD_ISSET(array_fds[j],&rfds))
                        {
                            char buf[10240];
                            ssize_t s = read(array_fds[j],buf,sizeof(buf)-1);
                            if(s>0)
                            {
                                buf[s] = 0;
                                printf("client say: %s\n",buf);
                            }
                            else if(s == 0)
                            {
                                printf("client quit!\n");
                                close(array_fds[j]);
                                array_fds[j] = -1;
                            }
                            else
                            {
                                perror("read");
                                close(array_fds[j]);
                                array_fds[j] = -1;
                            }
                        }
                        else
                        {

                        }
                    }
                }
                break;
        }
    }
    return 0;
}

client

#include<stdio.h>  
#include<sys/types.h>  
#include<netinet/in.h>  
#include<arpa/inet.h>  
#include<sys/socket.h>  
#include<stdlib.h>  
#include<string.h>  
#include<strings.h>  
#include<sys/stat.h>  
#include<unistd.h>  
static void Usage(char * proc)  
{  
    printf("Usage : %s [ip] [port]\n");  
}  

int main(int argc, char* argv[])  
{  
    if(argc != 3)  
    {  
        Usage(argv[0]);  
        return 1;  
    }  

    int sock = socket(AF_INET, SOCK_STREAM, 0);  
    struct sockaddr_in peer;  
    peer.sin_family = AF_INET;  
    peer.sin_port = htons(atoi(argv[2]));  
    peer.sin_addr.s_addr = inet_addr(argv[1]);  

    if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)  
    {  
        perror("connect");  
        return 2;  
    }  

    char buf[10240];  
    //int sfd = dup(STDOUT_FILENO);  
    while(1)  
    {  
        printf("Please Enter : ");  
        fflush(stdout);  
        ssize_t s = read(0, buf, sizeof(buf)-1);  
        int sfd = dup(STDOUT_FILENO);  
        if(s > 0)  
        {  
            buf[s-1] = 0;  
            //write(sock, buf, strlen(buf));  
            //输出重定向  
            //close(1);  
           // int new_fd = dup(sock);  
           int new_fd = dup2(sock, 1);  
            if(new_fd == -1)  
            {  
               perror("dup()");  
               return -1;  
            }  
            printf("%s",buf);  
            fflush(stdout);  
            //恢复stdout  
            dup2(sfd, STDOUT_FILENO);  

            ssize_t _s = read(sock, buf, sizeof(buf)-1);  
            if(_s > 0)  
            {  
               buf[_s] = 0;  
               printf("sever # %s \n", buf);  
            }  
        }  
    }  
    close(sock);  
    return 0;  
}  

总结select服务器优缺点,与多进程/多线程服务器进行对比:
使用select注意事项:
1.要将sock_fd加入到maxfd+1中,要不就无法检测到网络连接,会一直阻塞在select语句
2.通过存储每次连接的描述符,设置FD_SET函数,在遍历的去判断FD_ISSET处理。
3.我们可以看到select每次有数据到来时,需要遍历的去寻找所有可用的描述符,来判断其是否满足处理的条件。
4.select的通知机制,轮询的去查看是否在maxfd+1内有满足条件的描述符
select优缺点:
与多进程/多线程服务器进行对比 它的优点在于:
1、不需要建立多个线程、进程就可以实现一对多的通信。
2、可以同时等待多个文件描述符,效率比起多进程多线程来说要高很多。
3、select()的可移植性更好,在某些Unix系统上不支持poll()
4、select() 对于超时值提供了更好的精度:微秒,而poll是毫秒

与多进程/多线程服务器进行对比 它的缺点在于:
1、每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大 ,循环次数有点多;
2、同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 。
3、select支持的文件描述符数量太小了,默认是1024;

<think>嗯,用户让我介绍一下TCP中的select服务器。首先,我需要确认自己对select模型的理解是否正确。select是I/O多路复用的一种机制,用于处理多个套接字的异步事件。在TCP服务器中,使用select可以同时监控多个连接,提高效率。 用户可能已经了解基本的TCP服务器模型,比如单线程阻塞式的,但遇到并发连接数多时性能问题。这时候需要多路复用技术,而select是其中一种经典方法。我需要解释清楚select的工作原理,以及它在TCP服务器中的应用步骤。 接下来,我应该先介绍select的基本概念,包括它如何同时监视多个文件描述符,比如读、写、异常集合。然后说明select的工作流程,包括设置fd_set、调用select函数、检查就绪的描述符等步骤。需要注意select的优缺点,比如有最大文件描述符数量限制,以及效率问题。 可能用户需要知道如何实际编写一个select服务器,所以需要给出一个简单的代码示例框架,用中文注释说明每个部分的作用。同时,要指出select的局限性,比如FD_SETSIZE的限制,以及在高并发场景下可能不如epoll或kqueue高效。 还需要比较select和其他多路复用机制的区别,这样用户能明白在什么情况下选择select是合适的。例如,在连接数不高且跨平台需求强的情况下,select可能更合适;而在高并发Linux服务器中,epoll会更优。 最后,总结一下select的适用场景和注意事项,确保用户理解其实际应用中的权衡。检查是否有遗漏的关键点,比如超时机制、如何正确处理多个就绪的fd,以及错误处理等。</think>TCP中select服务器的实现原理与工作流程如下: 1. 基本概念 select是一种I/O多路复用技术,通过单线程同时监控多个socket文件描述符的状态变化。它能检测三种事件: - 可读事件(数据到达) - 可写事件(可发送数据) - 异常事件(连接错误) 2. 核心函数 $$select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)$$ 3. 实现步骤: (1) 创建监听socket ```c int listen_fd = socket(AF_INET, SOCK_STREAM, 0); bind(listen_fd, ...); listen(listen_fd, BACKLOG); ``` (2) 初始化fd_set ```c fd_set read_fds; FD_ZERO(&read_fds); FD_SET(listen_fd, &read_fds); int max_fd = listen_fd; ``` (3) 事件循环 ```c while(1) { fd_set tmp_fds = read_fds; int ready = select(max_fd+1, &tmp_fds, NULL, NULL, NULL); if(FD_ISSET(listen_fd, &tmp_fds)) { // 处理新连接 int conn_fd = accept(...); FD_SET(conn_fd, &read_fds); max_fd = (conn_fd > max_fd) ? conn_fd : max_fd; } for(int fd = listen_fd+1; fd <= max_fd; ++fd) { if(FD_ISSET(fd, &tmp_fds)) { // 处理客户端数据 recv(fd, ...); // 业务逻辑处理 } } } ``` 4. 特点分析 - 优点: - 跨平台支持(Windows/Linux/BSD) - 单线程处理多连接 - 避免多线程上下文切换开销 - 缺点: - 文件描述符数量限制(FD_SETSIZE通常为1024) - 线性扫描所有fd效率低 - 需要每次重置fd_set - 高延迟(时间复杂度O(n)) 5. 适用场景 - 连接数 < 1000的轻量级服务 - 需要跨平台兼容的客户端程序 - 短连接为主的网络服务 6. 优化建议 - 使用非阻塞socket避免单连接阻塞 - 采用时间轮记录连接活跃时间 - 配合内存池减少内存分配开销 - 设置合理的select超时时间 对比epoll: $$吞吐量 = \frac{有效数据量}{单位时间} \quad select\ll epoll\ (当连接数>1000时)$$ 实际开发中,Linux平台推荐使用epoll,Windows平台建议使用IOCP,select适合作为学习多路复用的入门模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值