博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux网络编程(附1)——封装read、write
阅读量:4133 次
发布时间:2019-05-25

本文共 4297 字,大约阅读时间需要 14 分钟。

原打算实践简单的模型的时候,主要专注于主要的模型,先用UNIX I/O糊弄下,但是未封装的read和write用起来实在心累,还是直接用前辈们已经实现好的高级版本read、write。

UNIX I/O   read、write

#include
ssize_t read(int fd, void* buf, size_t n);若成功则为读的字节数,若EOF则为0,若出错则为-1;ssize_t write(int fd, const void* buf, size_t n);若成功则为写的字节数,若出错则为-1
buf被拷贝的位置,n最大拷贝的字节数,fd描述符

在某些情况下,read和write由于各种原因会传送比预估要少的字节数(short count),但是有些时候这个并不是由于错误造成的,还可能由于以下几个原因。

1、读时遇到EOF: 假设我们准备读一个文件,该文件从当前文件位置开始只含有30个字节,而我们以60个字节的片进行读取(一般情况下重复调用read、write传入的n固定)。这样下来,返回值为30,此后再读的时候通过返回0来发出EOF信号。

2、从终端读,每个read函数将以此传送一个文本行。(此时会不断的返回,然后不断再次调用,封装read后可以直接智能处理这种情况)。

3、读和写网络套接字(socket)。内部缓冲约束、和网络延时(上面红色部分提到)会影响read和write提前返回。

非网络情况下,直接读本地文件,read、和write基本上不会遇到非EOf的(short count)情况。

高级I/O

非缓冲版本(并不是没有内核缓冲、是没有内核缓冲之上的二级缓冲)

这些函数直接在存储器和文件之间传送数据,没有应用级缓冲。它们对将二进制数据读写到网络和从网路读写二进制数据尤其有用。

readn

ssize_t readn(int fd,void *usrbuf,size_t n){    size_t nleft = n;    ssize_t nread;    char *bufp = usrbuf;    while(nleft > 0){        if((nread = read(fd,bufp,nleft)) < 0){            if(errno == EINTR){/*interrupted by sig handler return*/                nread = 0;            }else{                return -1;/*error*/            }        }else if(nread == 0){            break;  /*EOF*/        }else{/*read content*/            nleft -= nread;            bufp += nread;        }    }    return (n - nleft);}
writen
ssize_t rio_writen(int fd,void *usrbuf,size_t n){    size_t nleft = n;    ssize_t nwritten;    char *bufp = usrbuf;    while(nwritten = write(fd,bufp,nleft) <= 0){        if(errno == EINTR){            nwritten = 0;        }else{            return -1;        }        nleft -= nwritten;        bufp += nwritten;    }    return n;}

作为工具函数、具体实践就暂时不做了,反正以后会经常用到。

注意:writen的返回值其实是固定的,每次为n。但是read不固定。

原因:简单理解   远端write --->远端计算机(内核缓冲) ----------(网络传输中)----------->本地计算机(内核缓冲)--------->read

上面可以看出,read是读远端的内容,所以不能确定到底要读多少。但是write是往本地写的,应用程序知道要写多少数据,即便是一次没有写完,也可以通过反复调用写完心目中的数据。

带缓冲版本(内核缓冲之上)

此类函数允许高效地从二进制文中读文本行和二进制数据,这些文件缓存在应用级缓存内,类似于像printf这样的标准I/O函数提供的缓冲区。

缓冲区结构体(其实就是一些信息加一个大的数组)

#define RIO_BUFSIZE 8192typedef struct{    int rio_fd; /*To operate the file descriptor*/    int rio_cnt;/*unread bytes in internal buf*/    char *rio_bufptr;/*next unread byte int internal buf*/    char rio_buf[RIO_BUFSIZE];/*internal buf*/}rio_t;
初始化缓冲区(其实就是将缓冲区和网络描述符fd联系起来)

rio_init

没打开一个文件描述符都会调用一次rio_init

void rio_readinitb(rio_t *rp,int fd){    rp->rio_fd = fd;    rp->rio_cnt = 0;    rp->rio_bufptr = rp->rio_buf;}

rio_read(带缓冲版本的read, 和unix read(未封装的)具有一模一样的效果)

static ssize_t rio_read(rio_t *rp,char *usrbuf,size_t n){    int cnt;    while(rp->rio_cnt <= 0){/*Read the file content if buf is empty*/        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 {/*reset buf ptr*/            rp->rio_bufptr = rp->rio_buf;        }    }    /*when n < rp->rio_cnt, need copy some times */    cnt = n;    if(rp->rio_cnt < n){/*one time copy end*/        cnt = rp->rio_cnt;    }    memcpy(usrbuf,rp->rio_bufptr,cnt);    rp->rio_bufptr += cnt;    rp->rio_cnt -= cnt;    return cnt;}
rio_readlineb

每次第一行,最多读maxlen-1,最后一个字留给空字符,超过maxlen-1的话将被截断,并用空字符结束

ssize_t rio_readlineb(rio_t *rp, void *usrbuf,size_t maxlen){    int n,rc;    char c,*bufp = usrbuf;    for(n = 1; n < maxlen; n++){        if (( rc = rio_read(rp,&c,1)) == 1){            *bufp++ = c;            if(c == '\n'){                break;            }        }else if (rc == 0){            if(n == 1){/*EOF no data read*/                return 0;            }else{/*EOF some data read*/                break;            }        }else{/*ERROR*/            return -1;        }    }    *bufp = 0;/*string end sign :'\0'*/    return n;}
rio_readnb(带缓冲版本的readn)
ssize_t rio_readnb(rio_t *rp,void *usrbuf,size_t n){    size_t nleft = n;    ssize_t nread;    char *bufp = usrbuf;    while(nleft > 0){        if((nread = rio_read(rp,bufp, nleft)) < 0){            if(errno == EINTR){/*interrupted by sig handler return*/                nread =0;            }else{/*errno set by read() */                return -1;            }        }else if(nread == 0){/*EOF*/            break;        }        nleft -= nread;        bufp += nread;    }    return (n-nleft);/*return >=0*/}

转载地址:http://ekjvi.baihongyu.com/

你可能感兴趣的文章
好多程序员都认为写ppt是很虚的技能,可事实真的是这样么?
查看>>
如果按照代码行数发薪水会怎样?码农:我能刷到公司破产!
查看>>
程序员失误造成服务停用3小时,只得到半月辞退补偿,发帖喊冤
查看>>
码农:很多人称我“技术”,感觉这是不尊重!纠正无果后果断辞职
查看>>
php程序员看过来,这老外是在吐糟你吗?看看你中了几点!
查看>>
为什么说程序员是“培训班出来的”就是鄙视呢?
查看>>
码农吐糟同事:写代码低调点不行么?空格回车键与你有仇吗?
查看>>
阿里p8程序员四年提交6000次代码的确有功,但一次错误让人唏嘘!
查看>>
一道技术问题引起的遐想,最后得出结论技术的本质是多么的朴实!
查看>>
985硕士:非科班自学编程感觉还不如培训班出来的,硕士白读了?
查看>>
你准备写代码到多少岁?程序员们是这么回答的!
查看>>
码农:和产品对一天需求,产品经理的需求是对完了,可我代码呢?
查看>>
程序员过年回家该怎么给亲戚朋友解释自己的职业?
查看>>
技术架构师的日常工作是什么?网友:搭框架,写公共方法?
查看>>
队列的基本操作
查看>>
别把最美女教师只当成宣传工具
查看>>
母函数(Generating function)详解
查看>>
内存对齐
查看>>
C++ 虚函数表解析
查看>>
Aviator—Java表达式求值引擎的使用
查看>>