分别使用select、poll、epoll实现echo服务器

1. 封装一些必要的C/S架构头文件(放在common文件夹中)

common

├── head.h

├── common.c
├── common.h
├── tcp_client.c//可以省略
├── tcp_client.h//可以省略,后面用telnet来测试就行
├── tcp_server.c
├── tcp_server.h

  1. head.h

    #ifndef HEAD_H
    #define HEAD_H
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <fcntl.h>
    #include <stdbool.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <poll.h>
    #include <sys/select.h>
    #include <errno.h>
    #endif
    
  2. common.h

    #ifndef _COMMON_H
    #define _COMMON_H
    char conf_ans[50] = {0};
    char *get_value(char *path, char *key);
    void make_nonblock_ioctl(int fd);
    void make_block_ioctl(int fd);
    void make_nonblock(int fd);
    void make_block(int fd);
    #endif
    
  3. common.c

    #include "head.h"
    
    char *get_value(char *path, char *key) {
        FILE *fp = NULL;
        ssize_t nrd;
        char *line = NULL, *sub = NULL;
        extern char conf_ans[50];
        size_t linecap;
        if (path == NULL || key == NULL) {
            fprintf(stderr, "Error in argument!\n");
            return NULL;
        }
        if ((fp = fopen(path, "r")) == NULL) {
            perror("fopen");
            return NULL;
        }
        while ((nrd = getline(&line, &linecap, fp)) != -1) {
            if ((sub = strstr(line, key)) == NULL) 
                continue;
            else {
                if (line[strlen(key)] == '=') {
                    strncpy(conf_ans, sub + strlen(key) + 1, nrd - strlen(key) - 2);
                    *(conf_ans + nrd - strlen(key) - 2) = '\0';
                    break;
                }
            }
        }
        free(line);
        fclose(fp);
        if (sub == NULL) {
            return NULL;
        }
        return conf_ans;
    }
    
    void make_nonblock_ioctl(int fd){
        unsigned long ul = 1;
        ioctl(fd, FIONBIO, &ul);
    }
    
    
    void make_block_ioctl(int fd) {
        unsigned long ul = 0;
        ioctl(fd, FIONBIO, &ul);
    }
    
    
    void make_nonblock(int fd) {
        int flag;
        if ((flag = fcntl(fd, F_GETFL)) < 0) {
            return;
        }
        flag |= O_NONBLOCK;
        fcntl(fd, F_SETFL, flag);
    }
    
    void make_block(int fd) {
        int flag;
        if ((flag = fcntl(fd, F_GETFL)) < 0) {
            return ;
        }
        flag &= ~O_NONBLOCK;
        fcntl(fd, F_SETFL, flag);
    }
    
  4. tcp_server.h

    #ifndef _TCP_SERVER_H
    #define _TCP_SERVER_H
    int socket_create(int port);
    #endif
    
  5. tcp_server.c

    #include "head.h"
    int socket_create(int port) {
        int server_listen;
        if ((server_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            return -1;
        }
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = INADDR_ANY;
    
        struct linger m_linger;
        m_linger.l_onoff = 1;
        m_linger.l_linger = 5;
        if (setsockopt(server_listen, SOL_SOCKET, SO_LINGER, &m_linger, (socklen_t)sizeof(m_linger)) < 0) {//延迟关闭的实现
            return -1;
        }
        int flag = 1;
        if (setsockopt(server_listen, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {//地址重用
            return -1;
        }
    
    
        if (bind(server_listen, (struct sockaddr *)&server, sizeof(server)) < 0) {
            return -1;
        }
    
        if (listen(server_listen, 20) < 0){
            return -1;
        }
       /* int maxfd = server_listen;
        struct timeval tv;
        int retval;
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(maxfd, &rfds);    
        int sub, fd;
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
        if(retval == -1) {
            perror("select()");
        } else if(retval) {
            printf("Data is available now.\n");
        } else {
            printf("No data within five seconds.\n");
        }
    
        if ((fd = accept(maxfd, NULL, NULL)) < 0) {
            perror("accept");
        }
        if(fd > maxfd) {
           maxfd = fd;
        }*/
        return server_listen;
    }
    
  6. tcp_client.h

    #ifndef _TCP_CLIENT_H
    #define _TCP_CLIENT_H
    int socket_connect(char *host, int port);
    int socket_connect_timeout(char *host, int port, long timeout);
    #endif
    
  7. tcp_client.c

    #include  "head.h"
    #include "common.h"
    int socket_connect(char *host, int port) {
        int sockfd;
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(host);
    
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            return -1;
        }
        printf("Socket create.\n");
        if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) {
            perror("connect");
            return -1;
        }
        return sockfd;
    }
    
    int socket_connect_timeout(char *host, int port, long timeout) {//此处就是用select实现的超时
        int sockfd;
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(host);
    
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            return -1;
        }
        make_nonblock(sockfd);
        struct timeval tv;
        fd_set wfds;
        FD_ZERO(&wfds);
        FD_SET(sockfd, &wfds);
        tv.tv_sec = 0;
        tv.tv_usec = timeout;
        if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) {
            int retval, error = -1;
            int len = sizeof(int);
            retval = select(sockfd + 1, NULL, &wfds, NULL, &tv);
    
            if(retval < 0) {
                close(sockfd);
                return -1;
            } else if(retval) {
                //ok;
                if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error,(socklen_t *)&len ) < 0) {
                    close(sockfd);
                    return -1;
                }
                if(error) {
                    close(sockfd);
                    return -1;
                }
            } else {
                //timeout;
                printf("Connect Time Out!\n");
                close(sockfd);
                return -1;
            }
        }
        make_block(sockfd);
        return sockfd;
    }
    

2. server_select

select.c

#include"../common/head.h"
#include"../common/color.h"

#include"../common/common.h"
#include"../common/tcp_server.h"

#define MAX_N 512
#define BUFFSIZE 1024


char ch_char(char c) {
    if(c >= 'a' && c <= 'z') {
        return c - 32;
    }
    return c;
}


int main(int argc, char **argv) {
    if(argc != 2) {
        fprintf(stderr, "Usage: %s port!\n", argv[0]);
        exit(1);
    }
    int server_listen, fd, client[MAX_N] = {0};

    if((server_listen = socket_create(atoi(argv[1]))) < 0) {
        perror("socket_create");
        exit(1);
    }
    make_nonblock(server_listen);//变为非阻塞
    int maxfd = server_listen;//开始server_listen最大
    fd_set rfds, wfds, efds;
    for(int i = 0; i < MAX_N; i++) {
        client[i] = -1;
    }
    client[server_listen] = server_listen;
    FD_SET(server_listen, &rfds);//先把开始的第一个加入到rfds集合中

    while(1) {
        FD_ZERO(&rfds);
        FD_ZERO(&wfds);
        FD_ZERO(&efds);
//        FD_SET(server_listen, &rfds);
        for(int i = 0; i < MAX_N; i++) {//因为FD_SET不能重用,所以要每次都得重置一波
           // if(client[i] == server_listen)continue;
            if(client[i] > 0) {
                if(maxfd < client[i])maxfd = client[i];//寻找最大的fd
                FD_SET(client[i], &rfds);
            }
        }
        if(select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0) {//开始阻塞
            perror("select");
            return 1;
        }
        //此时是由于select无限期的阻塞
        if(FD_ISSET(server_listen, &rfds)) {//如果server_listen是符合rdfs的,sever_listen特殊
            printf("Connect ready on serverlisten!\n");
            if((fd = accept(server_listen, NULL, NULL))< 0) {
                perror("accept");
                return 1;
            }
            if(fd > MAX_N) {//如果新的fd > MAX_N, 认为客户端太多了;
                printf("Too many clients!\n");
                close(fd);
            } else {//认为他是有用的
                make_nonblock(fd);//设置为非阻塞的
                if(client[fd] == -1) {
                    client[fd] = fd;//因为下表对应的fd
                }
            }
        }
        for(int i = 0; i < MAX_N; i++) {
            if(i == server_listen)continue;//特殊的server_listen
            if(FD_ISSET(i, &rfds)) {//i就是fd
                char buff[BUFFSIZE] = {0};
                memset(buff,0, sizeof(buff));
                int retval = recv(i, buff, BUFFSIZE, 0);//收
                if(retval <= 0) {//没有收到
                    printf("Logout!");
                    client[i] = -1;//client归位
                    close(i);
                    continue;
                }
                for(int j = 0; j < strlen(buff); j++) {
                    buff[j] = ch_char(buff[j]);
                }
                FILE *fp;
                fp = fopen("a.log", "a+");
                fwrite(buff, sizeof(buff), 1, fp);
                fclose(fp);
                printf("Recv: %s , %ld\n", buff, strlen(buff));
                char buff1[BUFFSIZE];
                if(strlen(buff) == 2) {
                    fp = fopen("a.log", "r");
                    while(fread(buff1, BUFFSIZE, 1, fp) > 0) {
                        send(i, buff1, strlen(buff1), 0);
                    }
                    fp = fopen("a.log", "w+");
                    fflush(fp);
                }
            }
        }
    }
    return 0;
}

3. server_poll

#include"../common/head.h" 
#include"../common/color.h"

#include"../common/common.h"
#include"../common/tcp_server.h"

#define POLLSIZE 100
#define BUFFSIZE 512
char ch_char(char c) {
    if(c >= 'a' && c <= 'z') {
        return c - 32;
    }
    return c;
}


int main(int argc, char **argv) {
    if(argc != 2) {
        fprintf(stderr, "Usage: %s port!\n", argv[0]);
        exit(1);
    }
    int server_listen, fd;
    if((server_listen = socket_create(atoi(argv[1]))) < 0) {
        perror("socket_create");
        exit(1);
    }

    struct pollfd event_set[POLLSIZE];//client[];

    for(int i = 0; i < POLLSIZE; i++) {
        event_set[i].fd = -1;
    }

    event_set[0].fd = server_listen;
    event_set[0].events = POLLIN;

    while(1) {
        int retval;
        /*阻塞监听是佛有客户端链接请求*/
        if((retval = poll(event_set, POLLSIZE, -1)) < 0) {
            perror("poll");
            return 1;
        }
        /*listenfd有读时间就绪*/
        if(event_set[0].revents & POLLIN) {
            /*接受客户端请求accept不会阻塞*/
            if((fd = accept(server_listen, NULL, NULL)) < 0) {
                perror("accept");
                continue;
            }
            retval --;
            int i = 1;
            for(i = 1; i < POLLSIZE; i++) {
                if(event_set[i].fd < 0) {/*找到空闲位置,存放accept返回的connfd*/
                    event_set[i].fd = fd;
                    event_set[i].events = POLLIN;
                    break;
                }
            }
            if(i == POLLSIZE) {/*达到了最大客户端数*/
                printf("Too many clients!\n");
            }
        }
        for(int i = 1; i < POLLSIZE; i++) {/*检测client[]看是哪个fd满足*/
            if(event_set[i].fd < 0)continue;
            if(event_set[i].revents & (POLLIN | POLLHUP | POLLERR)) {
                retval--;
                char buff[BUFFSIZE] = {0};
                if(recv(event_set[i].fd, buff, BUFFSIZE, 0) > 0) {
                    printf("Recv : %s \n", buff);
                    for(int i = 0; i < strlen(buff); i++) {
                        buff[i] = ch_char(buff[i]);
                    }
                    send(event_set[i].fd, buff,strlen(buff), 0);
                } else {
                    close(event_set[i].fd);
                    event_set[i].fd = -1;/*poll中不监控该文件描述符,直接置位-1,不像select中那样移除*/
                }
            }
            if(retval <= 0)break;
        }
    }

    return 0;
}

4. server_epoll

server.c

#include <sys/epoll.h>
#define MAX_EVENTS 10
#include "../common/head.h"
#include "../common/tcp_server.h"
#include "../common/common.h"
#define BUFFSIZE 512

int main(int argc, char **argv) {
    struct epoll_event ev, events[MAX_EVENTS];/*ev:epoll_ctl参数 events[]:epoll_wait参数*/
    int listen_sock, conn_sock, nfds, epollfd;
    char buff[BUFFSIZE] = {0};
    if (argc != 2) exit(1);
    listen_sock = socket_create(atoi(argv[1]));
/* Code to set up listening socket, 'listen_sock',
   (socket(), bind(), listen()) omitted */
    epollfd = epoll_create1(0);/*创建epoll模型,epollfd指向红黑树根节点*/
    if (epollfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLIN;/*指定lfd的监听事件为"读"*/
    ev.data.fd = listen_sock;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {/*将lfd及对应的结构体设置到树上,epollfd可找到该树*/
        perror("epoll_ctl: listen_sock");
        exit(EXIT_FAILURE);
    }

    for (;;) {
        /*epoll为server阻塞监听事件,events为struct epoll_event类型数组,MAX_EVENTS维数组容量,-1代表永久阻塞*/
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

        if (nfds == -1) {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        for (int n = 0; n < nfds; ++n) {
            if (events[n].data.fd == listen_sock) { //判断事件fd是不是lfd;
                conn_sock = accept(listen_sock, NULL, NULL); //接受链接
                if (conn_sock == -1) {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                make_nonblock(conn_sock);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = conn_sock;

                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
                    perror("epoll_ctl: conn_sock");
                    exit(EXIT_FAILURE);
                }
            } else {//不是lfd
                //do_use_fd(events[n].data.fd);
                if (events[n].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) {
                    memset(buff, 0, sizeof(buff));
                    if (recv(events[n].data.fd, buff, BUFFSIZE, 0) > 0) {
                        printf("recv: %s", buff);
                        for (int i = 0; i < strlen(buff); i++) {
                            if (buff[i] >= 'a' && buff[i] <= 'z') buff[i] -= 32;
                        }
                        send(events[n].data.fd, buff, strlen(buff), 0);
                    } else {
                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, events[n].data.fd, NULL) < 0) {//将该文件描述符从红黑树摘除
                            perror("epoll_ctrl");
                        }
                        close(events[n].data.fd);
                        printf("Logout!\n");
                    }
                }
            }
        }
    }
return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值