readn

 

在Linux中,read的声明为:

ssize_t read(int fd, void *buf, size_t count);

它的返回值有以下情形:

1.大于0,代表成功读取的字节数

2.等于0,代表读取到了EOF,一般是对方关闭了socket的写端或者直接close

3.小于0,出现错误。

我们编写一个readn函数,声明与read一致,但是,readn在未出错或者fd没有关闭的情况下,会读满count个字节

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count; //剩余的字节数
ssize_t nread; //用作返回值
char *bufp = (char*)buf; //缓冲区的偏移量 while(nleft > 0)
{
nread = read(fd, bufp, nleft);
if(nread == -1)
{
if(errno == EINTR)
continue;
return -1; // ERROR
}
else if(nread == 0) //EOF
break; nleft -= nread;
bufp += nread;
} return (count - nleft);
}

readn的返回值含义如下:

1.小于0,出错

2.等于0,对方关闭

3.大于0,但是小于count,对方关闭

4.count,代表读满count个字节

 

writen

 

write函数的声明如下:

ssize_t write(int fd, const void *buf, size_t count);

man手册中对write的返回值描述如下:

       On success, the number of bytes written is returned (zero indicates nothing was  writ‐

       ten).  On error, -1 is returned, and errno is set appropriately.

       If  count  is  zero and fd refers to a regular file, then write() may return a failure

       status if one of the errors below is detected.  If no errors are detected, 0  will  be

       returned  without  causing any other effect.  If count is zero and fd refers to a file

       other than a regular file, the results are not specified.

解释如下:

成功时,返回成功写入的字节数,否则返回-1,并设置相应的errno。

如果count为0,并且fd指向一个普通文件,那么当探测到错误时返回-1.如果没有错误发生,返回0,不会产生任何影响。

如果count为0,并且fd指向的不是普通文件,那么结果未定义。

我们不去追究write为0的情形。编写write如下:

ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwrite;
const char *bufp = (const char*)buf; while(nleft > 0)
{
nwrite = write(fd, bufp, nleft);
if(nwrite <= 0) // ERROR
{
if(nwrite == -1 && errno == EINTR)
continue;
return -1;
} nleft -= nwrite;
bufp += nwrite;
} return count;
}

从代码中可以看出,writen要么写满count字节,要么失败。

 

readline

 

在网络编程中,很多协议是基于文本行的,例如HTTP和FTP,还有telnet,他们的消息每行都是以\r\n作为结束标志的。于是我们开发一个readline函数,声明如下:

ssize_t readline(int sockfd, void *usrbuf, size_t maxlen)

readline函数的语义是:

如果碰不到\n,那么读取maxlen-1个字节,最后一个位置补充\0。

否则读取到\n,在后面加一个\0。如果中间遇到EOF,直接返回0,而不是已经读取的字节数。

我们先给出一种低效的实现:

ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen)
{
char *bufp = usrbuf; //记录缓冲区当前位置
ssize_t nread;
size_t nleft = maxlen - 1; //留一个位置给 '\0'
char c;
while(nleft > 0)
{
if((nread = read(fd, &c, 1)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}else if(nread == 0) // EOF
{
break;
} //普通字符
*bufp++ = c;
nleft--; if(c == '\n')
break;
}
*bufp = '\0';
return (maxlen - nleft - 1);
}

这个的思路很简单,每次读取一个字节,直到遇到换行符为止。

这种实现是低效的,因为每次读取一个字节,都要进行一次系统调用。

在网络编程中,还有一个函数叫做recv,如下:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

它相对于read,多了一个flags选项。

有一个选项为MSG_PEEK,描述如下:

This flag causes the receive operation to return data from the beginning of the

receive queue without removing that data from the queue.   Thus,  a  subsequent

receive call will return the same data.

大致意思是它从内核中读取数据,但并不会将数据移除,所以这个flag起到了一个预览内核数据的作用。这样我们就可以先从内核中读取一大块数据,检查其中是否存在\n,如果不存在,这么将这些数据全部读取,如果存在,则读取到\n为止。

我们先实现recv_peek函数:

ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
int nread;
do
{
nread = recv(sockfd, buf, len, MSG_PEEK);
}
while(nread == -1 && errno == EINTR); return nread;
}

readline函数的实现如下:

ssize_t readline(int sockfd, void *usrbuf, size_t maxlen)
{
//
size_t nleft = maxlen - 1;
char *bufp = usrbuf; //缓冲区位置
size_t total = 0; //读取的字节数 ssize_t nread;
while(nleft > 0)
{
//预读取
nread = recv_peek(sockfd, bufp, nleft);
if(nread <= 0)
return nread; //检查\n
int i;
for(i = 0; i < nread; ++i)
{
if(bufp[i] == '\n')
{
//找到\n
size_t nsize = i+1;
if(readn(sockfd, bufp, nsize) != nsize)
return -1;
bufp += nsize;
total += nsize;
*bufp = 0;
return total;
}
} //没找到\n
if(readn(sockfd, bufp, nread) != nread)
return -1;
bufp += nread;
total += nread;
nleft -= nread;
}
*bufp = 0;
return maxlen - 1;
}

 

我们编写的这三个函数后面可以用于处理TCP分包问题,后面写文章叙述。

网络编程readn、writen和readline函数的编写的更多相关文章

  1. Linux网络编程-readn函数、writen函数、readline函数实现

    readn函数功能:在网络编程的读取数据中,通常会需要用到一个读指定字节才返回的函数,linux系统调用中没有给出,需要自己封装. readn实现代码: int readn(int fd, void ...

  2. UNIX网络编程——客户/服务器心搏函数

    阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...

  3. 【Socket】Socket网络编程常用的结构及函数小结

    名词解析 IP地址的作用是标示计算机的网卡地址,每台计算机都有一个IP地址: 端口是指计算机中为了标示在计算机中访问网络的不同程序而设的编号,并不是网卡接线的端口,而是不同程序的逻辑编号,并不是实际存 ...

  4. 网络编程API-下 (I/O复用函数)

    IO复用是Linux中的IO模型之中的一个,IO复用就是进程预先告诉内核须要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理.从而不会在单个IO上堵塞了. Linu ...

  5. UNIX网络编程读书笔记:shutdown函数

    终止网络连接的通常方法是调用close函数.不过close有两个限制,却可以使用shutdown来避免. close 把描述字的引用计数减1,仅在该计数变为0时才关闭套接口.使用shutdown可以不 ...

  6. TCP/IP网络编程之多种I/O函数

    send和recv函数 在之前的学习中,我们在不少示例中用到send和recv这两个函数,但一直没有详细解释过着两个函数中每个参数的含义.本节将介绍Linux平台下的send&recv函数 # ...

  7. UNIX网络编程读书笔记:poll函数

    poll函数提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息. poll函数原型 #include <poll.h> int poll(struct pollfd * ...

  8. UNIX网络编程读书笔记:pselect函数

    函数原型 pselect函数是由POSIX发明的,其原型如下: #include <sys/select.h> #include <signal.h> #include < ...

  9. UNIX网络编程读书笔记:select函数

    select函数概况: select函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或多个事件发生或经历一段指定的时间后才唤醒它. 作为一个例子,我们可以调用select,告知内核仅在下 ...

随机推荐

  1. [网络流24题] COGS 运输问题1

    11. 运输问题1 ★★☆   输入文件:maxflowa.in   输出文件:maxflowa.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述]     一个工厂每天生 ...

  2. xpath和CSS选择器

    .content是二进制 用来处理声音.图片.视频 .text是文本 xpath语法: /一层层查找 //不固定位置 //title/text() @选取属性 [@href]和[@href=''] . ...

  3. NS_AVAILABLE_IOS(6_0)

    http://www.cocoachina.com/bbs/read.php?tid=241951 一个简单的小问题,请诸位大侠帮助给看看 ,新手 ,勿拍砖       本帖属于CocoaChina会 ...

  4. Altium 原理图出现元件 “Extra Pin…in Normal of part ”警告

    原理是因为元器件库中的元器件的所有模式MODE不能和pcb中的引进匹配造成的,那什么又是MODE呢, 看下买的图便很清楚了, MODE可以理解为不同的视图模式吧. 然后赶紧打开原理图库中的有问题的元器 ...

  5. centos 资源链接

    不时更新.. 安装.启动相关 自动安装的精简的ISO 教你制作属于自己的CentOS 6.4一键自动化安装ISO镜像光盘 initrd介绍 理解 vmlinuz, initrd 和 System.ma ...

  6. FreeRTOS系列第2篇---FreeRTOS入门指南【转】

    转自:http://blog.csdn.net/zhzht19861011/article/details/49819309 版权声明:本文为博主原创文章,未经博主允许不得转载.联系邮箱:zhzhch ...

  7. Linux/Android——input_handler之evdev (四) 【转】

    转自:http://blog.csdn.net/u013491946/article/details/72638919 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲 ...

  8. ubuntu 更换系统源和pip源

    1 . 备份 cd /etc/apt sudo cp sources-list sources-list.bak 2 . 编辑 这里用了阿里云的源 sudo vi sources-list 将文件内容 ...

  9. asp.net+uploadify实现图片上传图片

    前段代码如下 $("#file_upload").uploadify({ 'auto': true, 'swf': '/template/js/cutImg/uploadify/u ...

  10. [BZOJ1834][ZJOI2010]network 网络扩容 最大流+费用流

    1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec  Memory Limit: 64 MB Submit: 3330  Solved: 1739 [Subm ...