天天看點

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位址
 }
           
  • 套接字位址有很多種如下圖
    unix網絡程式設計2 讀寫函數介紹
  • 不僅僅是ipv4套接字位址結構,還有ipv6,unix域等,每一種協定的位址結構是不同的,後面使用的套接字函數如bind是ANSI C之前定義的,以前是定義的通用套接字結構,為了使用這些函數,必須進行強制類型轉換。
  • 通用套接字位址結構
struct sockaddr{
    sa_family_t sin_family;
}
           
  • 位元組排序函數
#include<netinet/in.h>
下面兩個函數為主機位元組序轉網絡位元組序
htons 位
htonl 位
           
void bzero(void *dest,size_t nbytes);
    目标字元串指定數目位元組置為,常用來把套接字位址結構置為
           

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傳回(沒有資料可讀了),出錯-
ssize_t read(int fd,void *buf,size_t n);

ssize_t write(int fd,const void *buf,size_t n);
從buf寫n個位元組到fd關聯的檔案
傳回值:成功傳回寫的位元組數,出錯-

           
  • 無緩沖區輸入輸出函數

    直接在存儲器和檔案之間交換資料,沒有緩沖。把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 > ){
        if((nread = read(fd,buf,n))< ){
            if(errno == EINTR)//中斷
                nread =  //重新再讀
            else 
                return -;
        }else if(nread == )//沒資料讀了這種情況造成不足值
                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 > ){
            if((nwritten = write(fd,buf,n)) <  ){
                if(errno == EINTR)
                    nwritten = ;
                else 
                    return -;
            }
            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 = ;
    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 <=){
        if((rp->rio_cnt = read(rp->rio_fd,rp-  >rio_buf,sizeof(rp->rio_buf)))<){
            if(errno != EINTR)
                return -;
        }else if(rp->rio_cnt == ){//沒資料讀了EOF
            return ;
        }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 > ){
            if((nread = rio_read(rp->fd,buf,n)) < ){
                if(errno == EINTR)
                    nread = ;
            }else if(nread == ){
                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 = ; n < maxlen ;n++){
            if((rc = rio_read(rp->fd,&c,)) == ){
                *bufp++ = c;
                if(c == '\n')
                    break;
            }else if(rc == ){
                if(n == )
                    return ;//沒有資料讀
                else
                    break;//讀到資料,遇到EOF
            }else
                return -;
        }
        *bufp = ;
        return n;
}
           

繼續閱讀