欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于select的回声服务器

程序员文章站 2024-03-23 10:14:40
...

一:函数理解

(1)
要想通过select函数处理多个客户端的连接,我们得需要一个函数把这些文件描述符集中起来吧,不然怎么处理多个客户端的链接呢?先来看select函数的第234参数,这几个参数,都是fd_set类型的变量,用来把文件描述符集中起来,第2个参数是集中想读的文件描述符,第3个参数是集中想写的文件描述符,第4个参数是集中异常的文件描述符。后面会举例子,先记这么多吧。
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);

(2)
我们怎么通过fd_set变量集中文件描述符?或者清除不感兴趣的文件描述符?有以下4个函数完成这些操作。

FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位

当客户端连入时,我们怎么知道是那个文件描述符发生了变化?以select函数来说,如果我们关心读事件,那么当可读时,相应的变化就会记录在select函数的第二个实参里,但是,我们不知道这个变化了的文件描述符的值是多少,所以要遍历,这也是select函数的缺点之一,所以它的第一个参数总是记录着最大的文件描述符是多少,因为后面遍历需要。

二:例程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>

#define BUF_SIZE 100
void error_handling(char *buf);

int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    struct timeval timeout;
    fd_set reads, cpy_reads;

    socklen_t adr_sz;
    int fd_max, str_len, fd_num, i;
    char buf[BUF_SIZE];
    if(argc!=2) {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));
    
    if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");
    if(listen(serv_sock, 5)==-1)
        error_handling("listen() error");

    FD_ZERO(&reads);
    FD_SET(serv_sock, &reads);
    /*目前serv_sock文件描述符的数值是最大的*/
    fd_max=serv_sock;

    while(1)
    {
        /*这个动作要有,因为select函数返回时
        cpy_reads都会发生变化,而后续有新的客户端连   入时,也需要通过reads记录相应的文件描述符,所以每次循环到select函数时,cpy_reads的值都会是所有的文件描述符的集合*/
        cpy_reads=reads;
        timeout.tv_sec=5;
        timeout.tv_usec=5000;

        if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1)
            break;
        
        if(fd_num==0)
            continue;

        for(i=0; i<fd_max+1; i++)
        {
            if(FD_ISSET(i, &cpy_reads))
            {
                if(i==serv_sock)     // connection request!
                {
                    adr_sz=sizeof(clnt_adr);
                    clnt_sock=
                        accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
                    FD_SET(clnt_sock, &reads);
                    if(fd_max<clnt_sock)
                        fd_max=clnt_sock;
                    printf("connected client: %d \n", clnt_sock);
                }
                else    // read message!
                {
                    str_len=read(i, buf, BUF_SIZE);
                    if(str_len==0)    // close request!
                    {
                        FD_CLR(i, &reads);
                        close(i);
                        printf("closed client: %d \n", i);
                    }
                    else
                    {
                        write(i, buf, str_len);    // echo!
                    }
                }
            }
        }
    }
    close(serv_sock);
    return 0;
}

void error_handling(char *buf)
{
    fputs(buf, stderr);
    fputc('\n', stderr);
    exit(1);
}

基于select的回声服务器
基于select的回声服务器
基于select的回声服务器

相关标签: # 网络编程