网络编程中的半包粘包问题

本文主要是讨论一下网络编程的基础,组包解包,以及常见的半包粘包。

在开发网络应用程序的话,特别是偏底层涉及具体的应用协议的时候,你可能会碰到服务端得到的数据不完全的问题。比如说客户端发送了1024个字符,但是你先得到了其中512字节,剩下的数据之后才送到。而已这通常发生在TCP协议下。理论上来说,TCP相对于另一种协议UDP,保证不会丢包,丢了也会重传,而且包的顺序是可预测的,但是对于传输的包大小没有约定。

一方面有类似nagle算法会把客户端的一些长度很短的包组合起来一次发送,另一方面因为以太网MTU等数据长度限制的存在,发到服务器的包有可能被分割成几个连续发送。前者可能会造成把逻辑上分离的包合在了一起,这通常称为粘包问题。后者可能会导致服务端处理包需要多次接收才能开始处理,这通常称为半包问题。

对于这两类问题,首先你要把TCP理解为面向流的协议,而不是包。其次你可以考虑怎么用处理文件的输入流的方式来处理网络的输入流。具体的,常见的解决方案:

  • 逻辑上定长,你可以不关心底层传输的包长度,一个逻辑上的完整包的长度
  • 使用特定分隔符分割,比如按行
  • 对于内容长度不定的数据,你可以采用先传包长度,然后传包内容
  • 应用层处理

(来自 http://www.cnblogs.com/sloong/p/5047743.html

其中,按行分割在普通文件读取中也有应用,库往往会开辟一个缓冲区,读入一定数据,找到行分隔符停止,这和网络编程中有点类似,不过网络编程的异常处理要更加小心,否则你会碰到帧(比如一行)过长等攻击。

Java NIO基础之ByteBuffer学习

Java NIO使用的数据结构不是普通的byte[],而是类似ByteBuffer的类。ByteBuffer使用起来与byte[]有很大不同,下面主要是自己学习到的一些使用方法。

ByteBuffer同时可以读写,一开始理解有点困难,建议按照推荐的操作步骤来使用ByteBuffer。即

  1. Channel#read(ByteBuffer)或者ByteBuffer#put方法,往ByteBuffer里面写入数据
  2. ByteBuffer#flip
  3. Channel#write(ByteBuffer)或者ByteBuffer#get方法,从ByteBuffer里面读取数据
  4. ByteBuffer#clear, ByteBuffer#compact

读写模式通过不同的API来切换,一般情况下,按照上面这个顺序使用ByteBuffer就可以。不过要正确理解和使用ByteBuffer,需要对ByteBuffer的内部有所了解。

ByteBuffer主要有三个位置指针(不是C/C++的pointer)

  • position,当前位置
  • limit,当前模式下的位置限制
  • capacity,分配时确定的空间大小

capacity因为一开始就被确定了,最容易理解。剩下两个位置指针需要配合ByteBuffer的“读写模式”来理解。

  1. ByteBuffer被分配之后,默认是“写模式”,因为刚分配的空间你读取是没有意义的。这时position为0,limit为capacity的值。假设代码中执行了一些写入操作,每次写入都会增加position。
  2. flip切换读写模式,position为0,即你写入的第一个byte的位置,limit变成你写入数据的最后一个byte后面的位置。
  3. 接下来你可以从ByteBuffer中读取数据了,每次读取增加position。
  4. 读取完毕之后,可以复用这个ByteBuffer,调用clear的话,position重置为0,limit设置为capacity,即回到写模式的初始状态。调用compact的话,你没有读取的数据会向前移动(System.arrayCopy),position设置为这些数据末尾之后的位置,limit设置为capacity。

用代码来测试一下

(more…)

非阻塞IO与reactor模式

在写了非阻塞IO的基础知识之后,决定学习一下常规的非阻塞IO运行模式。

所谓运行模式,就是指以怎样的代码来实现非阻塞IO服务。为了比较和说明,先从阻塞IO的线程池化的服务器开始。

这是一个简单的Echo服务。ServerSocket在accept得到一个Socket之后,由线程池中的某个线程来处理这个Socket。

(more…)

非阻塞IO与异步IO

本文是自己对最近学习到的IO相关知识的一点整理,之后会逐渐增加。

首先非阻塞IO(non-blocking IO)相信很多人都听说过,比如Nginx,Redis,NodeJS等等。得到的印象大多是非阻塞IO比传统IO(blocking IO)要好。这里多少有点误解。

非阻塞IO的目的是高并发,比如C10K这种目标。在连接数不高的时候性能并不会比传统IO好。为什么传统IO难以做到C10K,主要原因还是可以建立的进程/线程数量有限,以及高并发情况下IO等待时间太多,阻塞进程/线程运行等原因。

当然非阻塞IO并不是完全非阻塞的,IO通常分为数据等待和数据从内核空间拷贝到用户空间的两部分,传统IO(阻塞IO)在这两个步骤都会阻塞,但是非阻塞IO只在数据拷贝的时候阻塞,数据等待时系统通常会返回一个特定的异常码来提示数据未准备好。

非阻塞IO是一个行为特征,具体实现有select/poll,Linux 2.6之后的epoll等IO多路复用的系统调用。直观上来讲,非阻塞IO和IO多路复用没有关系,但是如果你一直在某个fd(文件描述符)上轮询的话,就会变得和传统IO没有区别,所以一般都是在多个fd上等待,当其中某个数据就绪了再取数据,这样就可以体现在非阻塞IO在数据等待这一步非阻塞的优势了。

从编程语言来说,非阻塞也可以选择用注册callback异步调用来实现。实际的IO类型中也有异步IO,但是大部分人都不怎么谈到异步IO,说非阻塞IO其实就是指epoll,其原因之一是Linux上AIO(异步IO)实现差强人意,Windows上IO多路复用只有select,剩下的就是异步IO,即IOCP,Windows貌似希望开发人员使用IOCP而不是开发类似epoll在Windows上的实现。服务器端开发,你懂的,Windows的用武之地很少。

IO多路复用为什么epoll脱颖而出,主要还是他的数据通知机制。select虽然大部分平台都支持,但是fd有数量限制,1024个。poll消除了这个限制,但是得到数据就绪的响应之后,你必须遍历庞大的fd列表来得到就绪的fd。epoll的优势在于返回的响应中包含数据就绪的fd,综合各方面来说是最优的。

当然epoll是Linux的系统调用,如果在Windows上你只能用select,在Solaris上你需要/dev/poll,OS X是kqueue,通常编程语言会帮你统一API,比如Java。但是关于非阻塞IO的基本知识建议还是要了解一下。

参考:

台場 2016-08-27

趁着台风来之前做点ingress任务,选择了台場。

查路线的时候鬼使神差地选了一个到现在为止我还读着拗口的「ゆりかもめ」电车,然后选择在「お台場海浜公園」下车。自然而然的我就开始做那个18个「お台場公園めぐり」连续任务了。

刚开始一两个我觉得还没什么,还觉得那两个不同颜色的缠在一起的管子挺好玩的。

任务一开始会往「お台場第三公園」方向走,那个公园人很少,从地图上看是一个正方向的公园,三面环海,中间有个盆地。

(more…)

2016年8月25日

好久没有写博客了。但是VPS的钱还是要交,于是想着继续写点东西吧。

之前博客放置不写,有一个原因是没法升级到最新WP,今天查了下,感觉可能是服务器的防火墙的问题,增加了可以访问外部HTTPS网站之后就可以了。

小结就是iptables功力不够。

今天另外做的一件事情是换了主题,这样感觉就和以前不一样了。

暂时就这些。

来日本一周年了

去年的今天,一个人固执的来到了日本,开始了新的生活。

离开父母,去陌生的城市陌生的国家,讲陌生的语言。
在这之前我从来不敢想象。
以便至今都没有任何的真实感。
每天上下班,看着帷子川里流动的水,就会想,为什么我在这里,我真的在这里吗?

今天和我妈谈到时间,她说,人年纪越大就会觉得时间过得越快。
看来我已经是老了。

还记得一年前的那个晚上,刚搬进Share house时房间里空空荡荡的,隔壁烧烤店和楼下卡拉OK的声音交杂着传入耳里,关了灯裹在公司给的廉价的被褥里,不停的问自己是不是在做梦。

在Share house里生活到了去年3月底,经历了很多事,认识了很多人。
一边学着日语,一边习惯生活,一边忙着工作。
一边想着,为什么我会在这里。

樱花盛开的晚上一个人扛着大包小包的家具用品走在去新公寓的路上。
累了就在树下坐会儿,拍拍樱花,看着路灯洒在河里的光和月亮的倒影交织在一起。

新公寓不大,就一个房间,加上阁楼也就9平米。
在那个工作也忙得像狗一样的四月里,还得跑家具城为了那蜗居购置家具布置房间。
人生第一次,拥有自己的一套家具。

一切安顿下来后的初夏,开始了登山。
人总是怕闲,或者说是怕寂寞。
从高尾山、日ノ出山、金時山、尾濑高原,到富士山顶,一个人,说是体验人生什么的都是假的。
爬山的时候,很少会去想东西,身边一个人都没有,往往走了一个小时却看不到人影。
即便如此,也不会去思考什么。
只是走,不停的向前走。

就这么一个人安安静静的过着日子,转眼到了九月份,身边多了一个吵吵嚷嚷的家伙。
吵吵嚷嚷的过上了当初约定的两个人的「小日子」。
偶尔晚上一个人还醒着的时候,还是会怀念一个人住的那段光景。

于是,一年过去了。

8月24日随笔

时间一晃已经到了24日,距离辞职已经两周多了。这段时间除了处理事情之外就是休息了,现在记录一些这些天的小事。

口吃

在原公司头两年我是没口吃的,但是三四年的时候我发现自己在说话时会很高几率口吃。原因我大致知道,长时间工作没有和他人谈话。两点一线,在家也很少谈话。解决方法理论上很简单就是平时和别人多说话。不过造成这样情况不得不说原来小组的氛围太压抑,工作分配不合理等等。
现在几个星期后,感觉自己好多了,基本没有那种症状了,主要可以轻松休息,做自己喜欢的事情。这也算是这段时间的工作状态恢复吧。

twitter

月初对twitter很上心,因为好久没用了。不过最近发现时间线上的内容好没有营养,越是生活推越是有固定的圈子;而且年龄层次不同谈的内容感觉也不同,好多学生;在职的稍微好点,几个现实中就有接触的自然没什么问题,其他的感觉难谈拢。唉,还是当做信息窗口吧。

(more…)