网络编程readn、writen和readline函数的编写
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函数的编写的更多相关文章
- Linux网络编程-readn函数、writen函数、readline函数实现
		
readn函数功能:在网络编程的读取数据中,通常会需要用到一个读指定字节才返回的函数,linux系统调用中没有给出,需要自己封装. readn实现代码: int readn(int fd, void ...
 - UNIX网络编程——客户/服务器心搏函数
		
阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...
 - 【Socket】Socket网络编程常用的结构及函数小结
		
名词解析 IP地址的作用是标示计算机的网卡地址,每台计算机都有一个IP地址: 端口是指计算机中为了标示在计算机中访问网络的不同程序而设的编号,并不是网卡接线的端口,而是不同程序的逻辑编号,并不是实际存 ...
 - 网络编程API-下 (I/O复用函数)
		
IO复用是Linux中的IO模型之中的一个,IO复用就是进程预先告诉内核须要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理.从而不会在单个IO上堵塞了. Linu ...
 - UNIX网络编程读书笔记:shutdown函数
		
终止网络连接的通常方法是调用close函数.不过close有两个限制,却可以使用shutdown来避免. close 把描述字的引用计数减1,仅在该计数变为0时才关闭套接口.使用shutdown可以不 ...
 - TCP/IP网络编程之多种I/O函数
		
send和recv函数 在之前的学习中,我们在不少示例中用到send和recv这两个函数,但一直没有详细解释过着两个函数中每个参数的含义.本节将介绍Linux平台下的send&recv函数 # ...
 - UNIX网络编程读书笔记:poll函数
		
poll函数提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息. poll函数原型 #include <poll.h> int poll(struct pollfd * ...
 - UNIX网络编程读书笔记:pselect函数
		
函数原型 pselect函数是由POSIX发明的,其原型如下: #include <sys/select.h> #include <signal.h> #include < ...
 - UNIX网络编程读书笔记:select函数
		
select函数概况: select函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或多个事件发生或经历一段指定的时间后才唤醒它. 作为一个例子,我们可以调用select,告知内核仅在下 ...
 
随机推荐
- [LeetCode]Word Search 回溯
			
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from l ...
 - PLSQL自动断开服务器连接 (转)
			
最近,有同事反映在使用PLSQL连接数据库的时候,总是出现数据库频繁自动断开连接.这是因为资源配置文件中IDLE_TIME参数的限制,IDLE_TIME指定会话允许连续不活动的总的时间,以分钟为单位, ...
 - [LA_3938]最大连续动态和
			
Sample Input 3 1 1 2 3 1 1 Sample Output Case 1: 1 1 线段树 L,R表示该区间的左右端点,sum表示该区间值的总和 l,r表示该区间连续的最大和的左 ...
 - PHP开发
			
php.ini error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_CORE_WARNING & ~E_NOT ...
 - 手动破解的 Linux下的Maltab 2014b
			
人人网上一个很不错的东东,转发全文如下: 好久没有写日志了,今天更新一篇. 承蒙 @刘慎修(263525031 )修哥分享的各种激励,发布一发我自创的黑科技. Maltab 2014b 发布了,但是只 ...
 - 优化html中mp4视频加载速度
			
如果使用参数faststart就会在生成完上边结构之后将moov移动到mdat前面:ffmpeg –i input.flv –c copy –f mp4 –movflags faststart out ...
 - 判断dataset是否被修改—DataSet.HasChanges 方法
			
DataSet.HasChanges 方法 获取一个值,该值指示 DataSet 是否有更改,包括新增行.已删除的行或已修改的行. 命名空间: System.Data程序集: System.Da ...
 - win7下提权代码
			
inline BOOL SetPrivilege() { HANDLE hProcess, hToken; TOKEN_PRIVILEGES NewState; LUID luidPrivilegeL ...
 - .net 实用功能
			
c#的DateTime.Now函数详解 [C#]Color颜色对照表 Sql日期时间格式转换 关于asp.net中页面事件加载的先后顺序 asp.net在ie7中使用FileUpload上传前预览图片 ...
 - (2)C#工具箱-公共控件2
			
1.MaskedTextBox 限制填写数据格式的文本框 2.MonthCalendar 用法和DateTimePicker相同 日历 3.NotifIcon (1)添加此控件后,此界面运行时会弹出用 ...