Netty的深入浅出--32.NIO实现简单网络聊天程序(上)
在之前的项目里面,我们写了服务端,然后通过telnet和nc作为客户端进行连接,接下来我们将完整的写完服务端与客户端的代码,进行数据通信。
想必大家都使用过IO写过简单了聊天程序,现在我们试着使用NIO来实现网络聊天。
在之前的项目里面我们使用的是多个channel,建立了多个端口,这次我们采用一个通道channel(一个端口)进行网络通信。但是这个和传统的IO编程不同的是,NIO实现的网络通信,只有一个线程!
首先我们要考虑到一个问题,那就是客户端连接到服务器之后通过什么来标识的呢?UUID或者是客户端的Mac地址保存到对应的channel当中。
服务端:
创建ServerSocketChannel对象,一定要记得设置为非阻塞的形式
通过ServerSocketChannel获取服务端的socket连接,并且绑定端口号
调用selector的静态方法open,获得服务端的selector对象
现在有了serverSocketChannel对象和selector对象,一定要记住是将独立的serverSocketChannel 对象注册到selector中。
SelectionKey.OP_ACCEPT是一个事件,也就是定义了只有当触发了当前事件之后,才会调用相应的channel。而这里所对应的channel就是serverSocketChannel。
selector:该方法是一个阻塞式方法。不断监听它所关注的selectionKey中事件发生,这个方法返回的是它所关注这个事件发生的数量。
当selector监听到某个事件之后,就可以获取到该事件注册的所有SelectionKey事件
现在我们针对不同的事件进行处理
如果在获取的selectionKey的集合中有Acceptable这个key说明连接成功
连接成功之后,我们可以通过selectionKey来获取相对于的channel对象。
这里我说明一下:因为我们是将channel对象注册到selector里面的,因此通过selectionKey对象来直接获取相对应的Channel对象。O(∩_∩)O感觉到是不是很腻害的样子
既然这么腻害,那我们就去看一下.channel()这个方法的源码
返回一个SelectableChannel
而ServerSocketChannel是一个子类
返回的是一个父类型,因此我们得强制转换成一个子类型:
但是问题来说,那为什么就一定转成ServerSocketChannel就正确呢?
因为我们在刚开始的时候将OP_ACCEPT事件注册到了serverSocketChannel上面,也就是说当前只有一个Channel将accept事件注册了,所以返回的肯定只有serverSocketChannel了!
当前的servrsocketChannel任务已经完成了(主要是启到建立连接的作用)
建立连接之后,使用的是连接之后返回的socket
也要配置 成非阻塞的,至于为什么?
这还用问,阻塞式的还得等待,也就是同步,而非阻塞式就是异步,你觉得应该用什么。
创建以后也要注册到selector里面,至于为什么?
因为在NIO编程中,所有的channel都由selector管理,而且这里使用到的key是只读事件。
那现在怎样才能将消息保存到服务端,然后实现消息的分发呢?
我们使用map对象来保存客户端的连接信息:
这里我们采用UUID的方法来辨别客户端:
我们将客户端连接的socketChannel保存到map中
现在判断是否建立连接已经编写完毕,现在我们就要讨论是否客户端有发送请求信息过来
获取发送请求信息的客户端对应的channel对象
创建byteBuffer 来处理客户端发送过来的请求信息
读取客户端发送过来的请求信息:
现在是一个客户端将请求信息写入到了readBuffer中, 我们需要向其它所有连接的客户端发送信息
现在我们进行一个写操作:
调用byteBuffer中的flip方法进行反转:
对byteBuffer中的内容进行解码:
打印请求信息:
验证一下程序的正确性:
启动服务器
nc连接
程序出错了o(╥﹏╥)o
分析问题:
打印出来说明代码执行了。
再次发送,信息竟然能够发送出来
但是都会有空指针异常;
原因在于我们没有将当前使用过的key清除掉,导致出现空指针异常。
重新启动