00:02
Zero与您拷贝的案例。嗯,我们举一个例子来。给大家说一下在NIO里面咱们怎么去使用零拷贝,实际上它提供了一个方法。那么在IO里边呢,它零拷贝的方式是用个transfer to,诶这个transfer to呢,就可以传一个大文件,对你找一个大文件可以进行一个测试,那么为了能够看到我们ZERO0拷贝跟我们传统IO方法传递文件的区别呢,我们把这个传统的IO传递文件的方式先写一下,呃,因为传统IO并不是我们的特点,所以说我直接呢已经把案例准备好了,咱们拿过来用一下就可以好吧。在这边呢,对。在这边有资料,资料有两个文件,我把这两个文件拷贝到我们的项目里边去就可以了,新建。一个包,叫zero。
01:00
枯否?然后我把刚才这两个文件呢拷贝过来。同学们看,现在呢,这有个server这段代码,我相信同学们都是没有什么问题的。Socket在7001上进行啊,进行一个监听accept,等待客户端连接,连接如果成功了,过后呢,我们通过socket对得到一个input stream流,然后呢,返回,最终我们构建了一个data input stream。然后这边创建一个什么呀,Bit数组,然后while循环进行一个读取,如果在读的过程中我们发现呢。整个过程啊,如果说等于一个负一,那不用多说,直接就break啊,就没读到,没读到你就就break就完事了,好,这是传统的传统IO的服务器。Java IO。的服务器端。
02:00
A的。服务器。那客户端怎么样子的呢?来看一下客户端呢,是要写的我们。Localho,当然你也可以写127.0.0.17001,我们待会传的一个文件呢,是个Z文件哈,然后同样先。跟这个文件关联一个input流,然后呢,在。再得到一个通过这个socket,对吧,因为这边拿到socket,通过这个socket呢,去创建一个data output stream同样也有一个字节数组。下面呢,一样的,最后在这里我们先打印出在。发送,因为我在这个过程中是要把客户端的数据,就大家可以看到。现在这边是我们的客户端。我们把这些关闭一下。把不要的关闭下,太多了。我们把客户端的发送数据之前呢时间拿到毫秒。
03:03
十大time这边有个循环,对循环不停的去读啊读,读完了功能就write write出去,因为你这个that out put已经跟这个socket进行关联了,所以说通过我们socket就写到这个buffer里面去,对吧,但是这个buffer呢,并不是我们这个IO里面的buffer,就是一个直接数组而已。最后呢,放完过后我们怎么办啊,统计一下放松的字节数。还有我们花费的时间,花费时间就是什么呀,就是现在的时间。就是发送结束的时间减去开始的时间,最后关闭相关的流。好,现在呢,我们把这个文件拷贝过来,在资料这边呢,我们有一个文件哈,文件不是很大,不到一兆,呃,当然你你愿意的话,也可以找一个大一点的大文件,现在为什么我不用,待会我说为什么原因哈,待会再说。先把这里拷贝过来。拷贝过来呢,我们来。
04:00
跟大家来测试一下,发送这么一个文件,在传统IO的情况下,要花费多少时间运行一把?好的。愿意起来。过后呢,我们。运行客户端。运营客户端,我们可以看到发送数据要60毫秒。字节数这么多,看字节数对不对。字节数呢,跟我们这个文件的大小是否能够保持一致哈,看一下。的确就是这么多字节,对不对,1007473。1007473没问题,那现在呢,我们来写用什么呀,用我们的。ZNIO的零拷贝怎么去进行一个传输,来写一个这段代码,我们写一下。好,六,我们叫六。I or?
05:03
好,我们现在开始编写这段代码。写上我们的命方法,然后前面这步骤都大同小异。六一个in net circuit。对,然后呢,我们open,呃,我们先指定一个端口吧,比如说我们是七。7001。得到一个地址address。拿到这个钻石过后呢,下一步怎么办呢?通过12。SSU。Channel。Open一个。对,Open一个,我们得到了server so China。就server拿到了。这个时候会有异常,这次呢,我们简单处理一下数。扔出去。接着继续往下写,这里我就不写注释了,同学们下一步该干什么呢?OK,下一步我们得到一个。
06:03
Serverck得到一个server很简单哈,那就是server,点什么呢?CK。拿到一个server socket。拿到这个server以后,我们进行一个地址的绑定,没问题吧,点B。B、我们的前面重建的地址。好创建好了过后呢,我们创建一个buffer。创建一个buffer。创建buffer的形式,跟前面一样,Bit buffer。点什么lo?大小呢,咱们就4096啊,当然你也可以设置一个自己的。拿到一个buffer。Buffer,我们就bad buffer吧,拿到bad buffer以后是不是就Y循环可以读取了true?好的true,那这个true呢,我们就循环的在这里进行一个监听server。
07:02
Shocked China except。对。拿到一个什么呢?如果说这个返回的话,就得到跟客户端对应的shaet China没问题吧,Short,呃,Sha China拿到S过后呢,我们下一步设置一个读取的。一个int值,看看我们能读取多少个Y循环,在这里循环读取。循环读取Y循环哈处,嗯,大体循环呢,我们就不要写错了,就是一负一,如果说。如果我们返回到这个值read,看呢,它不等于负一,我们就可以反复的去读,那这边我们来个TRY1句。Catch啊,有可能有异常发生。有异常的话呢,我们捕获一下ex。怎么写呢?这样写就可以了。我们在这里通过socket channel。
08:01
So channel.read对,从这个channel里面我们读取数据到bed buffer。对吧,当然他这个时候呢,会返回他读取到的数量。如果说我们在下一次判断,如果这个地方它等于负一,我们就怎么样啊,就退出这个Y循环,如果发生了异常怎么办呢?咱们。就输出这个异常就可以了。下一步在整个这块循环,呃,这个TRY做完了以后,Try catch做完以后,我们还有一个动作,就是因为你buffer,呃读了一次过后,你是不是下次还要用啊,所以说呢,我们要怎么样啊,我们要调用这个方法,将我们这个buffer进行一个什么呢?进行一个倒带啊这这样写的。点raven。这个re,这个单词呢,就是倒带的意思。倒带,倒带是什么意思?倒带就这个意思,倒带就是说我们相当于让这个buffer的那一个什么呢?Position。
09:07
Po。Position变成这个零,OK,然后呢,Mark这个标志作废。作废,就这意思,同学们呢,可以看一下这个方法。哎,看大家写的很清晰啊,Position等于零,Mark等负一,也就是说。倒带这个buffer,就让这个buffer跟the position is set to zero and the mark mark标志意思,第四第4CUT。就是作废了,就相当于说下次呢,我可以继续再用这个buffer啊bad buffer进行一个呃读取,就是把把我的so呢干什么呢,读取到这个buffer,那不是我们就得到这个数据了吗。下面我就不处理了,因为我主要是看网络整个这个传输的过程怎么样,好,下面代码我就不写了。这就是我们一个服务器端。服务器。
10:00
因为我这里只是在去跟我们客户端一样嘛,客户端也是是不是其实就是把这个数据读了一下就完事了,我们这里呢,也是按这个服务器看看从客户端读取到数据花就是在这个网络传过来的时候,我们花了多少时间,就是看这个东西。好,现在呢,我们再写对应的客户端。客端,客户端我们取个名字给它对应叫尿。6IO。CLA。好,那6L6LC呢?下面我们就跟着刚才的思路来写就行了。主方法写好,写好过后是不是一样的道理啊,首先我们用这个socket。点open。对,拿到一个什么呢。啊,我们都拿到一个socket channel。对,这边同样也会有异常发生,漏掉。
11:00
接着继续往下写。拿到这个以后,下一步我们干什么呢?好,我们就要进行一个可以进行一个连接。点。对,直接溜一个。In net shed address。写上我们要连接的地址,就是要连接的服务器local host端口7001啊,7001没问题,下面呢,再接着我们要传输的文件是哪一个,比如说my name。等于待会儿我们要传送的文件。我已经。拷贝到这里了,对不对,所以说呢,来我把这个文件名字用一下。拷贝。点zip。好的,拿到这个文件名字以后,下一步呢,干什么我们得到。我们就得到一个文件的channel,没问题吧,很简单,六一个fire。
12:00
发什么呢?Input stream。然后这边咱们就写上fair,嗯,这问写fair。对。呃,我们应该是六一个这样东西,然后再点get channel啊,这样就对了。这样我们就得到一个文件channel。是不是文件channel,得到这个文件channel以后呢,我们就开始在这里先记录,就准备发送了啊,准备准备发送。那么准备发送,发送之前呢,我们把这个时间记录下来。怎么记住这个时间啊,用system。system.current当前的这个毫秒拿到。我们取个名字叫starter。太猛。就是开始时间。对。呃,开始时间呢,现在呢,我们就用于在linu,注意听这里啊,在Linux下,Linux下一个函数一个什么呢?一个transfer函数。
13:06
Transfer to函数。函数就方法啊,教方法比较好,方法就可以就可以完成传输。但是呢,我说一下,在linu在Windows下,在Windows下呃它呃最多就是一个transfer to调用一次呢,最多只能发送八兆的文件。一次调用。啊,一次。调用。调用调用。调用我们transfer to呢,只能发送八兆的文件。只能发送八兆文件。对,那意思就是说,如果你这个在window在Li下面呢,你一个传two就搞定,如果你是在window下面呢,你要每一次只能发送八兆,所以说你要分段就。需要分段。
14:01
分段传输文件。啊文件,而且而且而且要注意要注意呃,这个起点传送的传输时,传输时的这个位置。好的,那呃,那现在呢,我们先因为这是传了一个小文件嘛,如果是大文件的话,你要进行这个分段,好其实也挺简单的,就是用一个你把这个文件的大小算出来,除以这个八兆,你就知道应该调用几次了,然后每次把它的传输,传输到的那个位置记录下来,再下一次再从这个位置再传输。相应的这个数据就完事了,那我这先调一下啊,叫发channel.transfer to。大家看这里边呢。Position就是,呃,你是从哪里开始填,第一次肯定是从零嘛。啊,我要传多大呢,这次因为我现在这个文件不到八兆,所以说我直接这样就完事了。
15:02
对不对,因为这个fire channel里面已经跟这个文件关联起来了,然后下面呢,怎么办,我们往这个socket channel里面放。是不是就是这三个参数不要写错了,这个transfer to底层就用了零拷贝。川式ver to底层使用到零拷贝。您拷贝。好的啊,这边我们先拿下来,最后这边会返回一个值,返回什么呢?返回浪,返回浪呢,我们把它取个名字叫做transfer count。好,把这个名字改一下,Transfer就是我传递了多少个。传递了多少个过后呢?我在这里输出相应的信息,我写这样一句话啊。呃,发送。发送的总的。总的。总的字节数等于。等于多少呢?就等于刚才我们统计出来的transfer to count。
16:03
对,再加上再加上一个什么呢,耗时。因为我们主要是看花费了多少时间,对不对,时间当然很好,算了,就用我们当前这个时间。当前这个时间减去OK,减去我们开始的时间完事毫秒,最后我们关闭这个通道。关闭通道,好的,Fair close,我再说一遍。就是说如果在Windows,在Linux下面呢,这一个文件就是transfer to啊,就可以一次性的把这个文件传过去,如果在window下面调用呢,因为它一次只能发送八兆数据,所以说这个传输to呢,你要用一个分段方式,怎么分段呢?很简单,你把这个文件的大小,这个文件大小吗。算计出来。除以。八兆对应的字节,那就八乘以1024,再乘以1024,就知道应该发多少次了,每发一次呃你就呃你做一个循环嘛,做一个循环,然后每一次呢,把这个零改成相应的这个这个地方当然就是你要发送到大小。
17:08
好吧,这个其实挺简单的,自己去做一下就行了。那现在呢,我们先来测试一下,在这样一个情况下,如果我们用这个。就是。我们叫做NIO的方式来进行这个传送会怎么样?那这里面的重点其实就是transfer to了,我们看这个transfer to它是怎么说的方法。同学们看这个transfer to呢,它在这里有几句话特别的关键,我们来看一下。这上面啊,看这。OK,他说这个方法呢,呃,是一个更高效率的,比普通的一个loop。而他为什么效率比较高呢?看这里。看这里有一个特别重要的东西在这里哈。好,他说很多的操作系统。
18:00
很多的操作系统可以传,传递字节直接的啊,Direct直接from,从这个操作,从这我们这个文件系统进行这个传输,传输呃到哪里去呢?从这个文件系统的缓存到目标channel。A,目标channel就是从我们这个文件系统到我们的目标这个channel去。Without就是没有实际的copy,他们就是没有实际的拷贝,换言之就是有点类似于我们现在说的零拷贝,他只是用的DD ma。直接内存拷贝。啊,到了一次,到了我们这个。这个缓冲可以叫我们channel到了这个channel过后呢,直接就对应到下一个就是从这边再来一次d ma。好到哪里去呢?到我们对方的相应的文件去,中间呢没有经过拷贝,所以它速度呢会更快一点,就这句话是比较重要的,Without。
19:01
Actually actually就是没有实际的copy他们,没有实际copy他们好,这句话呢,大家要。重视一下,好,现在呢,我们来试一下,呃,它的速度到底大概是呃到底是多快,我们原先这个耗时是60毫秒,对吧,现在呢,我关闭。关闭过后,我们启动呃,当前的这个新的呃server。运行。运营起来。好的。那运行起来以后呢,我们再运行6IO client。好,我们看一下这次时间多长。好,48。我们再运行一次。早。好,这边我们有一个小问题啊,小问题是哪里出了问题,我们找一下。
20:02
这个new server。6l server呢,这边地方我们就如果发生异常,咱们就直接break,让他进行下一次的处理就可以了。好,我们再来运行一下。跑起来。好,再来看一下时间哈,走。好47对吧。再来一次,26。再来一次。28,好,再来一次。再来一次好,这次呢,二十一时间呢,还是应该说比原先要。快了很多,主要是因为我这个文件呢,并不是特别的大,并不是特别大,如果这个文件呢,它更大一点,就能把这个时间看的更加明显,至少从这看,你原先是大概是60毫秒,这边呢平均大概是20毫秒,是不是速度还是快了很多。那同学们有兴趣,你可以再去进行一个调整,那这里我再多说一句,我们这个代码呢,呃,还有在就是整个这个传递是代码是没什么问题的,如果说在Linux下面呢,这个用这一个方法调用是OK的。
21:12
用这个方法调用是OK的,Twenty two是OK的,但是在Windows下面呢,如果文件大了,就超过了八兆,它最多只能传传八兆过去就会出问题,所以说你每次发现他最多传递的这个字节数并不是我们这个文件的全部,那怎么办呢?就要分段传输,分段传输这个问这一点啊,我教给同学们做一个课后思考。就是你们做这个客户思好。可后。课后思考。把这个做一下就行了,非常的简单,提示大家一个思路,把文件的总的大小拿到除以八兆对应的字节数。就知道一共要。传递多少次,但是你这个呃,传下来过后呢,你要多一次哈,就是因为比如说你除下来是个小数是1.51.2。
22:03
那1.2你不能取一个整,你说穿一次就行了,不是你要穿两次,明白吧。所以相当于相当于是向上矩阵的,就是说呃,只要有小数点呢,我就要多加一次,或者说你出现是个整数,你加一个一。但是呢,如果刚好是一个整数,你就不要加了,哦,就不要加了,这个地方你们做一个小算法就行了,然后还要考虑的是每次传输的时候呢,不能每次都是零了,哎,为什么呢?因为position代表是从哪里开始传,传多少个。对,这样子就可以解决问题,好,这个题呢,同学们自己做一下,并不是很难。好,那关于呃,我们所说的这个零拷贝呃的一个案例就说到这里。
我来说两句