unix网络编程2 读写函数介绍

Inroduction

  • 这一节首先介绍必要的基础知识,比如网络字节序和主机字节序,套接字地址结构,然后详细叙述各个套接字api,最后给出一个线程安全的读写函数,读写函数非常重要,其中还会写一个带缓冲的读函数,用于处理文本行,减少上下文切换。

基本函数介绍

  • ipv4套接字介绍(这里没把套接字结构里面所有内容写出来)
#include<netinet/in.h>
struct sockaddr{
    sa_famliy_t sin_family;//协议族,ipv4是AF_INET
    in_port_t sin_port;//端口号
    struct in_addr sin_addr;//网络字节序32位ip地址
 }
  • 套接字地址有很多种如下图
    这里写图片描述

  • 不仅仅是ipv4套接字地址结构,还有ipv6,unix域等,每一种协议的地址结构是不同的,后面使用的套接字函数如bind是ANSI C之前定义的,以前是定义的通用套接字结构,为了使用这些函数,必须进行强制类型转换。

  • 通用套接字地址结构
struct sockaddr{
    sa_family_t sin_family;
}
  • 字节排序函数
#include<netinet/in.h>
下面两个函数为主机字节序转网络字节序
htons 16位
htonl 32
    void bzero(void *dest,size_t nbytes);
    目标字符串指定数目字节置为0,常用来把套接字地址结构置为0

I/O包

  • 下面介绍读写函数,上一节介绍了应用进程的缓冲区可以无限,但是在TCP层的缓冲区是由对端告知的有限大小,所以read和write会返回不足值所以网络数据可能会反复使用read,write,下面会介绍一个健壮I/O包解决多次多写,而且这些函数是线程安全的。

  • 首先介绍基本读写函数

注意ssize_t与size_t的区别
ssize_t 被定义为int有符号
size_t 被定义为unsigned int 无符号整型

#include<unistd.h>
从fd描述符关联的文件读数据到buf
返回值:成功返回读的字节数,若是EOF返回0(没有数据可读了),出错-1
ssize_t read(int fd,void *buf,size_t n);

ssize_t write(int fd,const void *buf,size_t n);
从buf写n个字节到fd关联的文件
返回值:成功返回写的字节数,出错-1

  • 无缓冲区输入输出函数
    直接在存储器和文件之间交换数据,没有缓冲。把2进制数据写到网络很有用
    下面这个读函数在网络数据很有用,对于一个很大的数据可以反复调用read函数,而且出现中断返回,每个函数手动重启read
ssize_t rio_readn(int fd,void *buf,size_t n){
    size_t nleft;//剩下多少没有读完的数据
    char *pbuf;
    nleft = n;
    pbuf = buf;//用来指示当前读到缓冲区哪一个字节的数据

    while(nleft > 0){
        if((nread = read(fd,buf,n))< 0){
            if(errno == EINTR)//中断
                nread = 0 //重新再读
            else 
                return -1;
        }else if(nread == 0)//没数据读了这种情况造成不足值
                break;
       pbuf += nread;
       nleft -= nread;
    }

    return (n-nleft);//返回读了的多少字节的数据
}
ssize_t rio_written(int fd,const void *buf,size_t n){
        size_t nleft = n;
        char *pbuf = buf;
        while(nleft > 0){
            if((nwritten = write(fd,buf,n)) < 0 ){
                if(errno == EINTR)
                    nwritten = 0;
                else 
                    return -1;
            }
            pbuf += nwritten;
            nleft -= nwritten;
        }
        return n;
}
  • 带缓冲的读函数
    这样做的目的减少read的反复读,减少陷入内核,上下文切换的开销
    下面一读函数是缓冲的,它从fd出读10240字节(1024的倍数)到缓冲区。这样做我们需要定义一个结构,如下
#define RIO_BUFSIZE 10240
typedef struct {
    int rio_fd;//用来关联的文件描述符
    int rio_cnt;//内部缓冲区中剩余字节数
    char rio_buf[RIO_BUFSIZE];//内部缓冲区
    char *rio_bufp;//如果内部缓冲区已经读了一部分那么rio_bufp的作用就是指向已读完的字节的下一个

}rio_t
  • 初始化函数
    用来把文件描述符和上面的结构联系起来,并初始化
void rio_readinit(rio_t *rp,int fd){
    rp->rio_fd = fd;
    rp->cnt = 0;
    rp->rio_bufp = rp->rio_buf;
}
  • 带缓冲的read函数
ssize_t rio_read(rio_t *rp,char *buf,size_t n){
    int cnt;
    //缓冲区没数据就调用读函数往缓冲区读数据
    while(rp->rio_cnt <=0){
        if((rp->rio_cnt = read(rp->rio_fd,rp-  >rio_buf,sizeof(rp->rio_buf)))<0){
            if(errno != EINTR)
                return -1;
        }else if(rp->rio_cnt == 0){//没数据读了EOF
            return 0;
        }else
        rp->rio_bufp =  rp->rio_buf;//每一次重新填充缓冲区,就把rio_bufp置"0"
    }
    cnt = n;
    if(rp->rio_cnt < n)
        cnt  = rp->rio_cnt;
    memcpy(buf,rp->rio_bufp,cnt);
    rp->rio_bufp += cnt;
    rio_cnt -= cnt;
    return cnt;
}
  • 带缓冲区的readn函数
ssize_t readnb(rio_t *rp,void *buf,size_t n){
        size_t  nleft = n;
        ssize_t nread;
        char *bufp = buf; 
        while(nleft > 0){
            if((nread = rio_read(rp->fd,buf,n)) < 0){
                if(errno == EINTR)
                    nread = 0;
            }else if(nread == 0){
                break;
            }
            bufp += nread; 
            nleft -= nread;
        }
        return (n-nleft);

}
  • 带缓冲的readline
void readlineb(rio_t *rp,void *buf,size_t maxlen){

        ssize_t  rc;//读一个字节
        char c ,*bufp = buf
        for(n = 1; n < maxlen ;n++){
            if((rc = rio_read(rp->fd,&c,1)) == 1){
                *bufp++ = c;
                if(c == '\n')
                    break;
            }else if(rc == 0){
                if(n == 1)
                    return 0;//没有数据读
                else
                    break;//读到数据,遇到EOF
            }else
                return -1;
        }
        *bufp = 0;
        return n;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值