本文实例为大家分享了Java NIO实现群聊系统的具体代码,供大家参考,具体内容如下
实例要求:
1)编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
2)实现多人群聊
3)服务器端:可以监测用户上线,离线,并实现消息转发功能
4)客户端:通过channel 可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)
5)目的:进一步理解NIO非阻塞网络编程机制

服务端代码:

  1. import java.io.IOException;
  2. import java.net.InetSocketAddress;
  3. import java.nio.ByteBuffer;
  4. import java.nio.channels.*;
  5. import java.util.Iterator;
  6. public class GroupChatServer {
  7. //定义属性
  8. private Selector selector;
  9. private ServerSocketChannel listenChannel;
  10. private static final int PORT = 6667;
  11. //构造器
  12. //初始化工作
  13. public GroupChatServer() {
  14. try {
  15. //得到选择器
  16. selector = Selector.open();
  17. //ServerSocketChannel
  18. listenChannel = ServerSocketChannel.open();
  19. //绑定端口
  20. listenChannel.socket().bind(new InetSocketAddress(PORT));
  21. //设置非阻塞模式
  22. listenChannel.configureBlocking(false);
  23. //将该listenChannel 注册到selector
  24. listenChannel.register(selector, SelectionKey.OP_ACCEPT);
  25. }catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. //监听
  30. public void listen() {
  31. System.out.println("监听线程: " + Thread.currentThread().getName());
  32. try {
  33. //循环处理
  34. while (true) {
  35. int count = selector.select();
  36. if(count > 0) {//有事件处理
  37. //遍历得到selectionKey 集合
  38. Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  39. while (iterator.hasNext()) {
  40. //取出selectionkey
  41. SelectionKey key = iterator.next();
  42. //监听到accept
  43. if(key.isAcceptable()) {
  44. SocketChannel sc = listenChannel.accept();
  45. sc.configureBlocking(false);
  46. //将该 sc 注册到seletor
  47. sc.register(selector, SelectionKey.OP_READ);
  48. //提示
  49. System.out.println(sc.getRemoteAddress() + " 上线 ");
  50. }
  51. if(key.isReadable()) { //通道发送read事件,即通道是可读的状态
  52. //处理读 (专门写方法..)
  53. readData(key);
  54. }
  55. //当前的key 删除,防止重复处理
  56. iterator.remove();
  57. }
  58. } else {
  59. System.out.println("等待....");
  60. }
  61. }
  62. }catch (Exception e) {
  63. e.printStackTrace();
  64. }finally {
  65. //发生异常处理....
  66. }
  67. }
  68. //读取客户端消息
  69. private void readData(SelectionKey key) {
  70. //取到关联的channle
  71. SocketChannel channel = null;
  72. try {
  73. //得到channel
  74. channel = (SocketChannel) key.channel();
  75. //创建buffer
  76. ByteBuffer buffer = ByteBuffer.allocate(1024);
  77. int count = channel.read(buffer);
  78. //根据count的值做处理
  79. if(count > 0) {
  80. //把缓存区的数据转成字符串
  81. String msg = new String(buffer.array());
  82. //输出该消息
  83. System.out.println("form 客户端: " + msg);
  84. //向其它的客户端转发消息(去掉自己), 专门写一个方法来处理
  85. sendInfoToOtherClients(msg, channel);
  86. }
  87. }catch (IOException e) {
  88. try {
  89. System.out.println(channel.getRemoteAddress() + " 离线了..");
  90. //取消注册
  91. key.cancel();
  92. //关闭通道
  93. channel.close();
  94. }catch (IOException e2) {
  95. e2.printStackTrace();;
  96. }
  97. }
  98. }
  99. //转发消息给其它客户(通道)
  100. private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{
  101. System.out.println("服务器转发消息中...");
  102. System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
  103. //遍历 所有注册到selector 上的 SocketChannel,并排除 self
  104. for(SelectionKey key: selector.keys()) {
  105. //通过 key 取出对应的 SocketChannel
  106. Channel targetChannel = key.channel();
  107. //排除自己
  108. if(targetChannel instanceof SocketChannel && targetChannel != self) {
  109. //转型
  110. SocketChannel dest = (SocketChannel)targetChannel;
  111. //将msg 存储到buffer
  112. ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
  113. //将buffer 的数据写入 通道
  114. dest.write(buffer);
  115. }
  116. }
  117. }
  118. public static void main(String[] args) {
  119. //创建服务器对象
  120. GroupChatServer groupChatServer = new GroupChatServer();
  121. groupChatServer.listen();
  122. }
  123. }
  124. //可以写一个Handler
  125. class MyHandler {
  126. public void readData() {
  127. }
  128. public void sendInfoToOtherClients(){
  129. }
  130. }
  1. import java.io.IOException;
  2. import java.net.InetSocketAddress;
  3. import java.nio.ByteBuffer;
  4. import java.nio.channels.SelectionKey;
  5. import java.nio.channels.Selector;
  6. import java.nio.channels.SocketChannel;
  7. import java.util.Iterator;
  8. import java.util.Scanner;
  9. import java.util.Set;
  10. public class GroupChatClient {
  11. //定义相关的属性
  12. private final String HOST = "127.0.0.1"; // 服务器的ip
  13. private final int PORT = 6667; //服务器端口
  14. private Selector selector;
  15. private SocketChannel socketChannel;
  16. private String username;
  17. //构造器, 完成初始化工作
  18. public GroupChatClient() throws IOException {
  19. selector = Selector.open();
  20. //连接服务器
  21. socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
  22. //设置非阻塞
  23. socketChannel.configureBlocking(false);
  24. //将channel 注册到selector
  25. socketChannel.register(selector, SelectionKey.OP_READ);
  26. //得到username
  27. username = socketChannel.getLocalAddress().toString().substring(1);
  28. System.out.println(username + " is ok...");
  29. }
  30. //向服务器发送消息
  31. public void sendInfo(String info) {
  32. info = username + " 说:" + info;
  33. try {
  34. socketChannel.write(ByteBuffer.wrap(info.getBytes()));
  35. }catch (IOException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. //读取从服务器端回复的消息
  40. public void readInfo() {
  41. try {
  42. int readChannels = selector.select();
  43. if(readChannels > 0) {//有可以用的通道
  44. Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  45. while (iterator.hasNext()) {
  46. SelectionKey key = iterator.next();
  47. if(key.isReadable()) {
  48. //得到相关的通道
  49. SocketChannel sc = (SocketChannel) key.channel();
  50. //得到一个Buffer
  51. ByteBuffer buffer = ByteBuffer.allocate(1024);
  52. //读取
  53. sc.read(buffer);
  54. //把读到的缓冲区的数据转成字符串
  55. String msg = new String(buffer.array());
  56. System.out.println(msg.trim());
  57. }
  58. }
  59. iterator.remove(); //删除当前的selectionKey, 防止重复操作
  60. } else {
  61. //System.out.println("没有可以用的通道...");
  62. }
  63. }catch (Exception e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. public static void main(String[] args) throws Exception {
  68. //启动我们客户端
  69. GroupChatClient chatClient = new GroupChatClient();
  70. //启动一个线程, 每隔3秒,读取从服务器发送数据
  71. new Thread() {
  72. public void run() {
  73. while (true) {
  74. chatClient.readInfo();
  75. try {
  76. Thread.currentThread().sleep(3000);
  77. }catch (InterruptedException e) {
  78. e.printStackTrace();
  79. }
  80. }
  81. }
  82. }.start();
  83. //发送数据给服务器端
  84. Scanner scanner = new Scanner(System.in);
  85. while (scanner.hasNextLine()) {
  86. String s = scanner.nextLine();
  87. chatClient.sendInfo(s);
  88. }
  89. }
  90. }

注意:必须设置通道为非阻塞,才能向Selector注册,否则报 java.nio.channels.IllegalBlockingModeException 错
注意:在客户端上要想获取得到服务端的数据,也需要注册在register上(监听读事件)

更多相关文章

  1. 如何远程登录云服务器?登录失败是什么原因?
  2. 部署小程序的云服务器要注意什么?配置过程是怎么样的?
  3. 程序员有必要拥有一台自己的云服务器吗?为什么?
  4. 小鸟云服务器FTP上传中断是什么原因?解决方法总结
  5. 小鸟云虚拟主机和云服务器和裸金属服务器有何不一样?
  6. 物理机、虚拟机、云主机、物理服务器、裸金属都是什么?
  7. Linux服务器遇到攻击怎么办?用这些方法封禁IP
  8. 记录对云服务器的初体验
  9. 云服务器如何通过本地安全策略阻止特定IP访问服务器?

随机推荐

  1. 官方示例(十):网页开发3D粒子系统实现降雨效
  2. Spring Boot 揭秘与实战 内嵌的服务器 To
  3. Linux性能优化(二)——sysbench压力测试工
  4. Spring Boot 开箱即用,内藏玄机
  5. 第二日学习
  6. Spring Boot 揭秘与实战 工作原理剖析
  7. Linux性能优化(一)——stress压力测试工具
  8. 安全要素与 STRIDE 威胁
  9. 在51CTO学院学习PMP,终于get证书
  10. 安全的软件开发生命周期