00:00
同学们,我们现在呢,就用代码的形式来把前面画的这个图做一个解读,对,就从代码的角度再来理解一下这个图就会很清晰了。那我们来看这个案例的要求是什么?NIO非阻塞网络编程的快速入门要求是这样子的,编写一个IO入门案例,实现服务器端和客户端之间的数据简单通讯,非阻塞。目的理解IO非阻塞网络编程的机制,然后呢,我们就直接走代码了,打开这里,嗯。我们先写第一,我们先写服务器端,再写客户端。来,走一个,我们叫IO server。好,跟着老师思路哈,走起来。主把写上,根据前面我们讲解的道理,是不是首先我们要去在服务器端拿到,诶是不是这个图啊。是不是我们首先在服务器端去做一个server socket channel过来好。
01:05
开始。在这边哈,创建,我这边一边写一边做些注释。创建什么呢?创建server socket channel。哦,这个你可以等价于,就是有点类似于我们原先Java IO的server circuit。类似哈,那我们就写一个,怎么写呢,非常简单,就server socket channel。Server socket channel.open。VR拿到一个值,我们就叫server得到了,因为这边有异常,我们就简单处理一下,So。后面我们会再说,呃,在异常的时候还有什么处理方案啊,现在先简单处理一下第二步。第二步,根据这边是不是我们应该创建一个或者得到一个select。对象,也就是说现在呢,我们要得到的就是这边画的select实力,因为这个东西很有用嘛,那现在我们把它重建起来,怎么得到呢?是不是前面讲过通过select。
02:15
这类的一个静态方法叫open,就可以拿到了。好,我们拿到一个a select吧,取个名字还叫select比较好。拿到了。好,拿到这个select对象以后,下一步我们该干什么呢?好绑定端口。Three。想啊,绑定绑定一个端口。啊比在呃,绑定端口666。666,我们就这样写到哪里呢?在服务器端,在服务器端接听。这样子。我们这时候所做的监听是指的让server so channel来监听,那怎么做呢?非常的简单,这样写就行了,Server socket channel.et拿到这个socket,再点band。
03:05
好,里面传输一个so地址socket address就可以了,六一个。Intersected China。这边。In net in net so address这边怎么填呢?直接填一个地址就行了,因为我们现在就在本地进行一个监听,所以说这边写的666。拿到以后下一步该干什么呢?我们有一个动作设置为非阻塞,设置为非主塞。非阻塞。那怎么写这句话呢?非常简单,还是用我们这个channel来做。对,那咱们这config。Confi blocking做成一个force,就是非阻塞模式。下一步该干什么事情呢?大家想一想,是不是老师讲过一件事情,还是到这个图,我们这个select呢,要注册这个通道,你本身同学们想一想,我们客户端,就是客户端本身发一个请求,要连接的请求是不是也要通过?
04:09
也要通过我们这个通道来做呀。那也就是说我们这个server so呢,也需要先注册到C上面去理解吧,也也就是说我们这边呃,只是老师画了一个client,其实本身本身这个serveret China呢,也要注册到我们select上面去再说一遍。因为你发你客户端发请求,始始终是要先通过一个呃通道发过来,然后我再给你创建一个跟跟你关联的,你自己去使用的一个通道,明白吧,好是这个这个这个道理,好,现在呢,我们再来做这件事情,讲什么呢?说把。把我们的server so China注册到。注册。注册。
05:01
注册到哪里呢?注册到我们这个S。而且他关心的事件是什么事情呢?他关心的事件是这个事件关心事件。事件。关心事件,哎,这个挺讨厌的。关心事件,为什么呢?就是我们所说的op_ACCEPT,就是呃,连接事件APT,对连接事件,那怎么做这个工作呢?非常简单,它里面有个方法,好,我们就选第一个方法怎么做,呃,首先把S放进去。再说我们关心的事件是哪一个呢?就selection。Selection,我看看是。沈。呃,应该是selection k点哦,找到它的一个属性就是except。
06:02
就OK了,拿到这个地方现在就可以来进行循环的监听,就是因为我可以支持多个线程嘛,就循环循环等待。等待客户端连接。OK,那就是一个Y循环来走处。包起来。那么你你在这进行循环的时候,应该怎么去做呢?是不是就应该像我们刚才所说的这个图例一样,进行一个select操作,我看看你有没有相关的事件来,我再做一个处理就可以了,所以说这边select呢,Select就会调用他的select select。方法。那我就开始写了,就怎么写。如果。select.select比如说我们在这里面呢,先注射。一秒钟,如果它返回的这个值是等于零,说明什么呢?说明现在还没有任何的事件在我们的这个通道上发生。
07:02
能理解吧,就现在呢,没,没有任何事件发生,就说没有,没有事件发生。那么没有事件发生呢?这边我们也就不傻等了,我写一句话。写句神话就说没就说这样写啊,就说可服务器,服务器等待了,等待了一秒。一秒,然后呢,我就不等了啊,无无连接无连接我就不等了,我就可以continue啊对。明白我意思吧,就是我可以这个时候我就不阻塞在这个S上去,因为我们不讲了吗?S,如果你写了1000秒,呃,一,1000毫秒,就代表我只等待一秒钟,我等待一秒钟,如果你没有什么事件发生,我就可以干别的事,我不阻塞在这里,明白吧,当然你也可以这样写,Second now,嗯,你可以这样写。三闹也可以好,那现在我们就还是等一秒钟。好的,Continue,继续往下走,这个我写一个注释啊,这里呢,我们等待。
08:05
等待一秒。等待一秒。如果呃一秒钟一秒后,呃,如果没有,没有事件,没有事件发生,比如说连接,这里当然主要指的是连接事件了。连接事件。OK,没有事件发生了,我就不干这个活了啊。当然,这个地方将来可能还会有别的事件,我就不要写这个连接事件了,如果没有事件发生,就返回。啊就继续啊,继续再来玩,好继这个做了过后,下面我们继续来写,嗯,那如果说他返回的同学们注意听,如果他返回的不是等于零,那肯定就大于零了,对吧,它一旦大于零了,我们就这样来操作了。这开始,呃。如果嗯嗯,应该怎么说这个事情呢?下面我们就这样子啊,如果返回的,如果返回的不是零啊,或者是大于零,大于零,我们就干什么呢,这样来操作select。
09:08
点。Selected。对,VAR VAR过后呢,我们返回的是一个集合,就是selection case集合,是不是说过这个事啊。那么我们就获取什么呢?就获取到。对,获取到。什么呢?相关的相关的这一个selection集合。集合。没问题吧,也是现在呢,我们就可以把这个集合拿到了。注意这个拿到的这个集合呢,呃,其实就是我们,呃,这个就是有事件发生的这个3K,注意听这句话啊,注意我们这解读一下。啊呃,就说如果他就说我们这如果返回的大于零。
10:03
如果返回大连表示。表示什么意思呢?表示已经已经获取到获取到关注的事件了。关注的事件是这样是吧,因为你如果没有关注的事件,它它它是等于零嘛,那如果说这个地方突破了,那肯定就大于大于零表示我已经获取到关注事件了第二点。第二点,第二点就是我们通过这个方法。诶通过这个方法,这个方法呢,呃,返回的是什么呢?返回的是关注。关注事件的集合。啊,这个集集合。好,现在我们可以在反向反过来再通过这个sal k呢,获取到相关的这个通道啊。啊,然后通过。通过这个呢,可以反向。啊,反向获取。
11:00
获取通道。获取这个通道。好,我们继续往下走。继续往下走,那拿到这个地方,显然你这个是个集合,是不是我要便利它呀。肯定我要便利我们这个集合。那怎么便利呢?我就用E使用迭代替。我使用迭代器病历,这点对我来说应该在Java基础里面就学过了,我就一步到位哈,大家同学们看一下怎么走。嗯。嗯,可以这样写,用它点。对。然后呢,我们返回一个。这样的一个E迭代器,那既然拿到底这个迭代器过后呢,我就Y循环在里面再去便利我们这个迭代器怎么组呢?好,这边叫K比较好。换个名字吧。K。将来我看起来就是ker啊。
12:01
放到这加现在呢,我们要next,如果还有的话呢,我就不停去看好,大家都知道我们这一个下。K呢,它其实是拿到了所有关注事件集合,那现在我们我们现在是不是目前对第一个动作而言,是不是应该应该是要分别处理,如果我拿到的是一个连接的事件,我就做连接处理,我拿到是读的事件,我就读,是不是这个道理啊,所以说呢,现在我们要做的工作就是来看看。来看看各个事件是什么。好获取。获取到。这一个小了。那怎么获取呢?因为你这个K。E已经拿到了,所以说我直接用NEX获取。他这个时候呢,就会返回,对它会返回一个select k,我们就叫K吧。对,这叫K或叫sal k都可以了。
13:03
那现在我拿到这个30K过后,我就要看你到底是,嗯,就是这个K关联的通道,它到底是发生什么事情,对不对,就是现在看看。看。根据这样说啊,根据这个K对应的通道,通道通道发生的发生的,诶这样写发生的事件。事件做什么呢?做不同的处理,或者叫做相应的处理。能理解,那现在我就来判断了,如果这个K它的事件是什么呢?Is except,他看这里面是不是有这几种。就是我们刚才写的那个on accept对应的就是这个。Connect,呃,Connect呢,就对对这个readable,这个able就这个,所以说呢,我们现在是这个,嗯,一是accept,也就说如果发生的事情是这个事件。
14:03
如果是哪个事件呢?就是刚才我们所说的。这个事情。对,那如果是这个事件,是不是就代表相当于说有一个客户端来连接我了呀,能理解吗?就是说相当于是什么呢?就说有客户端,有一个新的客户端,有新的客户。客户端连接我。那如果连接我根据我们前面的分析,是不是我应该产生一个新的一个通道,就是产生一个socket channel,然后呢,分配给这个客户端,是这意思吧,好的,那下面我们就应该干什么呢?生成。给要说给该客户端。客户端生成。一个什么呢,Soet China。China,对不对,这个没有问题吧,怎么生成呢,也是非常之简单的,我们这个时候呢,就使用这个地方来获取了,大家还记不记得我们这有一个server so。
15:04
点except来拿到一个,他看返回的是socket channel。点VAR,好,我们就拿到一个socket千人了。对这个这个呢,就是当前这个客户对应的双向,那有些同学就说了,诶老师不对啊,这个accept不是一个主车的吗。你们怎么理解?是不在我们Java IO里面这个accept它会阻塞,不要忘了一件事情呢,人家都已经告诉你了,经过你的这个便利,人家告诉你,人家就是要来连接人家连接都人家这个连接的行为都已经发生了,你还会在这等待吗?我们原先这个传统的传统的这一个server,他在这个的时候,他是并不知道下面这一个有没有连接来连接,他就在这傻傻的accept,而我们这个NIO,因为你这个事件驱动的,其实到这一步的时候,其实别人知道你已经有人连接,所以他马上就会执行。
16:04
是不是这个道理,所以它并不会在这阻塞,本身这个方法是阻塞的,但是呢,因为我们是事件驱动的,我们看我们就可以知道,这个时候的确已经有一个客户端来连接了,因此呢,这个方法就马上就会执行。明白吧,好的,我就说到这里啊,如果你不理解的话,你去看一下Java IO的东西。好的,那现在这个就拿到了,拿到是不是根据我们前面讲NIO的这个模型,是不是老师刚才这讲了我们得到这一个,嗯,烧过后呢,是不是又要把它注册到我们这个S上面去啊也也说这你可理解,这个通道也要注册到我们S上面去,没问题吧,同学们,那现在将什么呢?将当前的这个so China也注册到。注册到这个select上没问题吧,嗯,怎么注册呢?非常的简单,非常简单,前面我们已经用过这个东西了,就是socket turner。
17:01
点register,那这次这个register呢,我要换一个方法,我要填三个参数进去,第一个当然就是我们的select。就是我们选选择器,第二个呢,就是我们针对这个圈呢,我们关注的事件是什么呢?好,我们这次呢,针对这个事件,我们关注的是读这个事件,就是你有事件让我读,我你发生一个数据让我读了,我就去注册这个事件,就说如果你这个通道里面需有毒的事件发生的,我就去,呃,可以去读取你传过来数据,那下面呢,我们再填一个东西,这是前面没有讲过的,我们可以跟这个通道绑定一个什么呢,同学们。我们可以可以给这个通道绑定一个buffer。注册通道事件,关注的事件是关注事件为。他。堵的时间。OK,还同时呢,我们还做一件事情,同时同时给这个China。
18:05
呃,关联一个buffer给该。该channel就是一个socket啊。这个ET China。全,我们不也讲了很久这个buffer吗?关联一个。关联一个buffer。好吧。那怎么写呢?非常简单,一句话就可以搞定了。我们前面已经写过六。又一个bit buff法按自己bit。是这样写吧,就不用六了,直接这样产生就行了。Bed buffer,它有一个静态方法,Bed buffer.ok。然后我们大小为1024。也就是说将来我们再去读取数据的时候呢,也要借助这个buffer,我们讲过,呃,China呢,往往会不是China,就会关联一个buffer,是不是只是这个buffer呢,大家可以理解成什么,什么一种感觉呢,就相当于这个buffer。我们是在。
19:03
这个地方。是不是前面也讲过,我们服务器端其实也是有buffer的?理解哈,好,放到这就可以了。拿到这个B以后,下一个动作该干什么事情了呢?下一个动作干事干什么事情,这相当于说,呃,如果你是连接的话,这个事情就相当于完成了,就连接的事情就处理完了,好。那呃,得到一个连接过后,是不是对于客户端来讲,他第一次先连接好生成了一个烧先在过后他就可以发数据了呀,发数据是不是我们仍然是在这方也是select,因为这个select它返回的时间不一定,不一定只有你。呃,这个连接请求当然也有可能是让我来读取的,但是他第一次进来肯定是进到这里面哈。对于对于每一个客户来说,他肯定是先进到这儿,进完这个地方过后呢,你就可以发数据了,如果是你注册,你把这个通道注册好了过后呢,你下一个就要发。
20:01
发这个数据发数据,发数据的话,我们就来做一个另外一个判断,来判断一个,再接着往下判断,如果K。点是一个,呃,是一是让我们。Reliable就是可读的了。读取。对,因为他有可能是,呃,从这个K拿到这个3K呢,这次通道里面发生的是一个毒的一个事件,好,这是发生了什么呢。发生了这样一个事件。Or?好op p的呢,那我就简单一点,我就直接来读取就可以了,怎么读取呢,非常的简单,这时我们要这样来做哈,首先通过。呃,因为我们在读的时候,现在我们拿到K是不行的,要通过这个K反向获取。反向获取到对应的什么呀,Channel。是不是前面我们讲过这个事情呢?
21:00
对不对,好我再说一遍啊,为什么这有两个if,因为你这个获取到这获取到的这个这个SK呢,它可能是,嗯,是这个通道发生了连接的一个事件,也有可能是发生一个让我读的一个事件,是不是啊,所以说我要反向这个两个业务肯定是不一样的嘛。两个业务肯定是不一样的,好的,那现在我们就反向得到它的so channel,怎么得到呢?k.channel。好的,那这个这点圈呢,我们就得一下VAR拿到。好,这个地方我们需要强转一下,同学们要强转一下,转成我们so channel。啊,这边没有没有什么问题啊,我强转一下,因为呃,你们可以看一下这两两者的关系,Shocker China和刚才那个SS是我可以向下转型的啊,没有任何问题,没有任何问题。好,我们继续往下走,可以把这个拿到的这个类型转成China。
22:01
那如果同学们有怀疑的话,你可以把这个打开看一下。是不是你返回的是个sable channel,现在我把它转成是可以的,因为它本身是一条线嘛,这是Java基础告诉我们的,我觉得就。不再多说了。好,我们继续继续往下走。那拿到拿到这个China过后呢,下一步干什么呢?好的我们再获取到。获渠道。获取到该channel channel关联的关联的buffer,诶,同学们还记不记得?我每在我每注,我每产生一个呃,Channel这个so channel把它注册到S的时候,我已经附带给他。做了一个嗯,Buffer,那我现在直接拿拿到就行了,怎么拿呢,也非常简单,一个方法就可以搞定怎么拿K很强大哈。来做。
23:00
K点它这里面有一个什么呢?叫做attachment返回的一个对象,这个对象啊,就是我们的buffer。我把它强转一下。怎么转呢?转成我们的buffer。哎,就是bit buffer吧,就给到位bit buffer这边我们搜一下。好的名字咱们改成八份就可以了。关联buff分,那拿到这个buff分过后,下一步我们是不是就可以读取了呀,也就是说,呃,说说的再简单一点,就是我们要把当前这个通道里面的数据,呃,干什么呢?把这个当前这个通道数据。是,嗯,也就是说,呃,把当前这个China数据读入到这个buffer里面去,好的,那这个也简单,怎么做呢,就是channel。点。Channel点我们的read是read方法对不对,读到这里面去就可以了。
24:03
读的时候呢,就把这个buffer放进去好的。读好了以后,是不是这个时候我们就可以在服务器端显示一下,就说。客户端,客户端发送。我就叫小from吧,咱们就简单写from客户端。就是从客户端发过来,客户端数据是什么呢。加一下拼接,拼接就是还是老规矩,把它拿到的字节数组转成一个字符串就可以了,Buff。点。拿到了好这个代码呢,就写完了,最后还有一个特别重要的工作,不要忘了你整个呃变,你整个在进行遍历的时候呢,做完了以后,你要及时的把当前这个K删除掉,还有手动。手动。干什么呢?从集合中,从这个集合中。集合中干什么呢?移除当前的。
25:01
什么呀,这个select k。为什么要移除它呢?因为你在进行便利的时候,你是一个多线程的问题,你处理完了过后,你要及时删除,否则的话会出现一个重复操作,目的是防止。防止重复操作。操作,诶同学们还记不记得在讲Java技术的时候,老师有没有讲过怎么从这个inter里面把当前这个K删掉,是不是很简单呀,Remove就行了。完事了,好同学们,那这个代码写到这里呢,我们的这一个服务器端就写完了,其实就是把老师刚才整个这个图的。几个步骤用代码体现了一下。你看每每一步都是能够找到,是不是先server socket,然后server socket拿到过后,再先把这个server socket注册到我们这个sector上面去,然后呢,再产生烧channel,再把烧channel注册到S去,然后这个S开始监听,监听呢如果你是连接我就产生一个新的对不对,就这这个地方这个顺序啊,应该是。
26:09
应该这个subject呢,是呃,在产生的select过后才开始的,说监听是在这产生的,就是就是我们相当于这个S开始监听。三开始啊,开始监听。OK,那监听的时候其实呢,他在调用的是,呃,调用的是什么呀,调用的是我们的S来的方法啊,这这方也一样可以理解哈,就是他监听,监听完了过后,呃,如果如如果是一个连接,是except的一个连接,我就产生一个soet,把它注册到这上面去,是不是注册完了过后呢?呃,注册注册完了过后会返回一个select select k也放到select select select里面去了,呃,那这个监听呢,嗯,这个监听。因为在上前面已经监听了,所以说我们干脆把这个把这一步拎到上面去,好吧,其实是一个意思啊,因为我刚才讲的时候呢。
27:08
代码这块还没有把这个顺序体现出来,就是说监听监听过根据不同的情况来进行一个处理,好就说反正根据S性事验干相应的事情可以了,好整体并没有什么影响哈,整体并没有什么影响。只是呢,我从代码的角度呢,再把顺序给他捋一下就可以了,好同学们,关于这一块的服务器端,服务器端的代码我们就写完了。那下面呢,我们就准备写客户端,好,我们客户端下一个视频来讲。
我来说两句