Linux网络编程之select

简介:

使用select多路复用技术的非阻塞模型

select多路复用通常具有很好的跨平台性,也能提供不错的并发性能,但是在通常情况下有最大监听文件描述符的限制(通常1024),如果不需要达到C10K这种前端高性能服务器的要求,采用select+nonblocking的方式能降低编程的难度


用到的接口
FD_SETSIZE;
FD_SET(<#fd#>, <#fdsetp#>);
FD_ISSET(<#fd#>, <#fdsetp#>);
FD_ZERO(<#fdsetp#>);
select(<#(int)__nfds#>, <#(fd_set*)__readfds#>, <#(fd_set*)__writefds#>, <#(fd_set*)__exceptfds#>, <#(struct timeval*)__timeout#>)


//select-nonblocking
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/select.h>

#define MAX_READ_SIZE  20*1024*1024
#define MAX_WRITE_SIZE 20*1024*1024
#define MAX_CHUNCK_SIZE 2*1024*1024
#define KEEP_ALIVE 0

/*
 *
 * a simple server using select 
 * feilengcui008@gmail.com
 *
 */

void run();

int main(int argc, char *argv[])
{
    run();
    return 0;
}

//struct for storing read-write buffer
struct fd_state {
    char buffer[MAX_READ_SIZE];
    char write_buffer[MAX_WRITE_SIZE];
    int writing;
    size_t read_len;
    size_t written_len;
    size_t write_upto_len;
};

//alloc fd_state
struct fd_state *alloc_fd_state(void)
{
    struct fd_state *p = (struct fd_state *) malloc(sizeof(struct fd_state));
    if (!p) return NULL;
    p->read_len = p->writing = p->written_len = p->write_upto_len = 0;
    return p;
};

//free fd_state pointer
void free_fd_state(struct fd_state *p)
{
    free(p);
}

//handle error 
void error_exit()
{
    perror("errors happen");
    exit(EXIT_FAILURE);
}
//set sock non-blocking
void made_nonblock(int fd)
{
    if(fcntl(fd, F_SETFL, O_NONBLOCK)==-1){
        error_exit();
    }
}

//handle read event
int do_read(int fd,struct fd_state *state)
{
    char buf[MAX_CHUNCK_SIZE];
    int i;
    ssize_t result;
    while (1){
        result = recv(fd, buf, sizeof(buf), 0);
        /*printf("readlen:%d\n",(int)result);
        fflush(stdout);*/
        if (result<=0)
            break;
        //read buf to fd_state.buffer until "\n"
        for (int j = 0; j < result; ++j) {
            if (state->read_len< sizeof(state->buffer))
                state->buffer[state->read_len++] = buf[j];
            /*read until "\n"
            todo:
            handle the read buffer and set the write buffer
            if (buf[j]=='\n'){
                state->writing = 1;
                state->write_upto_len = state->read_len;
                break;
            }*/
        }

    }

    state->writing = 1;
    state->write_upto_len = state->read_len;

    //write(1, state->buffer, state->read_len);
    //nothing read
    if (result==0){
        return 1;
    }

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

int do_write(int fd,struct fd_state *state)
{
    //we borrow the readbuffer just for test
    //we should have a write buffer to store our http response body
    ssize_t result;
    while (state->written_len<state->write_upto_len){
        result = send(fd, state->buffer+state->written_len, state->write_upto_len-state->written_len, 0);
        if (result<=0){
            break;
        }
        state->written_len+=result;
    }

    if (state->written_len==state->read_len)
        state->written_len = state->write_upto_len = state->read_len = 0;
    state->writing = 0;

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

    return 0;
}

void do_select(int serverfd)
{

    //set init fd_state
    struct fd_state *state[FD_SETSIZE];
    for (int i = 0; i < FD_SETSIZE; ++i) {
        state[i] = NULL;
    }
    fd_set read_set,write_set,error_set;
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    FD_ZERO(&error_set);
    //main loop
    while(1){
        int maxfd = serverfd;
        FD_ZERO(&read_set);
        FD_ZERO(&write_set);
        FD_ZERO(&error_set);

        FD_SET(serverfd, &read_set);

        //server accept operation just alloc the fd_state struct
        //do_read and do_write just change the fd_state's state
        //here we add server sock and client socks to fd_set
    //we don not pass the readset/writeset to do_read or do_write func
    //so we add read write event here
    //when use poll,we can direct change the state in do_read and do_write use the events status
        for (int i = 0; i < FD_SETSIZE; ++i) {
            if (state[i]){
                if (i>maxfd){
                    maxfd = i;
                }
                FD_SET(i, &read_set);
                if (state[i]->writing){
                    FD_SET(i, &write_set);
                }
            }
        }
        //block until event happens
        if(select(maxfd+1, &read_set, &write_set, &error_set, NULL)<0){
            error_exit();
        }

        // if server becomes readable then accept the client sock
        // and alloc the client sock state
        if(FD_ISSET(serverfd, &read_set)){
            struct sockaddr_storage ss;
            socklen_t slen = sizeof(ss);
            int client = accept(serverfd, (struct sockaddr *)&ss, &slen);
            if(client<0){
            error_exit();
            }else if(client> FD_SETSIZE){
                close(client);
            }else{
                made_nonblock(client);
                state[client] = alloc_fd_state();
            }
        }

        //handle the fd_set
        for (int j = 0; j < maxfd + 1; ++j) {
            int flag = 0;
            if (j==serverfd) continue;
            //handle read
            if (FD_ISSET(j, &read_set)) flag = do_read(j,state[j]);
            //handle write
            if (flag==0&& FD_ISSET(j, &write_set)){
                flag = do_write(j, state[j]);
                //no matter what flag is,we close after write ops
                if(!KEEP_ALIVE){
                    free_fd_state(state[j]);
                    state[j] = NULL;
                    close(j);
                }
            }
            //handle error
            if (flag){
                free_fd_state(state[j]);
                state[j] = NULL;
                close(j);
            }
        }

    }
}

int create_server_socket()
{

    int serverfd = socket(AF_INET, SOCK_STREAM, 0);
    if(serverfd<0){
        error_exit();
    }
    int reuse = 1;
    if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))<0){
        error_exit();
    }

    return serverfd;
}

void run()
{
    int serverfd = create_server_socket();
    made_nonblock(serverfd);
    //server sockaddr
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(8000);
    if(bind(serverfd, (struct sockaddr *)&sin, sizeof(sin))<0){
        error_exit();
    }
    if(listen(serverfd, 50)<0){
        error_exit();
    }
    do_select(serverfd);
}   
相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
99 0
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
81 2
|
3月前
|
安全 Linux 网络安全
Web安全-Linux网络协议
Web安全-Linux网络协议
80 4
|
3天前
|
Ubuntu Unix Linux
Linux网络文件系统NFS:配置与管理指南
NFS 是 Linux 系统中常用的网络文件系统协议,通过配置和管理 NFS,可以实现跨网络的文件共享。本文详细介绍了 NFS 的安装、配置、管理和常见问题的解决方法,希望对您的工作有所帮助。通过正确配置和优化 NFS,可以显著提高文件共享的效率和安全性。
57 7
|
2月前
|
运维 监控 网络协议
|
2月前
|
Ubuntu Linux 虚拟化
Linux虚拟机网络配置
【10月更文挑战第25天】在 Linux 虚拟机中,网络配置是实现虚拟机与外部网络通信的关键步骤。本文介绍了四种常见的网络配置方式:桥接模式、NAT 模式、仅主机模式和自定义网络模式,每种模式都详细说明了其原理和配置步骤。通过这些配置,用户可以根据实际需求选择合适的网络模式,确保虚拟机能够顺利地进行网络通信。
120 1
|
2月前
|
网络协议 安全 Ubuntu
Linux中网络连接问题
【10月更文挑战第3天】
40 1
|
2月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控总结
Linux系统命令与网络,磁盘和日志监控总结
70 0
|
2月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控三
Linux系统命令与网络,磁盘和日志监控三
49 0
|
3月前
|
机器学习/深度学习 安全 网络协议
Web安全-Linux网络命令
Web安全-Linux网络命令
37 1