字节流套接字上的read和write函数所表现的行为不同于通常的文件IO
字节流套接字上调用read或write输入或输出的字节数可能比请求的数量少,然而这不是出错的状态
这个现象的原因在于内核中用于套接字的缓冲区可能已经达到了极限
此时需要的是调用者再次调用read或write函数,以输入或输出剩余的字节
ssize_t readn(int fd, void *vptr, size_t n)
/* include readn */ #include "unp.h" ssize_t /* Read "n" bytes from a descriptor. */ readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; /* and call read() again */ else return(-1); } else if (nread == 0) break; /* EOF */ nleft -= nread; ptr += nread; } return(n - nleft); /* return >= 0 */ } /* end readn */ ssize_t Readn(int fd, void *ptr, size_t nbytes) { ssize_t n; if ( (n = readn(fd, ptr, nbytes)) < 0) err_sys("readn error"); return(n); }
ssize_t writen(int fd, const void *vptr, size_t n)
/* include writen */ #include "unp.h" ssize_t /* Write "n" bytes to a descriptor. */ writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; /* and call write() again */ else return(-1); /* error */ } nleft -= nwritten; ptr += nwritten; } return(n); } /* end writen */ void Writen(int fd, void *ptr, size_t nbytes) { if (writen(fd, ptr, nbytes) != nbytes) err_sys("writen error"); }
ssize_t readline(int fd, void *vptr, size_t maxlen)
/* include readline */ #include "unp.h" static int read_cnt; static char *read_ptr; static char read_buf[MAXLINE]; static ssize_t my_read(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); read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return(1); } ssize_t readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == ' ') break; /* newline is stored, like fgets() */ } else if (rc == 0) { *ptr = 0; return(n - 1); /* EOF, n - 1 bytes were read */ } else return(-1); /* error, errno set by read() */ } *ptr = 0; /* null terminate like fgets() */ return(n); } ssize_t readlinebuf(void **vptrptr) { if (read_cnt) *vptrptr = read_ptr; return(read_cnt); } /* end readline */ ssize_t Readline(int fd, void *ptr, size_t maxlen) { ssize_t n; if ( (n = readline(fd, ptr, maxlen)) < 0) err_sys("readline error"); return(n); }
内部函数my_read每次最多读MAXLINE个字符,然后每次返回一个字符
readline函数本身的唯一变化是用my_read调用取代read
readlinebuf这个新函数能够展露内部缓冲区的状态,便于调用者查看当前文本行之后是否收到了新的数据
套接字地址结构是每个网络程序的重要组成部分,我们分配它们,填写它们,把指向它们的指针传递给各个套接字函数
有时我们把指向这些结构之一的指针传递给一个套接字函数,并由该函数填写结构内容
我们以引用形式来传递这些结构
而起将结构的大小作为另外一个参数来传递
当一个套接字函数需要填写一个结构时,该结构的长度也以引用形式传递,这样它的值也可以被函数更改
我们把这样的参数称为值-结果参数
套接字地址结构是自定义的,因为它们总是以一个标识其中所含地址之协议簇的字段开头
支持长度可变套接字地址结构的较新实现在开头还包含一个长度字段
它含有整个结构的长度信息
在表达格式(我们平时书写的格式,ascii字符串)
和数值格式(存放在套接字地址结构中的格式)
之间转换IP地址的2个函数:inet_pton和inet_ntop
TCP套接字为应用进程提供一个字节流,它们没有记录标记。从TCP套接字read的返回值可能比我们请求的数量少,但是这不代表发生了错误。