0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装)

0.春阳语录,代码嵌套三层以上就是一坨垃圾。因此良好的编程风格从封装开始。

1.封装select服务器模型

  1.1 如何封装?将select需要的数据结构都封装成结构体,通过参数在函数之间传递,将固定的操作封装成相应的函数。

  1.2 封装后的程序:

    1.2.1 封装的头文件 select_t.h   

#ifndef __SELECT_T_H__
#define __SELECT_T_H__
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

typedef struct{
    fd_set allset_;
    fd_set rset_;
    int clients_[FD_SETSIZE];
    int maxi_;
    int maxfd_;
    int nready_;
    int listenfd_;
    void (*handle_callback_)(int, char *buf); //有用户提供回调函数
}select_t;

void select_init(select_t *sel, int listenfd);
void select_set_callback(select_t *sel, void (*handler)(int, char *buf));
void select_do_wait(select_t *sel);
void select_handle_accept(select_t *sel);
void select_handle_data(select_t *sel);

#endif

    1.2.2 封装的select类函数 select_t.c

#include "select_t.h"
#include "network.h"
#include <assert.h>
//仅供本文件内部调用
void select_add_fd(select_t *sel, int fd); //将新连接的fd加入到监听集合 和 clients数组中
void select_del_fd(select_t *sel, int i); //将关闭的fd从监听集合和clients数组中基础,并关闭连接

void select_init(select_t *sel, int listenfd){
    sel->listenfd_ = listenfd;
    FD_ZERO(&sel->allset_);
    FD_ZERO(&sel->rset_);
    FD_SET(listenfd, &sel->allset_);
    int i;
    for(i = 0; i < FD_SETSIZE; i++){
        sel->clients_[i] = -1;
    }
    sel->maxi_ = -1;
    sel->maxfd_ = listenfd;
}

void select_set_callback(select_t *sel, void (*handler)(int, char*)){
    sel->handle_callback_ = handler;
}

void  select_do_wait(select_t *sel){
    sel->rset_ = sel->allset_;
    do{
        sel->nready_ = select(sel->maxfd_ + 1, &sel->rset_, NULL, NULL, NULL);
    }while(sel->nready_ == -1 && errno == EINTR);
}

void select_handle_accept(select_t *sel){
    if(FD_ISSET(sel->listenfd_, &sel->rset_)){
        int peerfd = accept(sel->listenfd_, NULL, NULL);
        if(peerfd == -1)
                ERR_EXIT("accept");
        select_add_fd(sel, peerfd);
    }
    sel->nready_--;
}

void select_handle_data(select_t *sel){
    if(sel->nready_ == 0)
        return;
    int i;
    char recvbuf[1024] = {0};
    for(i = 0; i < FD_SETSIZE; i++){
        if(sel->clients_[i] == -1)
           continue;
        int fd = sel->clients_[i];
        if(FD_ISSET(sel->clients_[i], &sel->rset_)){ //?这里用fd会出错
            int ret = readline(fd, recvbuf, 1024);
            if(ret == -1)
                ERR_EXIT("readline");
            else if(ret == 0){
                printf("client closed\n");
                select_del_fd(sel, i);
                continue;
            }
            sel->handle_callback_(fd, recvbuf);
        }
    }
}

void select_add_fd(select_t *sel, int fd){
    int i;
    for(i = 0; i < FD_SETSIZE; i++){
        if(sel->clients_[i] == -1){
            sel->clients_[i] = fd;
            if(i > sel->maxi_)
                sel->maxi_ = i;
            break;
        }
    }
    if(i == FD_SETSIZE){
        fprintf(stderr, "too many clients\n");
        close(fd);
        exit(EXIT_FAILURE);//!
    }
    FD_SET(fd, &sel->allset_);
    if(fd > sel->maxfd_)
        sel->maxfd_ = fd;
}

void select_del_fd(select_t *sel, int i){
    assert(i >= 0 && i < FD_SETSIZE); //!
    int fd = sel->clients_[i];
    sel->clients_[i] = -1;
    FD_CLR(fd, &sel->allset_);
    close(fd);
}

    1.2.3 封装后的mian函数 server.c

#include "network.h"
#include "select_t.h"
/*
 * 服务器端使用 select 模型
 *
 * 这里为服务器提供处理客户请求的方法
 * 服务器回调本函数
 */
void handler(int fd, char *buf){
    printf("recv data:%s", buf);
    writen(fd, buf, strlen(buf));
}

int main(int argc, const char *argv[])
{
    if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
       ERR_EXIT("signal");
    int listenfd = listenfd_init();
    select_t sel;
    select_init(&sel, listenfd);
    select_set_callback(&sel, handler);
    while(1){
        select_do_wait(&sel);
        select_handle_accept(&sel);
        select_handle_data(&sel);
    }
    close(listenfd);
    return 0;
}

2.封装poll服务器模型

  2.1 封装后的头文件 poll_t.h

#ifndef __POLL_T_H__
#define __POLL_T_H__
#include <poll.h>
#include <assert.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

typedef struct{
    struct pollfd clients_[2048];
    int maxi_;
    int nready_;
    int listenfd_;
    void (*handle_callback_)(int, char*);
}poll_t;

void poll_init(poll_t *pol, int listenfd, void (*handler)(int, char*));
void poll_do_wait(poll_t *pol);
void poll_handle_accept(poll_t *pol);
void poll_handle_data(poll_t *pol);

#endif

  2.2 封装的函数 poll_t.c

#include "poll_t.h"
#include "network.h"

void poll_add_fd(poll_t *pol, int fd);
void poll_del_fd(poll_t *pol, int i);

void poll_init(poll_t *pol, int listenfd, void(*handler)(int, char*)){
    pol->listenfd_ = listenfd;
    int i;
    for(i = 0; i < 2048; i++){
       pol->clients_[i].fd = -1;
    }
    pol->clients_[0].fd = listenfd;
    pol->clients_[0].events = POLLIN;
    pol->maxi_ = 0;
    pol->nready_ = 0;
    pol->handle_callback_ = handler;
}

void poll_do_wait(poll_t *pol){
    int nready;
    do{
        nready = poll(pol->clients_, pol->maxi_ + 1, -1);
    }while(nready == -1 && errno == EINTR);
    if(nready == -1)
        ERR_EXIT("poll");
    pol->nready_ = nready;
}

void poll_handle_accept(poll_t *pol){
    if(pol->clients_[0].revents & POLLIN){
        int peerfd = accept(pol->listenfd_, NULL, NULL);
        if(peerfd == -1)
            ERR_EXIT("accept");
        poll_add_fd(pol, peerfd);
        --pol->nready_;
    }
}

void poll_handle_data(poll_t *pol){
    if(pol->nready_ == 0)
        return;
    int i;
    char recvbuf[1024] = {0};
    for(i = 1; i <= pol->maxi_; i++){
        int peerfd = pol->clients_[i].fd;
        if(peerfd == -1)
            continue;
        if(pol->clients_[i].revents & POLLIN){
            int ret = readline(peerfd, recvbuf, 1024);
            if(ret == -1)
                ERR_EXIT("readline");
            else if(ret == 0){
                printf("client closed\n");
                poll_del_fd(pol, i);
                continue;
            }
            pol->handle_callback_(peerfd, recvbuf);
        }
    }
}

void poll_add_fd(poll_t *pol, int fd){
    int i;
    for(i = 0; i < 2048; i++){
        if(pol->clients_[i].fd == -1){
            pol->clients_[i].fd = fd;
            pol->clients_[i].events= POLLIN;
            if(i > pol->maxi_)
                pol->maxi_ = i;
            break;
        }
    }
    if(i == 2048){
        fprintf(stderr, "too many clients\n");
        close(fd);
        exit(EXIT_FAILURE);
    }
}

void poll_del_fd(poll_t *pol, int i){
    assert(i >= 1 && i < 2048);//
    close(pol->clients_[i].fd);
    pol->clients_[i].fd = -1;
}

  2.3 封装后的main 函数 server.c

#include "network.h"
#include "poll_t.h"

void handler(int fd, char *buf){
    printf("recv data:%s", buf);
    writen(fd, buf, strlen(buf));
}

int main(int argc, const char *argv[])
{
    if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
       ERR_EXIT("signal");
    int listenfd = listenfd_init();
    poll_t pol;
    poll_init(&pol, listenfd, handler);
    while(1){
        poll_do_wait(&pol);
        poll_handle_accept(&pol);
        poll_handle_data(&pol);
    }
    close(listenfd);
    return 0;
}

3.封装epoll服务器模型

  3.1 封装后的头文件 epoll_t.c

#ifndef __EPOLL_T_H__
#define __EPOLL_T_H__
#include <sys/epoll.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

typedef struct{
    int epollfd_;
    struct epoll_event events_[2048];
    int listenfd_;
    int nready_;
    void (*handle_callback_)(int, char*);
}epoll_t;

void epoll_init(epoll_t *epo, int listenfd, void (*handler)(int, char*));
void epoll_do_wait(epoll_t *epo);
void epoll_handle(epoll_t *epo);
void epoll_close(epoll_t *epo);

#endif  

  3.2 封装后的epoll类函数 epoll_t.c

#include "epoll_t.h"
#include "network.h"

void epoll_handle_accept(epoll_t *epo);
void epoll_handle_data(epoll_t *epo, int peerfd);
void epoll_add_fd(epoll_t *epo, int fd);
void epoll_del_fd(epoll_t *epo, int fd);

void epoll_init(epoll_t *epo, int listenfd, void(*handler)(int, char*)){
    if((epo->epollfd_ = epoll_create(2048)) == -1)
        ERR_EXIT("epoll_create");
    epo->listenfd_ = listenfd;
    epoll_add_fd(epo, listenfd);
    epo->nready_ = 0;
    epo->handle_callback_ = handler;
}
void epoll_do_wait(epoll_t *epo){
    int nready;
    do{
        nready = epoll_wait(epo->epollfd_, epo->events_, 2048, -1);
    }while(nready == -1 && errno ==  EINTR);
    if(nready == -1)
        ERR_EXIT("epoll_wait");
    epo->nready_ = nready;
}

void epoll_handle(epoll_t *epo){
    int i;
    for(i = 0 ; i < epo->nready_; i++){
        int fd = epo->events_[i].data.fd;
        if(fd == epo->listenfd_){
            epoll_handle_accept(epo);
        }
        else
            epoll_handle_data(epo, fd);
    }
}

void epoll_handle_accept(epoll_t *epo){
    int peerfd = accept(epo->listenfd_, NULL, NULL);
    if(peerfd == -1)
        ERR_EXIT("accept");
    epoll_add_fd(epo, peerfd);
}

void epoll_handle_data(epoll_t *epo, int peerfd){
    char recvbuf[1024] = {0};
    int ret = readline(peerfd, recvbuf, 1024);
    if(ret == -1)
        ERR_EXIT("readline");
    else if(ret == 0){
        printf("client closed\n");
        epoll_del_fd(epo, peerfd);
        return;
    }
    epo->handle_callback_(peerfd, recvbuf);
}

void epoll_close(epoll_t *epo){
    close(epo->epollfd_);
    close(epo->listenfd_);
}

void epoll_add_fd(epoll_t *epo, int fd){
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    if(epoll_ctl(epo->epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1)
        ERR_EXIT("epoll_ctl");
}

void epoll_del_fd(epoll_t *epo, int fd){
    struct epoll_event ev;
    ev.data.fd = fd;
    if(epoll_ctl(epo->epollfd_, EPOLL_CTL_DEL, fd, &ev) == -1)
        ERR_EXIT("epoll_ctl");
}

  3.3 封装后的main函数 server.c

#include "network.h"
#include "epoll_t.h"

void handler(int fd, char *buf){
    printf("recv data:%s", buf);
    writen(fd, buf, strlen(buf));
}

int main(int argc, const char *argv[])
{
    if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
       ERR_EXIT("signal");
    int listenfd = listenfd_init();
    epoll_t epo;
    epoll_init(&epo, listenfd, handler);
    while(1){
        epoll_do_wait(&epo);
        epoll_handle(&epo);
    }
    epoll_close(&epo);
    return 0;
}

4.总结

  封装对一个程序的编写,调试来说都是至关重要的,封装后的代码可读性明显大大提高,因此,以后在写程序时,要注意这一点。此外,从封装这三种模型来看,epoll显然更简单方便,因此,以后在写服务器的时候要改select为epoll。

0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装),布布扣,bubuko.com

时间: 2024-10-17 16:11:42

0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装)的相关文章

Linux网络编程:原始套接字的魔力【上】

基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Data字段,我们没法直接对TCP或UDP头部字段进行赤裸裸的修改,当然还有IP头.换句话说,我们对它们头部操作的空间非常受限,只能使用它们已经开放给我们的诸如源.目的IP,源.目的端口等等. 今天我们讨论一下原始套接字的程序开发,用它作为入门协议栈的进阶跳板太合适不过了.OK闲话不多说,进入正题. 原始

很全的linux网络编程技巧

注:作者王晓,本人认为总结得很好,故记之,绝无侵权之意. 1. LINUX网络编程基础知识 1 1.1. TCP/IP协议概述 1 1.2. OSI参考模型及TCP/IP参考模型 1 1.3. TCP协议 3 1.4. UDP协议 5 1.5. 协议的选择 6 2. 网络相关概念 6 2.1. socket概念 7 2.2. socket类型 8 2.3. socket信息数据结构 8 2.4. 数据存储优先顺序的转换 8 2.5. 地址格式转化 9 2.6. 名字地址转化 10 3. sock

Linux网络编程10&mdash;&mdash;使用UDP实现五子棋对战

思路 1. 通信 为了同步双方的棋盘,每当一方在棋盘上落子之后,都需要发送给对方一个msg消息,让对方知道落子位置.msg结构体如下: /* 用于发给对方的信息 */ typedef struct tag_msg { int msg_type; /* 悔棋? */ int msg_color; int msg_row; int msg_col; }MSG, *pMSG; 2. 悔棋 用链表头插法来模拟栈,链表记录了双方下子的轨迹.结构如下: /* 记录每一步的轨迹 */ typedef stru

Linux网络编程入门 (转载)

http://www.cnblogs.com/RascallySnake/archive/2012/01/04/2312564.html (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件

Linux网络编程入门

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端 在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一 个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端 和客户端相对应的程序即为服务端程序.被动的等待外面的程序来和自己通

Linux网络编程------概述

提供了完善强大的网络功能. 1.完善的内置网络.协议丰富,和内核紧密. 2.提供大量Intenet的软件. 3.非常多的命令.完成文件传输. 4.远程访问支持. 5.安全可靠. Linux网络模型: 协议: 数据链路层协议:以太网协议 网络层协议:IP(Internet协议) ICMP(网际控制报文协议)  ARP(地址解析协议) 传输层协议:TCP(面向连接可靠传输)   UDP(非连接不可靠传输协议) 应用层协议:Telnet  文件传输协议(FTP TFTP),简单文件传输协议(SMTP)

linux 网络编程需要学习的内容

Linux C++培训发 课程模块 Linux C++全科班课程由以下模块组成: Module01 - Linux系统基础 由于本系列课程基于Linux(或UNIX),熟悉Linux操作系统是必要的前提. 该模块的课程包含以下方面的内容: 常用Unix/Linux命令熟悉文件管理.文本处理.进程管理.网络.系统管理等各个方面大约100个常用的命令. 深入了解bash了解Linux默认shell: bash 的语法.命令执行.I/O重定向.任务控制等. 正则表达式基础由于UNIX/Linux中很多

[转] - Linux网络编程 -- 网络知识介绍

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端        和客户端相

Linux网络编程视频 百度网盘

Linux网络编程(总共41集)讲解Linux网络编程知识,分以下四个篇章.Linux网络编程之TCP/IP基础篇Linux网络编程之socket编程篇Linux网络编程之进程间通信篇Linux网络编程之线程篇Linux网络编程之TCP/IP基础篇01TCPIP基础(一)ISO/OSI参考模型TCP/IP四层模型基本概念(对等通信.封装.分用.端口)02TCPIP基础(二)最大传输单元(MTU)/路径MTU以太网帧格式ICMPARPRARP03TCPIP基础(三)IP数据报格式网际校验和路由04