00:01
TCB粘包和拆包解决方案。那么在我们实际的开发中,我们通常使用自定义协议再加编码器和解码器来解决。那我们现在呢,通过一个案例来说明它的使用,比如说。我们待会儿呢,准备发五个message对象。就服客户端发五个message对象,客户端呢,每次发送一个message对象,就是我发五次,服务器端呢,每接收到一个message对象,分五次进行解码。每读取到一个message对象呢,就回复一个message对象给客户端,这里面的关键是在什么地方,同学们。这里边儿的关键是在于。就是要解决服务器端每次读取数据长度的问题,这个问题解决了,就不再会出现服务器多读或者少读数据的问题,从而避免了TCP粘包和拆包的问题,是不是因此呢?这地方肯定要做一个自定义协议的。
01:09
就是你要规定你每次发送去的这个数据包,它的大小是多大。然后呢,在服务器这端呢,它通过这一个什么呀,通过这一个长度来进行读取,同时呢,这里面还涉及到一个编解码器的问题,因为我们。这个数据在发送的时候。它是需要按照我们规定的这个协议来进行发送的,所以说它仍然存在编码和解码问题,那现在呢,我们就直接按照刚才说的这个案例来给同学们解释一下。大致的一个示示意示意起示意图是这样子的,客户端这边呢,发一个两个三个四个五个message对象给我们的服务器端,服务器这端呢,不允许把它服务器端就不能说,诶我把M1和M2合起来进行。
02:04
读取,或者说我读M1的一部分,再读M2的一部分,不允许这么干了,怎么办呢?我每次在读取的时候就按顺序来读。比如说我先读到。M一再读M2,再读M3,再读M4,再读M5。明白,然后我我到时间呢,每读一个我还可以回复。自己的一个message给客户端来进行处理,那当然了,这两次接收到的message和回复的message可以不一样,待会儿呢,我们在这个message回复的呢,还是按照以前的规则回复一个随机数就可以了。大致的思路大家明白了,那现在呢,我们走代码。我们先走哪一段代码呢?我们先完成从客户端发送五个消息给服务器端。把这条线走完再走,我们的这条线分成两个部分来完成好,首先呢,我们来解决自定义协议的问题,打开我们的idea,我们现在呢来开始编写。
03:08
新各位,我们新建一个包,OK,新建一个包,取个名字吧,叫做pro口什么呢?TCP。叫协议啊,基于协议的TCB,那首先呢,我们先建一个message,我们叫message pro,就是我按这个协议来规定的,我取个名字叫做message protoc,没问题吧。好的。写进去,那待会我怎么去定义这个要发送的发送的消息呢?我这样来定义,首先我先定义它的一个长度叫嫩。没问题,然后呢,我在定义它要发送的数据content,那一般来讲我们数据呢,都是放在字节数组里面的,所以说我这就这么去写,然后给它生成set和get,方法非常简单。
04:03
Set get方法,我们把C和get方法都给它生成了。没问题,好的,这第一个就是我们的协议,我叫协议包。协议包。协议协议包,当然了,同学们,这里面除了带这些信息,是不是还可以带别的信息啊,我这里只是带了一个它的长度和他一个那但但这个长度是最很关键的,这是个关键,因为后边我们服务器端在读取每一个协议包的时候呢,是按这个长度来进行读取的,能理解。好的,那这个明白了过后,下面我们紧接着是不是要来编写,按照这个示意图编写我们的客户端和服务器端,那客户端和服务器端基于来呢,我们已经写过很多遍了,老师就不再重复,我把前面写的这段代码拿来为我所用,这个能理解吗?我把这个拿过来肯定要改吗?改肯定是要改的,但是如果重新写是不是太麻烦了呀,我把这个整个复制过来跟上老师试了。
05:06
过来。好的,那复制过来过后呢,嗯,这边我我们先从客户端这边改起好不好,我们先从客户端这边改起,呃,这块就不用改了。先看一下引入的东西是没有引错啊,千万不要引错啊,没有引错那这边呢,不用改它,到时候我们仍然找7000端口来经营。进行一个测试。再找他的。这一个calendar initialize这个地方肯定会有变化,对不对,肯定会有变化,我们先写哪一个呢?同学们,我们先写它的my c handler来吧,这个写完再一步步的走,显然这时我们再进行处理的时候,大家想一想,还是按照我们的bit buffer来发送吗?显然不是了,是不是应该改成message了?因为我们按照我们定义的这个协议包来处理,能理解不好,显然这样一处理过后,呃,这个read的零肯定也不一样了。这个我们就。
06:06
不要了哈,这边我们就直接关闭这个异常,我们也不看了,要看异常也可以这样打。简单一点,这样输出异常信息。System,然后呢,我们说。异常信息。消息等于加上。点get message好吧,这样看就行了,不要打出一堆那个红色的,看起来难看。这是我们的对异常的一个处理,打出消息过信,异常信息过后呢,关闭即可。我们现在先走第一条线,显然这边呢,他报了一个错什么呀。他说这个China read0必须实现对吧?那没办法,我们还得把它打开,打开过后呢,肯定这边要变化了,来同学们要把它换成它。这个能理解,那现在因为我还没有涉及到客户端的读取,我先把这段代码怎么样注销,这个能理解不?
07:02
注销好,现在我关键要写哪一块呢?要写的是China active。Active,好,现在呢,我们来开始在这里发送数据,我们hello service型,Hello,别的型,比如说我们这次用中文的发送十条数据。天气冷,吃火锅啊,今天。今天。今天天气冷。天气冷。哦,吃火锅。好的,那我现在就准备来发送十条数据,跟上老师思路for循环n ti等于零,I小于嘛呢小于十。对,哀家家。对不对,I加加走起来,那这边我们首先把这个字符串先做好,比如说我有个string,对不对,Message。比如说,我们要写message to。
08:02
呃,Sent叫做message就行了,Message等于什么呢?没写刚才这句话,天,今天天气冷,是我过。多吃几顿。放到这里。放到这里过后呢,是不是我们要把它做成一个什么样字节数组,这个能理解,那下面呢,我们就这样写就可以了,呃,就用message点什么呢?Get bits。Get bit,对,Get bit呢,我们把它的字符串,呃,这个编码方式写进来,Char set点。还是统一按UTF杠八来处理,能理解拿到了。拿到勾呢,这边就拿到,拿到我们的byte数组。这期就是我们的内容,这个能理解吧,就是我们发送的内容,其实就是今天天气冷,锁国。然后呢,我们要统计出当前它的长度,就MS。点get bit。
09:01
Guide bit。好,Get词呢,我们仍然是要指定按照这种编码的方式来统计它的长度。点什么呢,认识。拿到认识。好,这个认识呢,我们也拿到了。没问题,所以大家看有没有问题,就是我先把它编码成一个BAT数组。然后呢,再统计出当前我们这个内容的长度,因为后面要用嘛,那这个时候呢,我们就可以来创建什么呀,创建一个协议包。创建协议包。OK,对象。非常的简单。六一个message。我们是Portugal,对。返回message port。对,然后呢,下边我们是不是要设置相关的数据了,首先设置它的长度。它的长度刚才已经统计出来,就是嫩。再把我们刚才做好的内容给它设置进去,哪一个呀,就是我们content能理解。
10:05
做好以后通过cx.andsh给他发过去。好的。各位,那么我们在进行这个发送的时候呢,一般来讲我们会进行一个编码的处理。对吧,进行一个编码的处理,所以说呢,我要做这样一个处理形式形式,因为现在你这是个对象嘛,我要把它进行一个处理,编码形式处理来我这里需要再增加一个nco,没问题吧,我写一个名字叫my message。好的,Encoder。跟着老师思路啊。显然,我在这里首先要去继承message。MESAGE。这个是不是前面已经说过了,显然这时候我们都同,这个时候他拿到的信息是message prooc。
11:00
同样我要去实现其中的一个方法,哪一个方法N扣的这个方法,那么这个NQ的方法呢,为了方便呢,我这边写一句话,就是说某某方法不一调用,咱们这样写哈,就它的N扣的方法被调用。待会儿呢,我们可以看到被调用几次,被调用你没问题。那么调用的勾呢?我们现在要out.right in。对,首先呢,我们把什么呀,把它这个int拿到就长度拿到MSG.get。长度拿到发出去,然后再write by,按照字节的方式把我们这个字节数组。也通过。我们这个网络发送到我们的客户端,那就是write什么呢?Writes bits,这个能理解好。现在我们通过MT。点get。Content发出去了。好,同学们,这一块就写完,那显然这块写完过后,是不是在我们的initial international这个地方,我们除了有一个my my client handle,是不是还要加一个处理器啦,这个能理解吧,就是哪一个呢,刚刚写的my message。
12:15
Co。媒体把这个地方大家应该能想明白,也就是说我们在发送的时候呢,还要做这个编码的处理。购物端这块呢,基本就完事了。客户端这边就完事了,就说再捋一捋思路,好,这边发数据,发送的数据先给到我们哪一个呢?我们的这个nco进行一个。编码对,编码完了过后,把这个数据通过我们的这一个网络发给我们的客户端。没问题吧,那这个我已经加进去了,呃,这个这句话不要忘了哈,这句话很重要,不要忘了加入加入我们的编码器。编码器,那现在呢,就回到我们服务器端了,那服务器端一样的道理,你首先要进行一个解码,这个没问题吧,同学们,所以说我在这边呢,还得有一个MY什么呀,My message。
13:12
Message,你有n code,我就有d code,是不是前面已经讲过这个东西了?那现在呢,我们来编写一下它的这一个decoder decoder呢,仍然是继承我们的,这次我就简单一点,直接用我们reply是不是讲过了decoder。Reply,那写上一个void。同样,我们要去实现其中的一个方法。对不对,哪个方法D扣的方法解码。那进行节目的时候呢,我为了方便,我也提示一句话就说。My message。Decode方法被调用。对吧,这个被调用。跟上思路啊,被调用OK。调用好,下面接着写,那你这个时候拿到是什么?其实这个时候他拿到的本身是以字节是不是字节,你要把它怎么样,这里需要将字节将获取到的将。
14:14
得到的得到的二进制,二进制字节码。字解码转成嘛呢,转成我们的message。因为你按这个来说的话,就是messages message pro数据包。能理解吧,就数据包。其实就处对象了。其实就是一个对象。好的。那这是一个对象的话呢,我们来下一步哈,继续。拿到数据对象,那就需要我们来进行一个什么呀,进行所谓的解码,那下面首先我第一个先要读到哪个呢?读到程度是不是read一个int,因为这个呢,我知道你第一个发了个嘛,所以我拿到一个int,拿到了int呢,我们就取个名字叫做N吧。
15:08
Ang th,别写错了,认识拿到那识以后,是不是通过这一个长度我们再去获取,获取对应的字节数组的一个啊,一个就是对按照这个认识长度来得到一个字节数组,是不是啊,那就六一个bit字节数组,那多大呢?认识。V2。这个世界数组我们就拿到了,就叫content,其实它对应就是我们的content,拿到这个content以后,是不是就从我们这一个bad buffer里面去读取,这个没问题吧,同学们,因为你现在就是数据放到这个缓冲里面了嘛,我就读取in.read bits。Read bits放到我们刚刚做好的content里面去,OK,拿到了吧,拿到拿到以后下一步是不是就封装成,封装成嘛呢?封装成这么一个message对象。
16:06
对象,然后再传放入到哪里呢?诶,再放入到。放入到我们这个list中。传给下一个handle的处理是不是这样子的?不是已已经响过了吧?放入到out里面,然后传底,传给下一个handle进行业务处理。业务处理。OK,那非常的简单。六,一个message protocol。拿到。一个名称。的,那这边呢,我们就把相应的数据给他不就完了吗?Message set。下载谁呀,长度,这个长度是不是刚才已经拿到了。对,然后message pro.set content问题就是一个拆包解包的问题,拿到它过后,把刚才读到的内容扔给我们这一个message对象。
17:01
下一个是不是要放入到al传递给下一个handle进行业务处理了,那就应该是out点艾什么呀。Protocol。那同学们想一想,这个my message deco是不是应该加入到我们my server的这一个拍online里面去啊?这是肯定的嘛,Last有另一个my message。这个词decoder,别写错了。好。这个是什么呀?这是解码器。解码器。实际上它也是一个handler。下面呢,我们就要开始写这边的my server,这是处理业务的。处理业务的。Handler。好的,那现在这个地方我们应该改成什么了?肯定是message了,人家都已经给你封装好了,你看你在前面的这一个解码器,已经把我们的数据在这个地方。
18:10
在哪里能在我们的my message?Decoder,看这地方人家已经把数据给你解码成一个message pro对象了,因此你这个serve呢,显然就应该按照message prooc来处理,否则的话他直接给你跳过去就麻烦了,对不对?好,下面我们继续来玩,那这个地方是不是你已然把这个类型改一下。没问题吧,同学们。因为你是按这个方式来给你传递的嘛,然后过后这边数据我们是不是要做相应的改动了,来重新写一遍。接收。接收到数据并处理,那就跟上老师的思路,下面呢,我们就来编写我们server handler相应的内容。首先第一步我们先拿到它的长度,是不是因为这边已经给你把这个对象传过来了,所以说先把长度拿到。
19:06
认识。长度拿到了,然后呢,再根据这个长度,长度我们再获取相应的其他信息,但是点get content把内容也拿到,这时候这个时候是。Bit数组就是我们的字节数组content。下面继续走,我就提示一些信息了,那我们可以看一下,就是说服务端接收到接收。接收到信息如下,我们给同学们打出来看一下哪些信息呢?首先长度我可以输出一下,对不对,我把长度输出来。长度等于,显然就是我们这拿到的认识。我们可以看一下客端发送的和我们接收到是否是否一样好内容呢,我也可以可以给他打出来内容,显然内容我们这个地方拿到的是字节数组,是不是应该转成字符串啊六。
20:03
要对的,然后呢,Content按照我们规定好的。编码方式来进行获取,就筛char是不是点name,我们是按照什么编码呢?UTF杠八没毛病。这个信息就拿到了,就拿到过后,下面呢,就是我们还可以来输出,就是我是按多少次来接收的,就是我们规定的是根据刚才我的思路是我你这边发多少个。Me,像我我按照这个顺序一次只收一个,我来验证一下我是不是一共收了多少次哈,就是这写个服务器。服服务器接收到,接收到消息。消息。消息。
21:00
消息或者叫协议包都行,消息包吧,消息包数量。数量。那这个时候呢,我们是不是最好也做一个啊,这个content还在这,那就直接用了。加下就行了,this.com。然后前面来个加加即可。可以了。现在我还没有回送啊,同学们,我现在还没有回送,一会儿呢,我们再回复消息,回复消息。就写完了,这篇代码也就写完了啊,我们看到server这个server呢,我们也把解码器加进去了,其实现在就已经可以测试了,已经可以测试了,对吧,已经可测,我们再检查一下。这些都没毛病啊,都没有毛病呢,也没问题,现在呢,我们把这个改一下,就是刚才我们案例要求是发五个message,那么我在客户端这边呢,把它改一下,改成五。
22:01
这边我们改成五就可以了,火锅也不能吃的太多。吃十次这个要拉肚子的,所以说我们吃五次就可以了,来关闭。原先的这些信息关。好朋友们。朋友们。现在我们来启动一下。就是用协议加编辑码器来解决TCP粘包和拆包问题的案例,来运行一下,看看现在是不是已经可以解决这个问题了。我们现在解决的是从客户端向服务器端,好像不能不能从这启动啊。这边因为名字都一样,所以说得。考虑好。好,我们把这个先关一下。这是以前启动的嘛。对吧。好,同学们,我们启动client端,看看是否已经OK。好的,诶同学们看我们客户端是不是N扣的方法调用了五次,显然是五次,你发了五次,它调用五次不是很正常吗?看我们的腹肌端,诶同学们看。
23:10
是不是?Messagecode被调用,收到的是27,今天天气冷,吃火锅吃了,呃,接收到一个message消息包,这就是第二个消息包,内容是今天天气冷,搜狗第三次是不是又拿到了?第三次又拿到了,第四次又拿到,第五次又拿到,每来一次咱们进行一个解码,说这个这个是很稳定的,我编码了五次,给你发了五五个包,你按五次接收的,那有些同学,有些同学老师你多来几次,多来几次也没问题。我再来一次,够端。是吧,我再来一次。我们运行四个客户端应该就可以说明问题了,没问题吧,同学们来跑一个。各位同学可以看到,现在呢,我一共运行了四次客户端。
24:01
看这边。我们定下。是不是又来一个呀?所以这这又是一次。今天吃火锅一个。到这五个结束,五个结束以后是不是又来下面的。一组。下面是不是又来一组?是不是下面又来一组看一。2345,如果说要看的更清晰一点呢,我们可以在服务器端这边这样处理一下,看的更清晰。怎么处理呢?在handler这边打信息的时候。我们来几个换行吧。就来几个换行。复制一下。这样看着就比较清晰了。好,这次我们再看一下好吧。关闭一下。这边我就通通的一这样关闭快一点,一次性全部关闭。再来。跑起来。就是打一个换行符,这样子大家就可以看的更清晰,走一个客户端。
25:06
两个客户端。三个客户端。四个口端可以了,足以说明问题,足以说明问题,看这边。这样应该看的比较清晰了,哎哟,这个还还不行,因为我是那样子做的话。还不行,因为他每次都掉一次哈。掉一次。看,反正应该是看的很细,看一。二。三。451次是吧,然后这又是下一个客户端来了。又是一二。三。是。对不对,下次又来了,又来看一。二。三。是。所以它是非常稳定的,下面要一共有四次吗?12345。所以说从这地方我们可以看出来呢,就是没有出现这种所谓的。粘包和拆包问题,大家看是不是这样子的呀,原因,其根本的原因就是我们解决了这样一个问题,什么呢?就是解决了服务器端每次在读取数据程度的问题,这个问题得到解决,就不会再出现前面所说的粘包和拆包问题,大家再体验一下,那待会儿呢,我们再紧接着完成从服务器端回送客户端。
26:22
这样一个问题。
我来说两句