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 位
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;
}