一个简单http_server的实现(1)

我的博客:startcraft.cn
最近在学习网络编程的知识,所以准备实现一个http服务器,写博客来记录一下学习的过程

RIO

RIO是csapp中提到的一个健壮的I/O包,为什么要使用这个来进行文件的读写,是因为接下来的socket编程中不适合使用c标准I/O

先贴出Rio包的实现

#ifndef __RIO_H__
#define __RIO_H__
#include <http_server.h> 
#define RIO_BUFSIZE 8192//缓冲区的大小
struct rio_t
{
    int rio_fd;//缓冲区对应的文件描述符
    int rio_cnt;//缓冲区中的元素个数
    char * rio_bufptr;//缓冲区头指针
    char rio_buf [RIO_BUFSIZE];//缓冲区
    rio_t (int fd):rio_fd (fd),rio_cnt (0),rio_bufptr(rio_buf){}
};
class Rio 
{
    public:
        //从文件中读取n个字节,存入usrbuf,读到EOF返回0,出错返回-1,正常返回读入的字节数
        ssize_t rio_readn (int fd,void *usrbuf,size_t n);
        //将usrbuf中的n个字节写入文件fd中
        ssize_t rio_writen (int fd,void *usrbuf,size_t n);
        //初始化缓冲区
        void rio_readinitb (rio_t * rp,int fd);
        //从缓冲区读取一行文本,最多读取maxlen的字节,超出部分截断用'\0'结尾
        ssize_t rio_readlineb (rio_t * rp,void * usrbuf,size_t maxlen);
        //从缓冲区读n个字节
        ssize_t rio_readnb (rio_t * rp,void *usrbuf,size_t n);

    private:
        //维护缓冲区,为系统read的带缓冲区版本
        ssize_t rio_read (rio_t *rp,char *usrbuf,size_t);

};
#endif //__RIO_H__

rio_readn函数和rio_writen函数和linux底层的read和write函数很像,不过处理了EINTR的错误,当read和write在阻塞时被某个信号中断,这时没有字节被读取/写入,系统会将错误代码置为EINTR
显然这个错误是可以修复的,当产生这个错误时我们只需要继续调用read和write即可

#include "rio.h" 
ssize_t Rio::rio_readn (int fd,void * usrbuf,size_t n)
{
    size_t isleft=n;
    size_t isread;
    char *buf=static_cast<char*>(usrbuf);
    while (isleft>0)
    {   
        if ((isread=read (fd,buf,isleft))<0)
        {
            if (errno==EINTR)
                isread=0;
            else
                return -1;//读取出错
        }else if (isread==0)
            break;
        isleft-=isread;
        buf+=isread;
    }   
    return (n-isleft);
}
ssize_t Rio::rio_writen (int fd,void *usrbuf ,size_t n)
{
    size_t isleft=n;
    size_t iswrite;
    char *buf=static_cast<char*>(usrbuf);
    while (isleft>0)
    {   
        if ((iswrite=write(fd,buf,isleft))<=0)
        {
            if (errno==EINTR)
                iswrite=0;
            else
                return -1;//写入出错                                                              
        }
        isleft-=iswrite;
        buf+=iswrite;
    }   
    return n;
}

缓冲区是为了优化I/O速度,因为底层I/O要切换到内核态进行,切换过程开销很大
rio_t就是缓冲区的结构,rio_readinitb是将缓冲区与打开的文件关联上
rio_readnb函数就是read的带缓冲区版本
rio_readlineb是一次读取一行,这两个函数的核心是rio_read函数
rio_read函数是来维护缓冲区的,当缓冲区为空时它会将缓冲区填满,如果剩余未读取数据不够将缓冲区填满返回填入的字节数,只要缓冲区不为空就将指定的n与缓冲区剩余字节中小的那个值的字节数拷贝给用户缓冲区

...
ssize_t Rio::rio_read (rio_t *rp,char *usrbuf,size_t n)
{   
    int cnt;
    while (rp->rio_cnt<=0)
    {
        //填满缓冲区
        rp->rio_cnt=read (rp->rio_fd,rp->rio_buf,sizeof (rp->rio_buf));
        if (rp->rio_cnt<0)
        {
            if (errno!=EINTR)
                return -1;//读取出错
        }else if (rp->rio_cnt==0)//EOF
                return 0;
        else
            rp->rio_bufptr=rp->rio_buf;//重置缓冲区指针
    }
    cnt=std::min (n,(size_t)rp->rio_cnt);
    memcpy (usrbuf,rp->rio_bufptr,cnt);
    rp->rio_cnt-=cnt;
    rp->rio_bufptr+=cnt;
    return cnt;
}
ssize_t Rio::rio_readlineb (rio_t* rp,void *usrbuf,size_t maxlen)
{   
    int isread;
    int i;
    char c,*buf=static_cast<char*> (usrbuf);
    for (i=0;i<maxlen;++i)
    {
        if ((isread=rio_read (rp,&c,1))==1)
        {
            *buf++=c;
            if (c=='\n')
                break;
        }else if (isread==0)
        {
            if (i==0)
                return 0;//EOF,且未读入任何数据
            else
                break;
        }else
            return -1;
    }
    *buf='\0';
    return i+1;
}
ssize_t Rio::rio_readnb (rio_t* rp,void *usrbuf,size_t n)
{
    int isleft=n;
    int isread;
    char *buf=static_cast<char*> (usrbuf);
    while(isleft>0)
    {
        if ((isread=rio_read(rp,buf,n))<0)
            return -1;
        else if (isread==0)//EOF
            break;
        isleft-=isread;
        buf+=isread;
    }
    return (n-isleft);
}

RIO同时是线程安全的,因为它给每一个文件配置了一个缓冲区,当不同线程读取不同文件的时候是线程安全的,当然当不同线程读取同一个文件还是会出问题,这就要用锁机制了
代码会同步更新再github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值