字節流套接字上的read和write函數所表現的行為不同於通常的文件IO,字節流套接字上調用read和write輸入或輸出的可能比請求的數量少,然而這不是出錯的狀態,例如某個中端使read和write提前返回,這時就應該繼續讀和寫而不是出錯返回了,下面是unp中對read和write函數在socket中的使用的封裝。
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
ssize_t readn(int fd,void* buff,size_t nbytes){
ssize_t nleft;
ssize_t nread;
char* ptr;
nleft=(ssize_t)nbytes;
ptr=(char*)buff;
nread=0;
while(nleft>0){
if((nread=read(fd,ptr,nleft))<0){
if(errno==EINTR){
nread=0;
}
else{
return -1;
}
}
else if(nread==0){ //read返回0代表讀到EOF
break;
}
else{
nleft-=nread;
ptr+=nread;
}
}
return (nbytes-nread);
}
ssize_t writen(int fd,void* buff,size_t nbytes){
ssize_t nleft;
ssize_t nwrite;
char* ptr;
nleft=(ssize_t)nbytes;
nwrite=0;
ptr=(char*)buff;
while(nleft>0){
if((nwrite=write(fd,ptr,nleft))<=0){ //write返回0代表出錯
if(nwrite<0&&errno==EINTR){
nwrite=0;
}
else{
return -1;
}
}
else{
nleft-=nwrite;
ptr+=nwrite;
}
}
return (nbytes);
}
//頻繁使用read函數這個效率非常低
ssize_t readline(int fd,void* buff,size_t nbytes){
ssize_t n,rc;
char c,*ptr;
ptr=(char*)buff;
for(n=1;n<nbytes;++n){
again:
if((rc=read(fd,&c,1))==1){
*ptr++=c;
if(c=='\n')
break;
}
else if(rc==0)
return(n-1);
else{
if(errno==EINTR){
goto again;
}
return -1;
}
}
return (n);
}
//下面是一個改進版的但是這個版本使用了static變量,它是線程不安全的
#define MAXLINE 4096
static int read_cnt;
static char* read_ptr;
static char read_buf[MAXLINE];
//先從內核讀一些數據到用戶空間,在每次都從用戶空間中讀數據
static ssize_t myread(int fd,char* ptr){
if(read_cnt<=0){
again:
if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0){
if(errno==EINTR)
goto again;
return -1;
}
else if(read_cnt==0)
return 0;
else
read_ptr=read_buf;
}
read_cnt--;
*ptr=*read_ptr++;
return 1;
}
ssize_t readline(int fd,void* buff,size_t maxlen){
ssize_t n,rc;
char c,*ptr;
ptr=(char*)buff;
for(n=1;n<maxlen;++n){
if((rc=myread(fd,&c))==1){
*ptr++=c;
if(c=='\n')
break;
}
else if(rc==0){
*ptr='\0';
return (n-1);
}
else{
return -1;
}
}
*ptr='\0';
return n;
}
ssize_t readlinebuf(void **vptrtptr){
if(read_cnt)
*vptrvptr=read_ptr;
return read_cnt;
}