linux中网络编程I/O模型---select

本文深入探讨了Linux网络编程中的I/O模型——select模型,详细解析了select函数的使用方法,包括其参数、相关结构体及函数。通过具体代码示例,展示了如何使用select模型实现一个简单的server和client,涵盖了socket创建、监听、接受连接、文件描述符集合操作等关键步骤。

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

linux中网络编程I/O模型—select(非阻塞模型)

  1. select函数原型:
    int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
    这里用到两个结构体fd_set和timeval。fd_set可以理解为一个集合,时存放文件描述符的(想STDOUT_FILENO、STDIN_FILENO以及socket函数的返回的文件的句柄)
    select参数列表:
    maxfd:指集合中所有文件描述负的范围,即所有文件描述符的最大值加1
    readfds:监视可读的文件描述符的集合,即readfds中有文件可读,select返回一个大于0的值,在根据timeout判断是否超时,超时返回0,发生错误返回负数。
    writefds:监视可读文件描述集合
    errorfds:监视文件描述符异常的集合
    timeout:设置超时时间

  2. fd_set相关的函数:
    fd_set set; //初始化
    FD_ZERO(&set); //清空
    FD_SET(fd, &set); //添加文件描述符到set
    FD_CLR(fd, &set); //将fd从set中删除
    FD_ISSET(fd, &set); //检查set中fd是否可读

  3. 下面使用select模型实现一个简单的server、client
    server.cpp

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


#define DEFAUT_PORT 6666


int main()
{
    int serverfd;     //监听socket:serverfd;数据传输socket:acceptfd
    struct sockaddr_in my_addr;  //本机地址信息
    unsigned int lisnum = 10;
    //初始化流式socket
    if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
        return -1;
    }
    printf("socket ok\n");
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(DEFAUT_PORT);    //将主机字节序的监听端口转换成网络字节序
    my_addr.sin_addr.s_addr = INADDR_ANY;    //监听来自所有网络的消息
    bzero(&(my_addr.sin_zero), 0);
    //将需要监听的socket:serverfd与本地主机绑定
    if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        return -2;
    }
    printf("bind ok\n");
    if (listen(serverfd, lisnum) == -1)
    {
        perror("listen");
        return -3;
    }
    printf("listen ok\n");


    fd_set client_fdst;     //监控文件描述符集合
    int maxsock;             //监控文件描述符中最大的文件号
    struct timeval tv;      //超时返回时间
    int client_sockfd[5]; //存放存活的sockfd
    bzero((void*)client_sockfd, sizeof(client_sockfd));
    int conn_amount = 0;  //记录描述符数量
    maxsock = serverfd;
    char buffer[1024];
    int ret = 0;
    while (1)
    {
        //初始化文件描述符到集合
        FD_ZERO(&client_fdst);
        //将需要监控的服务器socket:serverfd添加到文件描述符集合
        FD_SET(serverfd, &client_fdst);
        //设置超时时间
        tv.tv_sec = 30;
        tv.tv_usec = 0;
        //将活动的句柄添加到集合
        for (int index = 0; index < 5; index++)
        {
            //程序listen的参数为5,故index必须小于5
            if (client_sockfd[index] != 0)
            {
                FD_SET(client_sockfd[index], &client_fdst);
            }
        }
        ret = select(maxsock + 1, &client_fdst, NULL, NULL, &tv);
        if (ret < 0)
        {
            perror("select error");
            break;
        }
        else if (ret == 0)
        {
            printf("timeout!\n");
            continue;
        }
        //轮询所有的文件描述符
        for (int index = 0; index < conn_amount; index++)
        {
            //FD_ISSET检查client_sockfd是否可读, >0可读写
            if (FD_ISSET(client_sockfd[index], &client_fdst))
            {
                printf("start recv from client[%d]:\n", index);
                ret = recv(client_sockfd[index], buffer, sizeof(buffer), 0);
                if (ret <= 0)
                {
                    printf("client[%d] close\n", index);
                    close(client_sockfd[index]);
                    //将文件描述符从集合中删除
                    FD_CLR(client_sockfd[index], &client_fdst);
                    client_sockfd[index] = 0;
                    --conn_amount;
                }
                else
                {
                    printf("recv from client[%d] : %s\n", index, buffer);
                }
            }
        }
        //检查是否有新链接,如果有新连接,添加到client_sockfd集合中
        if (FD_ISSET(serverfd, &client_fdst))
        {
            struct sockaddr_in client_addr;
            size_t size = sizeof(struct sockaddr_in);
            int sock_client = accept(serverfd, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));
            if (sock_client < 0)
            {
                perror("accept error");
                continue;
            }
            //把链接添加到集合中
            if (conn_amount < 5)
            {
                client_sockfd[conn_amount++] = sock_client;
                bzero(buffer, sizeof(buffer));
                strcpy(buffer, "This is server! Welcome!\n");
                send(sock_client, buffer, sizeof(buffer), 0);
                printf("New connection clinet[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                printf("new client : %d\n", sock_client);
                printf("new maxsock : %d\n", maxsock);
                bzero(buffer, sizeof(buffer));
                ret = recv(sock_client, buffer, sizeof(buffer), 0);
                if (ret < 0)
                {
                    perror("recv error!\n");
                    close(serverfd);
                    return -1;
                }
                printf("recv : %s\n", buffer);
                if (sock_client > maxsock)
                {
                    maxsock = sock_client;
                }
                else
                {
                    printf("max connections!!!quit!\n");
                    break;
                }
            }
        }
    }
    for (int index = 0; index < 5; index++)
    {
        if (client_sockfd[index] != 0)
        {
            close(client_sockfd[index]);
        }
    }
    close(serverfd);
    return 0;
}

client.cpp

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



#define DEFAUT_PORT 6666


int main(int argc, char *argv[])
{
    int connfd = 0;
    int clen = 0;
    struct sockaddr_in client;
    if (argc < 2)
    {
        printf("Uasge : Client [Server IP address]\n");
        return -1;
    }
    client.sin_family = AF_INET;
    client.sin_port = htons(DEFAUT_PORT);
    client.sin_addr.s_addr = inet_addr(argv[1]);
    connfd = socket(AF_INET, SOCK_STREAM, 0);
    if (connfd < 0)
    {
        perror("socket");
        return -1;
    }
    if (connect(connfd, (struct sockaddr*)&client, sizeof(struct sockaddr)) < 0)
    {
        perror("connect");
        return -1;
    }
    char buffer[1024];
    bzero(buffer, sizeof(buffer));
    recv(connfd, &buffer, sizeof(buffer), 0);
    printf("recv : %s\n", buffer);
    bzero(buffer, sizeof(buffer));
    strcpy(buffer, "this is client!\n");
    send(connfd, buffer, sizeof(buffer), 0);
    while (1)
    {
        bzero(buffer, sizeof(buffer));
        scanf("%s", buffer);
        int p = strlen(buffer);
        buffer[p] = '\n';
        send(connfd, buffer, sizeof(buffer), 0);
        printf("i have send buffer\n");
    }
    close(connfd);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值