利用recv和readn函数实现readline函数
在前面的文章中,我们为了避免粘包问题,实现了一个readn函数读取固定字节的数据。如果应用层协议的各字段长度固定,用readn来读是非常方便
的。例如设计一种客户端上传文件的协议,规定前12字节表示文件名,超过12字节的文件名截断,不足12字节的文件名用'\0'补齐,从第13字节开始是
文件内容,上传完所有文件内容后关闭连接,服务器可以先调用readn读12个字节,根据文件名创建文件,然后在一个循环中调用read读文件内容并存
盘,循环结束的条件是read返回0。
字段长度固定的协议往往不够灵活,难以适应新的变化。前面讲过的TFTP协议的各字段是可变长的,以'\0'为分隔符,文件名可以任意长,再看blksize
等几个选项字段,TFTP协议并没有规定从第m字节到第n字节是blksize的值,而是把选项的描述信息“blksize”与它的值“512”一起做成一个可变长的字
段。
因此,常见的应用层协议都是带有可变长字段的,字段之间的分隔符用换行'\n'的比用'\0'的更常见,如HTTP协议。可变长字段的协议用readn来读就很
不方便了,为此我们实现一个类似于fgets的readline函数。
首先来看一个跟read 相似的系统函数recv。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
recv函数与read函数类似,但只能读取套接字描述符,而不能是一般的文件描述符,且多了一个标志参数。
flags参数比较重要的有两个,一个是MSG_OOB,即读取带外数据时候的选项,tcp头部有一个紧急指针16位的值。另一个是MSG_PEEK,即从缓冲区
返回数据但不清空缓冲区,这点与read是不同的。
下面使用封装后的recv函数实现readline函数:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/* recv()只能读写套接字,而不能是一般的文件描述符 */
ssize_t recv_peek(int sockfd, void *buf, size_t len) { while (1) { int ret = recv(sockfd, buf, len, MSG_PEEK); // 设置标志位后读取后不清除缓冲区 /* 读到'\n'就返回,加上'\n' 一行最多为maxline个字符 */ while (1) nread = ret; return ret + count;
} } if (nread > nleft) exit(EXIT_FAILURE); nleft -= nread; ret = readn(sockfd, bufp, nread); if (ret != nread) exit(EXIT_FAILURE); bufp += nread; return -1; |
在readline函数中,我们先用recv_peek”偷窥“ 一下现在缓冲区有多少个字符并读取到bufp,然后查看是否存在换行符'\n'。如果存在,则使用readn连
通换行符一起读取(清空缓冲区);如果不存在,也清空一下缓冲区, 且移动bufp的位置,回到while循环开头,再次窥看。注意,当我们调用readn读
取数据时,那部分缓冲区是会被清空的,因为readn调用了read函数。还需注意一点是,如果第二次才读取到了'\n',则先用count保存了第一次读取的
字符个数,然后返回的ret需加上原先的数据大小。
使用 readline函数也可以认为是解决粘包问题的一个办法,即以'\n'为结尾当作一条消息。对于服务器端来说可以在前面的fork程序的基础上把do_service函数更改如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void do_echoser(int conn)
{ char recvbuf[1024]; while (1) { memset(recvbuf, 0, sizeof(recvbuf)); int ret = readline(conn, recvbuf, 1024); if (ret == -1) ERR_EXIT("readline error"); else if (ret == 0) //客户端关闭 { printf("client close\n"); break; } fputs(recvbuf, stdout); |
客户端的更改也是类似的,不再赘述,测试输出也是正常的。
参考:
《Linux C 编程一站式学习》
《TCP/IP详解 卷一》
《UNP》
利用recv和readn函数实现readline函数的更多相关文章
- UNIX网络编程——利用recv和readn函数实现readline函数
在前面的文章中,我们为了避免粘包问题,实现了一个readn函数读取固定字节的数据.如果应用层协议的各字段长度固定,用readn来读是非常方便的.例如设计一种客户端上传文件的协议,规定前12字节表示文件 ...
- Linux网络编程-readn函数、writen函数、readline函数实现
readn函数功能:在网络编程的读取数据中,通常会需要用到一个读指定字节才返回的函数,linux系统调用中没有给出,需要自己封装. readn实现代码: int readn(int fd, void ...
- TCP粘包问题的解决方案02——利用readline函数解决粘包问题
主要内容: 1.read,write 与 recv,send函数. recv函数只能用于套接口IO ssize_t recv(int sockfd,void * buff,size_t len,i ...
- 网络编程readn、writen和readline函数的编写
readn 在Linux中,read的声明为: ssize_t read(int fd, void *buf, size_t count); 它的返回值有以下情形: 1.大于0,代表成功读取的字节 ...
- 自己封装一个readline函数实现服务器客户端回射
实现的功能:一次只能读取一行,客户端输入之后,一回车,马上字符串传到服务器端并显示在终端,然后服务器端将字符串又传回给客户端. 服务器端可以接收多个客户端的连接请求,并fork一个子进程来进行服务. ...
- Python中readline()函数 去除换行符
从Python中readline()函数读取的一行内容中含有换行符\n,很多时候我们需要处理不含有换行符的字符串,此时就要去掉换行符\n. 方法是使用strip()函数. 例子如下: f = open ...
- 利用闭包特性改写addEventListener的回调函数
var numClicks = 0; document.addEventListener("click",function(){ alert( ++numClicks); },fa ...
- 利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法
1.利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法 首先判断字符串的长度是否为0,如果是,直接返回字符串 第二,循环判断字符串的首部是否有空格,如 ...
- python练习题:利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法
方法一: # -*- coding: utf-8 -*- # 利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法: def trim(s): whil ...
随机推荐
- 按示例学python:使用python抓取网页正文
平时打开一个网页,除了文章的正文内容,通常会有一大堆的导航,广告和其他方面的信息.本博客的目的,在于说明如何从一个网页中提取出文章的正文内容,而过渡掉其他无关的的信息. 这里先看看 demo : ht ...
- vue-router登录校验后跳转到之前指定页面如何实现
两个需求:1.用户点击购买需要下单,并跳转到订单页面,但是如果用户没有登录的话,中间有登录验证,会拦截:2.点击购买的时候,登录校验成功了,跳转到订单页面时,订单已创建,去付款即可.3.处理拦截至登录 ...
- Cognos 报表在列表上面显示汇总
一直以来,Cognos Report Studio设计报表的时候,汇总默认显示在列表下方: 1如图,拖一个列表 2运行如下,数据显示正常按日期排序 3选中订单笔数.订单金额,添加自动汇总 4:运行,可 ...
- C# ftp ListFilesOnServer
public static bool ListFilesOnServer(Uri serverUri) { // The serverUri should start with the ftp:// ...
- C#.NET常见问题(FAQ)-如何强制退出进程
可以使用Process.Kill方法,但是有时候执行完了该进程还在,要等一会才会自动关掉 更多教学视频和资料下载,欢迎关注以下信息: 我的优酷空间: http://i.youku.com/ac ...
- ccc数据库的水平分割和垂直分割
在数据库操作中,我们常常会听说这两个词语:水平分割和垂直分割.那么到底什么是数据库的水平分割,什么是数据库的垂直分割呢?本文我们就来介绍一下这部分内容. 1.水平分割: 按记录进分分割,不同的记录可以 ...
- iOS app集成支付宝支付流程及后台php订单签名处理
iOS app集成支付宝支付流程 1: 开通支付宝商户 由公司去支付宝 https://b.alipay.com/order/serviceIndex.htm 签约支付宝开通支付宝商家: 2:商户支付 ...
- ubuntu server vsftpd 虚拟用户及目录
ubuntu server vsftpd 虚拟用户及目录 一:需求场景: 在ubuntu server上开设一个虚拟网站,在网站目录建立一个ftp目录,允许用户通过ftp上传网站文件到网站目录: 同时 ...
- uni-app 生命周期
生命周期分为:页面生命周期和应用生命周期 生命周期可参考:uni-app官方API 注意平台支持,仅某个平台支持会显示,5+App是超HTML5+的App方案. 例如分享:只有小程序支持.这时我们就要 ...
- fis3 scss 版本报错
fis3 scss编译需要安装的node版本为4.x,node版本高了fis会报错.如下图所示: