00:01
TCP粘包和拆包现象实例,首先呢,我们编写一个net程序,如果我们没有做任何的处理,就会发生粘包和拆包的问题,我们看一个案例,就通过这个案例呢,我们来看是在实际的看一下粘包和拆包是怎么出现的,然后呢再说解决方案,打开我们的idea,我们写一段代码。对,OK,那这边我们就建一个包吧。就简单叫TCP好吧。GDP。好,那么这段代码呢,我我们需要写服务器端和客户端,那这个我们前面已经写了很多服务器端和客户端,我就拿来用一下,我们就用前面刚刚写过的这个client。还有这个server,诶写多了。和server这两个呢,我用一下好吧。走起来。转过来。
01:00
粘过来过后呢,我们在它的基础上再加东西就行了,我们先写客户端这边。大家看到客户端这边呢,他需要去先创建一个my client,呃,Initializer我们先写起来。好,这边。先不要让他印这个好吧,别让印错了,我们自己写一份就行。哟,他自己跑进去是吧,没事,我们把自己写份。包这个包里面去就可以了。好的,然后这边呢,我们就先。把这个拿掉。引入我们就用我们自己的就可以了。My initialize,诶,为什么他这报错呢?就在同一个包厢里,好先写上继承,继承什么呢?China。China initialize对不对?我们前面写过的,然后呢,写上socket。
02:00
Socket channel。China,然后实现它的一个方法。好的。好,再确认一下,这边引的是不是跟我们一样的,好,这样就没问题了,现在引的呢,就是我们本包下面的my clientizer。好,我这边继续写。每次这个包赢的都是。引入ni下面的,接着继续往下写,下面呢,我们还是按照以前的规则获取到他的排online。VR。拿到了。那么拿到排盘栏go呢?我们加入一个自定义的handler。At last。这个因为还没写,所以说我先写个空。对,我们先写客户端这边,那紧接着我们再来写客户端的自定义的一个handler,我们就先写my client。
03:01
MY。对不对,Handle。My client。Handler。好的。Handle。那这边我们首先人要继承什么呀,我们要按照以前的流程,我们来继承一个。这样的inbound handler,我们继承一下,叫做simple。Simple China in bond handler。对,这个时候我们整个发送的数据呢,咱们直接用BAT用net提供的BA buffer。是不是又要去实现一个方法了?哪个方法呢,China。好,现在我们先写的是哪一个,由先由客户端我们发十个数据包过去,我们这样写,重写其中的一个方法。哪一个方法呢?叫做China active active。在这里面呢,我准备这样发包,我们以前是不是每次发的都是一个,呃,一条一条的数据,这次呢,我们想用一个for循环发送十次啊,十十次数据使用客户端。
04:15
客户端。客户端发送。发送十句。十条这样。十条数据。诶,十条数据用循环,比如说叫hello。哈喽,什么呢?十我给你打招呼。那就会循环了,N ti等于零,I小于慢呢,小于十没问题吧,I就是叫加加A。没问题,然后呢,我们这边就用非词化on的点copyright buffer怎么样,往这里面放一些数据进去,比如说我们叫做hello。哈,S。
05:00
没问题吧,Hello server,我们再加一个这个I对不对,然后呢,给它进行一个出售处理。Chart。Char什么呢,Char?The end for name。我们指定我们这个编码是UTF杠八。看清楚了。也就是说我现在这个for循环呢,每次想发送的数据是hello server后面带了一个数字标识,它的不同,好,我们把这个变量接收一下。好的,接收成一个buffer就行了,Buffer。然后呢,再通过Ctx.right and flash扔出去,发出去。可以了,大家可以看到,这次呢,我们是用一个for循环发送了十条hello server后面带了一个编号。没问题吧,现在呢,我们在我们的服务器这这头来接收这个数据,同样道理,同样道理,我们服务器这边呢,也要写一个my server initialize。
06:09
Ben写上。好,这边我们服客户端这边还少了一点东西。是不是没有把它加进去,把它一次性的处理完就完了,六一个什么呀,按照原来写的六我们的my client。My client handler是不是这样就没问题了,这样就把它写全了,好,那回到这边,我们继续来写server这边的。先写server的initialize。没问题吧?写上去,那这个地方我们按照以前的流程应该写什么东西呢?我们同样也去继承。继承China。同样写上。Shocked。Sha China。
07:00
就这个。然后这边我们要去实现一个方法。实现一个方法。好的。在这里面我们怎么做,是不是按照以前的规则,仍然是先拿到China?啊,拍。拍先获取到。拿到了,拿到过后呢,我们往这边去加入一个自定义的handler。先写个空没底吧,同学们好,现在呢,我们来编写服务器端的。Handler。那就写my server。Handler。OK。那同样我们也来继承什么呢?我们前面写的simple。China in one hundred。数据当然是按照BAT。By buffer来接收,就是我们ni提供的一个数据容器。同样,我们去重写方法,实现一个方法。
08:03
好,那在这里呢,同学们,我们是不是就应该去接收我们这个数据了,那怎么去接收呢?非常的简单,我们先把这一个MSG转成一个字节数组。非常简单,那就这样写了。Bite。大家跟上我的思路,从哪取呢?MSG点盖read,这样子read read bits是不是就拿到了,拿到过后我们把它放到一个。数组里边去拜次数组,我叫buffer,就要取名叫buffer一样的。拿到书过后,是不是我们就把它读取进去,读到这面去,那就八分。那应该是mst。点read。是不是read by,呃,Read,我看看啊,叫read,对,这个是read,这个是read by。把它读进去,读到哪里去,读到buffer buffer里面去就OK。
09:02
拿到了下面呢?同学们想,是不是我们就可以把它转成一个字符,字符串将buffer。Buffer。它是一个字节数组吗?转成字符串。利于我们的输出。那就六一个使。就待会我我想看一下,在服务器上看一下到底我是怎样去接收的,把buffer放进去,然后呢。Sites。Site。对,For写上17F杠八。拿到一个string,拿到string呢,我们取个叫message。那现在我们就可以输出了,我们怎样输出呢?我们就要这样输出,就说服务器端接收到数据。加上什么呀,就是message可以了。问题吧,那同样我现在可以来,每接收一次呢,我们来统计一下到底接收接收的数据消息量是多少。
10:08
就是我到底是分几次接收到的,大家大家就是就是让大家看一下我们服务器端,你这发了十个包,发了十个包过来,我到底服务器端是收了几次才全部接收完毕,因此呢,我在这说一句话叫服务器端。服务器端接收到。到消息。六。好,接收到消息量呢,我们做一个等号加上去,怎么加呢?我在这里设置一个属性。Private统计一下int count,大家都知道我们每一个同学们,我们每一个客户端都会,呃,就说每一个客户端和服务器端呢,他们这个handler是独立的,因为每个通道都有自己的handler,所以说呃,在我们服务器这个server handler呢,是实际上是大家不会重用的,对不对?所以我在这呢就直接打出来就行了。
11:05
怎么加呢,我就这样子。这样写啊,同学们看一下就行了。刮起来,刮起来。加加Z点抗。啊,这样我就能看到到底我是分多少次把你客户端发送的十个消息接收到的。OK,好,这边就是我们China的一个,呃,读取事件。呃,读取实验读取过后呢,我也想回复一些数据给他,我想呢,在这边回一些数据给我们的客户端,好,现在我们再来看回送数据。服务器服务器。回送,回送数据给客户端。客户,客户。那我怎么来回送呢?非常简单,我也给他回送一个随机的,给他回送一个随机ID。啊,回送一个。
12:00
回送。回送一个随机。随机I地址,我也要看看客户端他是怎么接收的,好吧,那那么开始写,嗯,那仍然是非迟化。对,Copy buffer,那这里面我们就简单一点,其实就用UUID。点random id.to。没问题吧,然后呢,指定编码的方式。For name。我们仍然是按UTF杠八进行一个回送。写完写完过用Ctx.right andsh扔出去就可以了啊,不好意思,这个我忘了接收。是不是我们还是要接收一下呀,接收我们取个名字叫做。回回复的一个BA。八分。好。就这回送的,那么我把这个数据放这就可以了。
13:02
各位服务器这块就写完了,同时呢,我们把异常也给他处理一下。把异常处理一下,如果服务器团在处理数据的发生了异常怎么办呢?咱们就简单一点好吧,直接把错误信息打一下。然后呢,直接就关闭了这个通道。Clouds。就这样子,那大家想你服服务器端有回送数据给客户端,是不是就意味着我们客户端这边还要写点东西啊,诶好,我们来把这个先加进去,再说又一个my server handler。客户端这边呢,我还要在handle里面进行一个read的一个处理,是不是前面我们没有写,是因为那个时候还不知道怎么去接收,现在呢,我们已经知道他回送什么消息了,我来接收一下。怎么接收呢?也是相当简单哈,一样的bite。把我们MSG。Read able。
14:02
Read readable by就是我可读的字节数。根据这个可度系数生成一个字节数组,同样,我们也取个名字叫buffer吧。统一下。对,下面呢,就读取到把数据,把这个数据读取到我们这个BAT,嗯,就是读取到我们这个BAT数组里面去,非常简单,怎么读取呢?MC.reads。是不是,然后呢把buffer。拿到整个这个读完了之后。这个字节数组里面就有数据了,然后呢,我们也把它转成字符串。转成字符算当然是跟前面的思路一样的了,使。然后呢八对,然后呢指定编码。别写错了。UTF杠八。拿到了,拿到这个数据就是他回送的消息,对不对,我们也叫message。
15:01
然后我们输出信息,说客户端接收到接收到消息等于加上mst,同样我们呢,也来统计一下,它是分几次来接收到的,来整一个。同样在这里,我们。输一个count。对不对。然后这边呢,我们也输出。他接收到的次数是多少?客户端,客户端接收接收消息数量是分几次的。等于加上同样道理,加加this点控制完事,最后呢,我们也在客户端这边处理一下异常。对,这边异常我们也处理一下。知道吧。好的一样,跟前面一样道理,我们怎么样呢?说出错误的原因。然后呢,关闭。完事,好,同学们,现在我们代码就基本上写完了,写完我们来准备测试一下,来理解一下老师要做什么事情,现在我们客户端发送了十条数据,Hello server。
16:14
给我们的服务器端,服务器这端呢,在这里面进行了一个读取。读取的时候我想看一看服务器端到底是怎么接收的,它是有规律呢,还是没有规律呢,还是一个怎么样的情况。其实这块呢,在默认情况下,你没有做处理,它其实就会出现一个TCP的粘包和拆包问题,来我们运行一下,看看是不是这样子的。各位,我们先运行。服务器呢?运气了。看情况啊,同学们,有了这个问题,待会儿再说解决方案。请看此时刻。好,输出了一系列信息。呃,这个是我们的log破解,这个log破解我先展示把它。
17:01
关一下吧。好吧,我先暂时不用,因为这个拉拉半天,因为我现在不去做过多的调整任务嘛,所以我先不看。再启动一下我们server。好,那现在呢,我们来运行客户端。来运营客户端走起来。好,同学们可以看到。这边我们可以看到这边呢,会返回一些消息了。那我们来看这边是一个什么情况呢。从这边看呢,服务器端启动的跟我们想的不不太一样,服务器端启动的信息跟我们不一样,那说明我们这启动的服务器端肯定。跟我们这写的不是同一个文件,对吧?你要找到它的问题所在,那我们看一下my server是不是哪个地方引的不对,哦,大家看这里我引入的居然是inbound handler这个项目下边的my server in显然不对,因为我们自己有,所以说我要把它注销一下。
18:04
耶,这个怎么注销不了呢?我加一个注销。我这个快捷键用不了了,我把它稍微关一下哈,退出我再打开一下,可能是。好,我退出一下。再次打开。我的idea工具呢,可能是有些小问题。好把它打开。打开过后呢,我把刚才的那一句话注销应该就没问题了,就能够展现出我们TCP粘包和拆包的问题。好,稍等。各位。
19:01
找到我们的server。把这句话注销了。注销了。什么工具,这都什么玩意儿,这都。受不了。好的。注销了,然后。删掉就行了吧,删掉我们再把。Server initial letter打开我们发现这边引的也不对,对不对,是一个heartbeat,因为我当时是粘贴过来的,所以说呢,他可能会按照默认的方式引一些包,其实我们这些不要的,好,我们再来运行一下,看看是不是就正确了来运行。运行我们的server端。好的。然后再运行我们的客户端。来看,这次应该就没没有毛病了。各位。
20:00
这边呢,我们发现客户端收到了一个随机的字符串。是一,我们看服务器端怎么接收的,诶大家看服务器端在接收的时候是不是按照一个包来接收的。也就是说你其实发了十次,但是他是一次性接收的。对吧,那现在呢,我们再来看,如果我们再起一个客户端,它是不是总是这样做呢?VB。威逼。好,我们再来看。我们发现这一次同学们看客户端收到的返回的随机数据已经很多了。那这边呢,诶大家有没有发现这一次。从这开始看,HELLO0123,也就是说他是第一次接收的哈零。第二次接收了哈一,第三次是把二和三放在一起接收的,第四次是456,第四下一次是七,再下一次是八和九,也就是说在第二次服务器端接收的时候,我们发了十个,其实它是按照六次来接收的。
21:05
说到这边呢,他给他回复的应该也是六个随机数,那为什么看起来是一个呢?是因为我在这没有做一个分隔符,这样子为了让大家看的更清晰一点呢,我在my server handler这边。给他回送数据的时候,我给他来一个空格。这样呢,大家就看的比较清晰了。好,打个空格,我们再重新来玩一把。好,那我先把这个关闭。这边关闭,把服务器端也关闭,我们再来看一下。运行。服务器端my server。就是这已经出现了我们的拆包拆包问题,对不对,好。好的再来运行客户端。再来运营客户端。那这次我们看。大家看这一次的确只有一次,是不是这边呢,他在接收的时候也是按照。
22:00
一次性的把十个。跟十个字符串一次性接收,我们再来运行一个。对。我们看这一次,我们看这一次呢,同学们看它是不是这是一个呀。这是一个,这又是一个,这又是一个,下面又是一个,这个显然不是一次了,那我们看这边是什么呢?同学们可以从这看到,这次按照七个包。就是服务器端是按照七次来接收我们发送的十个数据包的。那现在呢,我们再来运行一次,各位朋友。再运行一次,可能又发生变化了。哎,你看再看伏极端,伏极端这一次又是几按几次来接收的呢。一个两个三个四个。五个六个七个,这次还是七个,但是显然跟上次这个七个又有些变化,你看上一次你在接受第七次接收的时候,是把HELLO8和HELLO9拼接在一起接收的,但是这一次虽然也是按七次呢,但是它是。
23:07
只收了一个hello酒,所以说这个程序就把我们这个TCP可能出现的粘包和拆包问题解释清楚了。大家看是不是就很好理解了,原因就是我们刚才讲的这个原因,回到这边来看。由于服务器一次性读取到的字节数是不确定的,故而造成各种可能。明白这个意思,那当然如果这样去做的话呢,我们的代码其实是有风险的,假如我们服务器端拿到这个数据以后。拿到这个message以后,他要处理,他怎么知道?他怎么知道这个message是个完整的、独立的一个数据包呢?他无法知晓。因此,如果你拿到这个message去显示给你的用户,可能信息就不完整。对不对,也有可能你在处理数据的时候,整个整个流程都会导致错误。
24:01
所以摆在我们面前的问题就是怎么去解决TCP粘包和拆包的问题,那么具体解决方案我们放在下个视频为大进行讲解。
我来说两句