Linux中select函数的使用举例

linux中select函数的使用例子

/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
/* for select */
#include <sys/select.h>

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

char rot13_char(char c) {
    /* We don't want to use isalpha here; setting the locale would change
     * which characters are considered alphabetical. */
    if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

struct fd_state {
    char buffer[MAX_LINE];
    size_t buffer_used;

    int writing;
    size_t n_written;
    size_t write_upto;
};

struct fd_state * alloc_fd_state(void)
{
    struct fd_state *state = malloc(sizeof(struct fd_state));
    if (!state)
        return NULL;
    state->buffer_used = state->n_written = state->writing =
        state->write_upto = 0;
    return state;
}

void free_fd_state(struct fd_state *state) {
    free(state);
}

void make_nonblocking(int fd) {
    fcntl(fd, F_SETFL, O_NONBLOCK);
}

/*
 * result = 0时,表示连接断开,return 0;
 * resut = -1时,如果errno = EAGAIN,表示阻塞式socket无数据可读,return 0;
 * result = -1时,如果errno != EAGAIN, 表示读取失败,return -1.
 */
int do_read(int fd, struct fd_state *state) {
    char buf[1024];
    int i;
    ssize_t result;
    while (1) {
        /* 从终端设备读,通常以行为单位,读到换行符就返回了.
         * recv函数返回时,不会添加'\0'结束符,因此buffer_used用来表示实际读取到的字节数。
         */
        result = recv(fd, buf, sizeof(buf), 0);
        printf("do_read, fd = %d, buf = %s, strlen(buf) = %d, recv return %d, errno = %d\n", fd, buf, strlen(buf), result, errno);
        if (result <= 0)
            break;

        for (i = 0; i < result; ++i) {
            if (state->buffer_used < sizeof(state->buffer))
                state->buffer[state->buffer_used++] = buf[i];
            /* 如果recv中碰到‘\n’,置该socket为可写状态, 一般从终端读取时最后一个字符为‘\n’.*/
            if (buf[i] == '\n') {
                state->writing = 1;
                state->write_upto = state->buffer_used;
            }
        }
    }

    if (result == 0) {
        return 1;
    } else if (result < 0) {
        if (errno == EAGAIN)
            return 0;
        return -1;
    }

    return 0;
}

/*
 * 将state->write_upto个数据写入客户端,如果写入完毕,置为不可写状态.
 * 写入失败则return -1;成功或发送缓冲区满则return 0.
 * */
int do_write(int fd, struct fd_state *state) {
    printf("do_write, fd = %d, state->written = %d, upto = %d\n", fd, state->n_written, state->write_upto);
    while (state->n_written < state->write_upto) {
        ssize_t result = send(fd, state->buffer + state->n_written,
                state->write_upto - state->n_written, 0);
        printf("do_write(), result = %d, fd = %d\n", result, fd);
        if (result < 0) {
            if (errno == EAGAIN) {
                return 0;
            }
            return -1;
        }
        assert(result != 0);

        state->n_written += result;
    }

    /*读到的数据已经全部写入客户端,则重置标志位*/
    if (state->n_written == state->buffer_used) {
        printf("Write completed\n");
        state->n_written = state->write_upto = state->buffer_used = 0;
    }

    /*置为不可写状态*/
    state->writing = 0;

    return 0;
}

void run(int port)
{
    struct fd_state *state[FD_SETSIZE];
    struct sockaddr_in sin;
    int i, maxfd;
    fd_set readset, writeset, exset;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(port);

    printf("FD_SETSIZE = %d\n", FD_SETSIZE);
    for (i = 0; i < FD_SETSIZE; ++i)
        state[i] = NULL;

    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    printf("listen_fd = %d\n", listen_fd);
    make_nonblocking(listen_fd);

#ifndef WIN32
    {
        int one = 1;
        setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
    }
#endif

    if (bind(listen_fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return;
    }

    if (listen(listen_fd, 16)<0) {
        perror("listen");
        return;
    }

    FD_ZERO(&readset);
    FD_ZERO(&writeset);
    FD_ZERO(&exset);


    while (1) {
        maxfd = listen_fd;

        FD_ZERO(&readset);
        FD_ZERO(&writeset);
        FD_ZERO(&exset);

        FD_SET(listen_fd, &readset);

        /* maxfd表示描述符集中最大的一个 */
        for (i = 0; i < FD_SETSIZE; ++i) {
            if (state[i]) {
                if (i > maxfd) {
                    maxfd = i;
                }

                FD_SET(i, &readset);
                /*writing = 1表示该socket还有未发送的数据,添加对可写状态的监听.*/
                if (state[i]->writing) {
                    FD_SET(i, &writeset);
                }
            }
        }


        if (select(maxfd + 1, &readset, &writeset, &exset, NULL) < 0) {
            perror("select");
            return;
        }

        /*判断listen_fd是否有读事件发生,即是否有新的连接过来.*/
        if (FD_ISSET(listen_fd, &readset)) {
            struct sockaddr_storage ss;
            socklen_t slen = sizeof(ss);
            int fd = accept(listen_fd, (struct sockaddr*) &ss, &slen);
            if (fd < 0) {
                perror("accept");
            } else if (fd > FD_SETSIZE) {
                close(fd);
            } else {
                make_nonblocking(fd);
                state[fd] = alloc_fd_state();
                assert(state[fd]);/*XXX*/
            }
        }

        /*遍历0~maxfd描述符*/
        for (i = 0; i < maxfd + 1; ++i) {
            int r = 0;
            if (i == listen_fd) {
                continue;
            }

            if (FD_ISSET(i, &readset)) {
                /*这里如果输入的数据很大, 是有可能比较耗时间的*/
                r = do_read(i, state[i]);
            }

            if (r == 0 && FD_ISSET(i, &writeset)) {
                r = do_write(i, state[i]);
            }

            /*读取或写入失败,则关闭该socket.*/
            if (r) {
                free_fd_state(state[i]);
                state[i] = NULL;
                close(i);
            }
        }
    }
}

int main(int argc, char **argv) {
    if(argc < 2) {
        fprintf(stderr, "Usage:\n./server <port>\n");
        exit(1);
    }

    setvbuf(stdout, NULL, _IONBF, 0);

    run(atoi(argv[1]));
    return 0;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值