Liso(1) echo server

文章目录
  1. 1. Client pool
    1. 1.0.1. 初始化客户池
    2. 1.0.2. 增加 client
    3. 1.0.3. 删除 client
    4. 1.0.4. handle clients
  • 2. IO
    1. 2.0.1. open_listenfd
    2. 2.0.2. send
  • 3. liso.c
  • 4. 参考资料
  • liujianhao 带佬的 git repo 真的干货满满,这波开始 Liso 的源码阅读。

    首先看 client_pool, 其中管理了连接到 server 的 client 信息。

    Client pool

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #ifndef __SERVER_H
    #define __SERVER_H

    #include "io.h"

    // 客户池
    typedef struct
    {
    // 全部
    fd_set all_set;
    // 准备要读的
    fd_set read_fds;
    // 最大值
    int maxfd;
    // 已经准备的个数
    int nready;
    // 在数组中可获得的最大下标
    int maxi;
    // 客户数组
    int client_fd[FD_SETSIZE];
    } client_pool;

    void init_pool(int listenfd, client_pool *p);
    void add_client_to_pool(int newfd, client_pool *p);
    void handle_clients(client_pool *p);
    void clear_client(int clientfd, int idx, client_pool *p);

    #endif

    初始化客户池

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 初始化客户池,刚开始只有 listenfd 才是需要监听的
    void init_pool(int listenfd, client_pool *p)
    {
    FD_ZERO(&p->all_set);
    FD_ZERO(&p->read_fds);
    FD_SET(listenfd, &p->all_set);
    p->maxfd = listenfd;
    p->nready = 0;
    p->maxi = -1;
    for (int i = 0; i < FD_SETSIZE; i++)
    {
    // -1表示这个位置还没被占用
    p->client_fd[i] = -1;
    }
    }

    增加 client

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 添加客户套接字文件描述符到客户池
    void add_client_to_pool(int newfd, client_pool *p)
    {
    int i;
    p->nready--;

    for (i = 0; i < FD_SETSIZE; i++)
    {
    // 先在数组中找到位置存储新到来的fd
    if (p->client_fd[i] < 0)
    {
    p->client_fd[i] = newfd;
    FD_SET(newfd, &p->all_set);
    p->maxfd = (newfd > p->maxfd) ? newfd : p->maxfd;
    p->maxi = (i > p->maxi) ? i : p->maxi;
    break;
    }
    }

    // 超过限制
    if (i == FD_SETSIZE)
    {
    printlog("[Client pool] Too many clients!");
    }
    }

    删除 client

    1
    2
    3
    4
    5
    6
    7
    // 从客户池中删除指定fd
    void clear_client(int client_fd, int idx, client_pool *p)
    {
    close(client_fd);
    FD_CLR(client_fd, &p->all_set);
    p->client_fd[idx] = -1;
    }

    handle clients

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    void handle_clients(client_pool *p)
    {
    int i, nbytes, clientfd;
    char buf[BUF_SIZE];

    for (i = 0; (i <= p->maxi) && (p->nready > 0); i++)
    {
    clientfd = p->client_fd[i];
    if (clientfd <= 0)
    continue;

    /* If client is ready, read request from it and echo it back */
    if (FD_ISSET(clientfd, &p->read_fds))
    {
    // 设置为非阻塞
    set_fl(clientfd, O_NONBLOCK);
    // 接受数据
    nbytes = recv(clientfd, buf, BUF_SIZE, 0);
    if (nbytes > 0)
    {
    printlog("[Client pool] Receive %d bytes from client on socket %d", nbytes, clientfd);
    Sendn(clientfd, buf, nbytes);
    clr_fl(clientfd, O_NONBLOCK); // clear nonblock
    }
    else if (nbytes <= 0)
    {
    // 客户端关闭了连接
    if (nbytes == 0)
    {
    printlog("[Client pool] Connection closed by client on socket %d", clientfd);
    }
    // 有错误
    else
    {
    printlog("[Client pool] Exception on recv() from client on socket %d", clientfd);
    }
    clear_client(clientfd, i, p);
    }
    p->nready--;
    if (p->nready <= 0)
    return;
    }
    }
    }

    IO

    先了解下几种不同的 sockaddr 结构体。可以参考博客网络编程通用结构体

    然后看 io 的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #ifndef __IO_H
    #define __IO_H

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>

    #include "utilities.h"

    #define SOCKET_API_ERR_MSG "[Error in socket_api]"

    typedef struct sockaddr sockaddr;
    typedef struct sockaddr_in sockaddr_in;
    typedef struct sockaddr_in6 sockaddr_in6;
    typedef struct sockaddr_storage sockaddr_storage;
    typedef struct addrinfo addrinfo;

    int open_listenfd(int port);
    int sendn(int, const void *, int);
    void Sendn(int, const void *, int);

    #endif

    open_listenfd

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // 监听指定端口
    int open_listenfd(int port)
    {
    int listenfd;
    int yes = 1;
    sockaddr_in serveraddr;

    // 创建套接字
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
    printlog("%s create listener socket", SOCKET_API_ERR_MSG);
    return -1;
    }

    // 消除 "Address already in use" 这种错误
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
    {
    printlog("%s set SO_REUSEADDR", SOCKET_API_ERR_MSG);
    return -1;
    }

    // 绑定
    memset(&serveraddr, 0, sizeof(sockaddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons((unsigned short)port);
    if (bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
    printlog("%s bind listenser socket", SOCKET_API_ERR_MSG);
    close(listenfd);
    return -1;
    }

    // 监听
    if (listen(listenfd, 10) < 0)
    {
    printlog("%s listen on listener socket", SOCKET_API_ERR_MSG);
    return -1;
    }

    return listenfd;
    }

    send

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // send的包裹函数
    int sendn(int fd, const void *p, int n)
    {
    size_t nleft = n;
    ssize_t nwritten = 0;
    const char *ptr = p;

    while (nleft > 0) // 全部发完
    {
    if ((nwritten = send(fd, ptr + nwritten, n, 0)) < 0)
    {
    if (nwritten < 0 && errno == EINTR)
    {
    nwritten = 0;
    }
    else
    {
    return -1;
    }
    }

    nleft -= nwritten;
    ptr += nwritten;
    }

    return n;
    }

    // sendn的包裹函数
    void Sendn(int fd, const void *ptr, int n)
    {
    if (sendn(fd, ptr, n) != n)
    err_sys("sendn error");
    }

    liso.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    #include "client_pool.h"
    #include "io.h"

    int main(int argc, char *argv[])
    {
    if(argc != 2)
    {
    printlog("Usage: %s ports", argv[0]);
    exit(1);
    }

    int port = atoi(argv[1]);
    client_pool pool;
    int listenfd, newfd;
    sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(sockaddr_in);

    printlog("[Main] ----- Liso Echo Server -----\n");

    if((listenfd = open_listenfd(port)) < 0)
    {
    printlog("open_listen error");
    exit(1);
    }
    printlog("[Main] Create listenfd sucessfully");

    init_pool(listenfd, &pool);

    while(1)
    {
    pool.read_fds = pool.all_set;
    pool.nready = select(pool.maxfd+1, &pool.read_fds, NULL, NULL, NULL);

    if(pool.nready == 0)
    {
    continue;
    }
    else if(pool.nready < 0)
    {
    printlog("[Main] Select error");
    }

    if(FD_ISSET(listenfd, &pool.read_fds))
    {
    newfd = accept(listenfd, (sockaddr *)&clientaddr, &addrlen);

    if(newfd < 0)
    {
    printlog("[Main] Accept error");
    continue;
    }

    add_client_to_pool(newfd, &pool);

    if(pool.nready <= 0)
    {
    continue;
    }
    }

    // 剩下的就是处理客户端的请求
    handle_clients(&pool);
    }
    }

    上面代码是标准的处理流程,值得借鉴。

    参考资料