NIO网络编程实战
利用NIO编程知识,实现多人聊天室。

1. NIO编程实现步骤

第一步:创建Selector

第二步:创建ServerSocketChannel,并绑定监听端口

第三步:将Channel设置为非阻塞模式

第四步:将Channel注册到Selector上,监听连接事件

第五步:循环调用Selector的select方法,检测就绪情况

第六步:调用selectedKeys方法获取就绪channel集合

第七步:判断就绪事件种类,调用业务处理方法

第八步:根据业务需要决定是否再次注册监听事件,重复执行第三步操作

2. NIO网络编程实战

利用NIO编程知识,实现多人聊天室。

2.1 程序结构目录

2.2 服务器端程序

package com.xuxin.niochatroom;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;/*** NIO服务器端*/
public class NioServer {/*** 启动*/public void start() throws IOException {// 1. 创建SelectorSelector selector = Selector.open();// 2. 通过ServerSocketChannel创建channel通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 3. 为channel通道绑定监听端口serverSocketChannel.bind(new InetSocketAddress(8000));// 4. 设置channel为非阻塞模式serverSocketChannel.configureBlocking(false);// 5. 将channel注册到selector上,监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动成功!");// 6. 循环等待新接入的连接for (;;) {// 获取可用channel的数量int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 获取可用channel的集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator iterator = selectionKeys.iterator();while(iterator.hasNext()) {// selectionKey实例SelectionKey selectionKey = (SelectionKey) iterator.next();// 移除Set中的当前selectionKeyiterator.remove();// 7. 根据就绪状态,调用对应方法处理业务逻辑// 如果是 接入事件if (selectionKey.isAcceptable()) {acceptHandler(serverSocketChannel, selector);}// 如果是 可读事件if (selectionKey.isReadable()) {readHandler(selectionKey, selector);}}}}/*** 接入事件处理器*/private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {// 如果要是接入事件,创建socketChannelSocketChannel socketChannel = serverSocketChannel.accept();// 将socketChannel设置为非阻塞工作模式socketChannel.configureBlocking(false);// 将channel注册到selector上,监听 可读事件socketChannel.register(selector, SelectionKey.OP_READ);// 回复客户端提示信息socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室里其他人都不是朋友关系,请注意隐私安全"));}/*** 可读事件处理器*/private void readHandler(SelectionKey selectionKey, Selector selector)throws IOException {// 要从selectionKey中获取到已经就绪的channelSocketChannel socketChannel = (SocketChannel) selectionKey.channel();// 创建bufferByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 循环读取客户端请求信息String request = "";while (socketChannel.read(byteBuffer) > 0) {// 切换buffer为读模式byteBuffer.flip();// 读取buffer中的内容request += Charset.forName("UTF-8").decode(byteBuffer);}// 将channel再次注册到selector上,监听他的可读事件socketChannel.register(selector, selectionKey.OP_READ);// 将客户端发送的请求信息 广播给其他客户端if (request.length() > 0) {// 广播给其他客户端broadCast(selector, socketChannel, request);}}/*** 广播给其他客户端*/private void broadCast(Selector selector, SocketChannel sourceChannel, String request) {// 获取到所有已接入的客户端channelSet<SelectionKey> selectionKeySet = selector.keys();// 循环向所有channel广播信息selectionKeySet.forEach(selectionKey -> {Channel targetChannel = selectionKey.channel();// 剔除发消息的客户端if ((targetChannel instanceof SocketChannel) && (targetChannel != sourceChannel)) {try {// 将信息群发给target的客户端((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(request));} catch (IOException e) {e.printStackTrace();}}});}/*** 主程序* @param args*/public static void main(String[] args) throws IOException {NioServer nioServer = new NioServer();nioServer.start();}
}

2.3 客户端程序

package com.xuxin.niochatroom;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;/*** NIO客户端*/
public class NioClient {/*** 启动*/public void start(String nickName) throws IOException {// 连接服务器SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));// 接收服务器端响应// 创建线程,专门负责接收服务器端的响应数据// selector socketChannel 注册Selector selector = Selector.open();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);new Thread(new NioClientHandler(selector)).start();// 向服务器端发送数据Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String request = scanner.nextLine();if ((request != null) && (request.length() > 0)) {socketChannel.write(Charset.forName("UTF-8").encode(nickName + " : " + request));}}}
}

2.4 客户端处理服务器端响应的线程

package com.xuxin.niochatroom;import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;public class NioClientHandler implements Runnable {private Selector selector;public NioClientHandler(Selector selector) {this.selector = selector;}@Overridepublic void run() {try {// 1. 循环等待服务器端响应for (; ; ) {// 获取可用channel的数量int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 获取可用channel的集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator iterator = selectionKeys.iterator();while (iterator.hasNext()) {// selectionKey实例SelectionKey selectionKey = (SelectionKey) iterator.next();// 移除Set中的当前selectionKeyiterator.remove();// 7. 根据就绪状态,调用对应方法处理业务逻辑// 如果是 可读事件if (selectionKey.isReadable()) {readHandler(selectionKey, selector);}}}} catch (IOException e) {e.printStackTrace();}}/*** 可读事件处理器*/private void readHandler(SelectionKey selectionKey, Selector selector)throws IOException {// 要从selectionKey中获取到已经就绪的channelSocketChannel socketChannel = (SocketChannel) selectionKey.channel();// 创建bufferByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 循环读取服务器端的响应数据String reponse = "";while (socketChannel.read(byteBuffer) > 0) {// 切换buffer为读模式byteBuffer.flip();// 读取buffer中的内容reponse += Charset.forName("UTF-8").decode(byteBuffer);}// 将channel再次注册到selector上,监听他的可读事件socketChannel.register(selector, selectionKey.OP_READ);// 将服务器端的响应数据打印到本地if (reponse.length() > 0) {System.out.println(":: " + reponse);}}
}

2.5 测试

2.5.1 创建用于测试的客户端类

ClientA:

package com.xuxin.niochatroom;import java.io.IOException;public class ClientA {public static void main(String[] args) throws IOException {new NioClient().start("ClientA");}
}

ClientB:

package com.xuxin.niochatroom;import java.io.IOException;public class ClientB {public static void main(String[] args) throws IOException {new NioClient().start("ClientB");}
}

ClientC:

package com.xuxin.niochatroom;import java.io.IOException;public class ClientC {public static void main(String[] args) throws IOException {new NioClient().start("ClientC");}
}

2.5.2 结果

启动服务器端

启动ClientA、ClientB、ClientC,并先由ClientA开始输入聊天信息,再由ClientB开始输入聊天信息,最后由ClientC开始输入聊天信息

2.6 NIO网络编程缺陷

麻烦

NIO类库和API繁杂。需要了解ServerSocketChannel、SocketChannel、ByteBuffer等核心类库的使用

问题

可靠性能力补齐,工作量和难度都非常大。
客户端的断连、重连、网络闪断、失败缓存、网络阻塞和异常码流等问题。

有坑

Selector空轮询,导致CPU100%。

NIO网络编程实战之简单多人聊天室相关推荐

  1. 基于Python Tkiner、thread与socket实现的简单多人聊天室,在Python中创建TCP服务器与客户端进行通信

    基于Python Tkiner.thread与socket实现的简单多人聊天室,在Python中创建TCP服务器与客户端进行通信 完整代码下载地址:基于Python Tkiner.thread与soc ...

  2. python多人聊天室_python实现简单多人聊天室

    本文实例为大家分享了python实现多人聊天室的具体代码,供大家参考,具体内容如下 刚开始学习python,写了一个聊天室练练手. Server.py import socket,select,thr ...

  3. socket.io php 聊天室,WebSocket学习(一)——基于socket.io实现简单多人聊天室

    前言 什么是Websocket呢? 我们都知道在Http协议中,客户端与服务器端的通信是靠客户端发起请求,然后服务器端收到请求再进行回应,这个过程中,客户端是主动的,服务器端是被动的.Websocke ...

  4. Java NIO Selector详解(含多人聊天室实例)

    一.Java NIO 的核心组件 Java NIO的核心组件包括:Channel(通道),Buffer(缓冲区),Selector(选择器),其中Channel和Buffer比较好理解 简单来说 NI ...

  5. python聊天室_python实现简单多人聊天室

    本文实例为大家分享了python实现多人聊天室的具体代码,供大家参考,具体内容如下 刚开始学习python,写了一个聊天室练练手. Server.py import socket,select,thr ...

  6. 网络编程-基于MFC的仿QQ聊天室-2020

    基于MFC的仿QQ聊天室(2020) 有幸学习过网络编程的一些知识,出于对编程的热爱,把曾经的一次简单实践编程作业进行了自定义的完成. 编程所需: 编程工具为VS 2010,需要掌握MFC的基本操作以 ...

  7. Python简单多人聊天室

    # ------------------------------服务器端-----------------------------------------# auther: kele # 创建时间:2 ...

  8. JAVA网络编程NIO实现简易多人聊天室

    BIO模型 BIO即blocking IO,顾名思义是一种阻塞模型.当没有客户端连接时,服务端会一直阻塞,当有客户端新建连接时,服务端会新开一个线程去响应(不用多线程的话服务端同一时刻最多只能接收一个 ...

  9. 陈硕《网络编程实战》 02 一个TCP的简单实验

    [稿] 陈硕<网络编程实战> 02 一个TCP的简单实验 站在巨人的肩膀之上. 按照录像整理,部分专有名词不太肯定,版权归陈硕大神. 实验用的是我家里的几台计算机,第一台的主机名叫atom ...

最新文章

  1. MongoDB (二) MongoDB 优点
  2. c#知识点——数据库
  3. Python函数名的本质,你有了解过嘛?
  4. Java中怎么样检查一个字符串是不是数字呢
  5. mvc框架upgrade
  6. svn ignore 的用法
  7. 人性歪曲的心理调适 一【浮躁心理、偏激心理、自卑心理、自杀心理、愤怒心理】...
  8. torch中loss.bacword的理解
  9. Linux服务器开机自动启动服务或脚本的方法
  10. 人民币大写金额转换C#方法
  11. 地图下载器拼接ArcGIS Server瓦片
  12. 微云存储空间多大_微云用户容量调整多少了 微云用户容量变小了是吗
  13. WinMerge使用
  14. EXCEL10:excel看板
  15. 电子竞技作为一项全新的竞技体育项目,近年来发展迅猛,未来发展趋势
  16. 全面接入:ChatGPT杀进10个商业应用,让AI替你打工
  17. KD树(K-Dimension Tree)
  18. 精读《web reflow》
  19. 测试开发实习日记(DAY1)
  20. hypermesh 连接单元_超低延时 13mm超大动圈单元 QCY T8S半入耳游戏耳机_蓝牙耳机

热门文章

  1. oracle nvachar 长度,Oracle中varchar、varchar2和nvarchar、nvarchar2
  2. java web配置dll文件_JavaWeb项目中dll文件动态加载方法解析(详细步骤)
  3. android自定义下载框架,Android_DownloadUtil
  4. java线程方法_Java线程的三种方式
  5. websvn mysql_Centos 5.3 Nginx+php+mysql配置 独立的 Subversion (SVN)服务器
  6. 华视电子读卡器web开发_业界动态:华视电子官方微信公众号电子书架正式上线...
  7. centos中ifcfg-eth0配置ip后,重启network服务提示地址已被使用
  8. 从输入url到显示网页,后台发生了什么?
  9. input标签内容改变的触发事件
  10. MVC三层+会话层+线程安全+抽象工厂+DataBase First的基础框架