tcp粘包问题补充

上篇文章

提到阻塞(block)一下如何read数据

这里针对是非阻塞如何read数据

并且纠正前面出现几个错误

(1) 非阻塞 遇到errno=EAGAIN必须continue处理 ,epoll_wait 下次还能触发吗?

(2) 服务器read一次数据 ,只解析一个包的数据

会不会出现每次客户端发送新数据 但是服务器读取仍然是历史发送记录,

缓存里留着未处理数据情况

在一个异步非阻塞的socket上调用read/write函数读为2个步骤

步骤1 调用read从系统 层读取到应用层

步骤2 解析数据

01

步骤1 调用read从系统buffer读取到应用层buffer

epoll提供两种工作模式:LT和ETLevel-Triggered and Edge-Triggered

区别是:

前者触发多次,下次触发条件:

只 要缓冲区有数据,不区分是上次未读取还是新来的

后者只 触发一次 下次触发条件:

1 有新的数据写入管道 缓冲区有数据 (consume the whole buffer data)

2 遇到EAGAIN (return EAGAIN)

3 缓冲区有数据但是属于上次遗留的 不触发

参考 man epoll 例子

如果是ET模式,管道中剩余的1KB被挂起,再次调用epoll_wait,得不到管道读者的文件句柄,除 非有新的数据写入管道

如果是LT模式,只要管道中有数据可读,每次调用epoll_wait都会触发。

//所以,在epoll的ET模式下,正确的读写方式为:

读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN(break 满足下次触发条件)

写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN(break 满足下次触发条件)

在epoll的LT模式下相反

读:忽略掉errno = EAGAIN的错误,下次继续读 continue

写:忽略掉errno = EAGAIN的错误,下次继续写

ET

02

步骤2 解析数据

说明: 这里约定数据包是指是客户端发送一次的数据

应用层 利用socket从系统底层缓冲区(buffer)read一次n字节大小数据到本地buffer

这些数据

可能客户端发送数据过大一个包拆拆分多个包发送,

也可能数据过小

多个包合并成一个包发送,

也可能就是客户端连续发送多次

解析n字节大小数据 步骤

1 小于一个包 俗称半包

判断bytebuffer中剩余数据是否足够一个包,不够继续系统缓冲区读取 IO操作

2 完整的一个包

读取一个包之后,剩余数据为零继续等待客户端下一个请求IO操作

3 包涵:多个包

继续解析,知道满足条件1和2为止

解包

关于同步和异步后面在详细说明

blocking I/O

nonblocking I/O

I/O multiplexing (select and poll)

signal driven I/O (SIGIO)

asynchronous I/O (the POSIX aio_functions)

io

本章节内容:

socket之send与发送缓冲区大小的没有任何关系

主要原因是发送缓冲区大小和接受缓冲大小可以设置任意数值

造成了这 一个数据包被多次接受才算完整

异步非阻塞的socket上调用read/write函数读为2个步骤

步骤1 如何读取数据,注意是遇到错误该如何处理

步骤2:如何处理这些数据,注意黏合包,半包如何处理

下章预告:

大纲

这次提到tcp数据流无边界特点

还有一个特点那就是 TCP协议中有长连接和短连接之分

需要心跳包传统的 keepAlive有什么缺点,为什么非要自己实现

计划:

plan

喜欢

分享

or

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171227G0H10I00?refer=cp_1026

同媒体快讯

扫码关注云+社区