您的位置:首页 > 理论基础 > 计算机网络

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

2015-04-09 17:31 435 查看

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;
//缓冲
4000
区没数据就调用读函数往缓冲区读数据
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息