Linux poll服务器

本文介绍了一个简易的TCP服务器实现,该服务器使用C语言编写,并利用poll机制处理多个客户端连接请求。服务器通过监听指定的IP地址和端口接收客户端连接,能够接受新连接、读取客户端消息并发送回应。

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

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

#define MAX_POLL_SIZE 128

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

int starup(const char* ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket");
        exit(2);
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip);
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

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

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

    return sock;
}

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

    int listen_sock = starup(argv[1],atoi(argv[2]));
    struct pollfd fds[MAX_POLL_SIZE];
    fds[0].fd = listen_sock;
    fds[0].events = POLLIN;
    int i = 1;
    for(;i<MAX_POLL_SIZE;++i)
    {
        fds[i].fd=-1;
    }
    while(1)
    {
        int num = poll(fds,MAX_POLL_SIZE,-1);
        switch(num)
        {
        case 0:
            printf("nothing ready\n");
            break;
        case -1:
            perror("poll");
            break;
        default:
            {
                for(i = 0;i < MAX_POLL_SIZE; ++ i)
                {
                    if(fds[i].fd == -1)
                        continue;
                    else if(i == 0 && fds[0].revents & POLLIN)
                    {
                        struct sockaddr_in client;
                        socklen_t len = sizeof(client);
                        int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); 
                        if(new_sock < 0)
                        {
                            perror("accept");
                            continue;
                        }

                        printf("get a new client:%s %d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                        int j = 1;
                        for(;j < MAX_POLL_SIZE; ++j)
                        {
                            if(fds[j].fd == -1)
                                break;
                        }

                        if(j < MAX_POLL_SIZE)
                        {
                            fds[j].fd = new_sock;
                            fds[j].events = POLLIN | POLLOUT;
                        }
                        else
                        {
                            printf("servet is busy!!\n");
                            close(new_sock);
                        }
                    }
                    else if(fds[i].revents & POLLIN)
                    {
                        char buf[1024];
                        ssize_t s = read(fds[i].fd,buf,sizeof(buf)-1);
                        if(s < 0)
                        {
                            perror("read ");
                            close(fds[i].fd);
                            fds[i].fd=-1;
                            continue;
                        }
                        else if(s == 0)
                        {
                            printf("client quit!!\n");
                            close(fds[i].fd);
                            fds[i].fd=-1;
                            continue;
                        }
                        buf[s]=0;
                        printf("%s\n",buf);
                    }
                    else if(fds[i].revents & POLLOUT) {
                        char* buf = "hello word";
                        write(fds[i].fd,buf,strlen(buf));
                        close(fds[i].fd);
                        fds[i].fd=-1;
                        printf("server close client\n");
                    }
                }//for
            }//default
        }//switch
    }//while

    return 0;
}
### LinuxPoll 函数的使用与原理 #### 1. **Poll 函数概述** `poll` 是 Linux 系统中的一个重要函数,属于 I/O 多路复用技术的一部分。它允许应用程序同时监控多个文件描述符的状态变化(如可读、可写或异常),从而提高程序性能和资源利用率[^3]。 --- #### 2. **Poll 的基本语法** `poll` 函数的标准定义如下: ```c #include <poll.h> int poll(struct pollfd fds[], nfds_t nfds, int timeout); ``` - 参数说明: - `fds[]`: 这是一个数组,每个元素都是一个 `struct pollfd` 类型的对象,用于指定需要监控的文件描述符以及期望发生的事件。 - `nfds`: 数组 `fds[]` 的长度,即需要监控的文件描述符数量。 - `timeout`: 超时时间,单位为毫秒;如果设为 `-1`,则无限期阻塞直到某个文件描述符准备就绪;如果是 `0`,则立即返回而不阻塞。 - 返回值: - 成功时返回已经准备好进行操作的文件描述符的数量; - 如果超时,则返回 `0`; - 发生错误时返回 `-1` 并设置相应的 `errno` 值。 --- #### 3. **Poll 结构体详解** `poll` 使用的核心数据结构是 `struct pollfd`,其定义如下: ```c typedef struct { int fd; // 文件描述符 short events; // 请求的事件掩码 short revents; // 实际发生的事件掩码 } pollfd; ``` - `fd`: 需要被监控的文件描述符。 - `events`: 用户希望监控的事件类型,常见的取值包括: - `POLLIN`: 数据可读。 - `POLLOUT`: 数据可写。 - `POLLERR`: 错误条件。 - `POLLHUP`: 挂起事件。 - `POLLNVAL`: 无效请求。 - `revents`: 当 `poll` 返回时,内核会在此字段中填充实际发生的事件[^3]。 --- #### 4. **Poll 的工作原理** 当调用 `poll` 时,系统会在内核空间执行以下步骤: 1. 初始化等待队列:通过调用内部函数 `poll_initwait()` 来初始化轮询所需的等待队列[^4]。 2. 注册回调函数:对于每一个传入的文件描述符,都会调用设备驱动程序提供的 `poll` 方法来检查当前状态,并将当前线程加入到对应设备的等待队列中[^2]。 3. 阻塞等待:如果没有满足条件的文件描述符,那么当前线程会被挂起到等待队列中,直到有文件描述符变为就绪或者达到设定的超时时间[^1]。 4. 唤醒线程:一旦某些文件描述符上的事件发生,相关联的进程将会被唤醒并继续运行[^2]。 --- #### 5. **Poll 的简单示例** 下面给出一段完整的代码示例,演示如何使用 `poll` 监控标准输入流和一个 TCP 客户端连接的情况: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <poll.h> #define BUFFER_SIZE 1024 int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); struct pollfd fds[2]; fds[0].fd = STDIN_FILENO; fds[0].events = POLLIN; fds[1].fd = sockfd; fds[1].events = POLLIN; char buffer[BUFFER_SIZE]; while (1) { int ret = poll(fds, 2, -1); if (ret == -1) { perror("poll"); break; } if (fds[0].revents & POLLIN) { // 标准输入可读 ssize_t bytes_read = read(STDIN_FILENO, buffer, BUFFER_SIZE - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; write(sockfd, buffer, strlen(buffer)); // 向服务器发送消息 } } if (fds[1].revents & POLLIN) { // Socket 可读 ssize_t bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0); if (bytes_received > 0) { buffer[bytes_received] = '\0'; printf("Server says: %s\n", buffer); } else if (bytes_received == 0) { printf("Connection closed by peer.\n"); break; } else { perror("recv"); break; } } } close(sockfd); return 0; } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值