1. import java.io.IOException;
  2. import java.net.InetSocketAddress;
  3. import java.nio.ByteBuffer;
  4. import java.nio.CharBuffer;
  5. import java.nio.channels.SelectionKey;
  6. import java.nio.channels.Selector;
  7. import java.nio.channels.ServerSocketChannel;
  8. import java.nio.channels.SocketChannel;
  9. import java.nio.charset.Charset;
  10. import java.util.Iterator;
  11. import java.util.Set;
  12.  
  13. /**
  14. * 非阻塞的Echoserver
  15. * 在这个非阻塞的模式下,Echoserver只用启动一个主线程就能同事处理其他三件事:
  16. * 1.接收客户连接
  17. * 2.接收客户发送的数据
  18. * 3.向客户发回响应数据
  19. *
  20. * @author Administrator
  21. *
  22. */
  23. public class NZSEchoServer {
  24. private Selector selector = null;
  25. private ServerSocketChannel serverSocketChannel = null;
  26. private int port = 8000;
  27.  
  28. private Charset charset = Charset.forName("GBK");
  29.  
  30. /**
  31. * 构造方法负责启动服务器,绑定端口
  32. *
  33. * @throws IOException
  34. */
  35. private NZSEchoServer() throws IOException {
  36. // 创建一个selector 对象
  37. selector = Selector.open();
  38. // 创建serverSocketChannel对象
  39. serverSocketChannel = ServerSocketChannel.open();
  40. // 设置参数 使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时可以顺利绑定到相同的端口
  41. serverSocketChannel.socket().setReuseAddress(true);
  42. // 使erverSocketChannel工作处于非阻塞模式
  43. serverSocketChannel.configureBlocking(false);
  44. // 把服务器进程与一个本地端口绑定
  45. serverSocketChannel.socket().bind(new InetSocketAddress(port));
  46. System.out.println("服务器启动!");
  47. }
  48.  
  49. /**
  50. * 负责开头说的三件事 * 1.接收客户连接 2.接收客户发送的数据 3.向客户发回响应数据
  51. *
  52. * @throws IOException
  53. */
  54. public void service() throws IOException {
  55. // serverSocketChannel向Selector注册接收连接就绪事件
  56. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  57. while (selector.select() > 0) {
  58. // 获得Selector的selected-keys集合
  59. Set readyKeys = selector.selectedKeys();
  60. Iterator it = readyKeys.iterator();
  61.  
  62. //从集合中依次取出SelectionKey对象,判断是那种事件发生,然后进行处理
  63. while (it.hasNext()) {
  64. SelectionKey key = null;
  65. try {
  66. // 处理selectionkey 取出第一个selectionkey
  67. key = (SelectionKey) it.next();
  68. // 把selectionKey从selected-key集合中删除
  69. it.remove();
  70.  
  71. // 这个key 标识连接就绪事件 处理
  72. if (key.isAcceptable()) {
  73. //获得与SelectionKey相连的ServerSocketChannel
  74. ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
  75. //获得与客户端连接的SocketChannel
  76. SocketChannel sockChannel = ssc.accept();
  77. System.out.println("接收到客户端连接,来自:" + sockChannel.socket().getInetAddress() + ":"
  78. + sockChannel.socket().getPort());
  79.  
  80. //设置SocketChannel为非阻塞模式
  81. sockChannel.configureBlocking(false);
  82. //创建一个用于存放用户发送来的数据的缓冲区
  83. ByteBuffer buffer = ByteBuffer.allocate(1024);
  84. //SocketChannel向Selector注册读就绪事件和写就绪事件 关联了一个buffer
  85. //这个byteBuffer将作为附件与新建的selectionKey对象关联
  86. sockChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);
  87.  
  88. }
  89. // 这个key 标识读就绪事件 处理
  90. if (key.isReadable()) {
  91. receive(key);
  92. }
  93. // 这个key 标识写就绪事件 处理
  94. if (key.isWritable()) {
  95. send(key);
  96. }
  97.  
  98. } catch (Exception e) {
  99. e.printStackTrace();
  100. try {
  101. //使得这个selctionkey失效
  102. //使得selectory不再监控这个selectionkey感兴趣的事件
  103. if (key != null) {
  104. key.cancel();
  105. key.channel().close();
  106. }
  107. } catch (Exception e2) {
  108. e.printStackTrace();
  109. }
  110.  
  111. }
  112.  
  113. }
  114.  
  115. }
  116.  
  117. }
  118. /**
  119. * 处理写就绪事件
  120. * @param key
  121. * @throws IOException
  122. */
  123. private void send(SelectionKey key) throws IOException {
  124. //获取与selectionKey相关联的附件
  125. ByteBuffer buffer = (ByteBuffer) key.attachment();
  126. SocketChannel socketChannel = (SocketChannel) key.channel();
  127. // flip的作用有两个:
  128. // 1. 把limit设置为当前的position值
  129. // 2. 把position设置为0
  130. // 然后处理的数据就是从position到limit直接的数据,也就是你刚刚读取过来的数据
  131. buffer.flip();
  132. //按照gbk编码,把buffer中的字节转换成字符串
  133. String data = decode(buffer);
  134. //如果还没有读到一行数据,之间返回
  135. if (data.indexOf("\r\n") == -1)
  136. return;
  137.  
  138. //已经有一行以上数据,截取一行数据
  139. String outputData = data.substring(0, data.indexOf("\n") + 1);
  140. System.out.println(outputData);
  141. //把输出的字符串按照gbk编码,转换成字节,把他反正outputBuffer中
  142. ByteBuffer outputBuffer = encode("echo" + outputData);
  143.  
  144. //outputBuffer.hasRemaining()判断是否还有未处理的字节
  145. //非阻塞模式下不确保write方法一次就把outputBuffer所有字节发送完,只是奉行能发多少就发送多少的原则,所以我们要采用循环
  146. while (outputBuffer.hasRemaining()) {
  147. socketChannel.write(outputBuffer);
  148. }
  149.  
  150. //我觉得 相当于移动 栈指针 然后删除没有指向的数据段
  151. ByteBuffer temp = encode(outputData);
  152. //设置buffer的位置为temp的极限
  153. buffer.position(temp.limit());
  154. //删除buffer中已经处理的数据
  155. buffer.compact();
  156. //如果已经输出了字符串 "bye\r\n" ,就使selectionKet失效,并关闭SocketChannel
  157. if (outputData.equals("bye\r\n")) {
  158. key.cancel();
  159. socketChannel.close();
  160. System.out.println("关闭与客户端的连接!");
  161. }
  162.  
  163. }
  164.  
  165. /**
  166. *
  167. * 处理读就绪事件
  168. * 把收到的数据放入buffer
  169. *
  170. * @param key
  171. * @throws IOException
  172. */
  173. public void receive(SelectionKey key) throws IOException {
  174. //获得与SelectionKey关联的附件
  175. ByteBuffer buffer = (ByteBuffer) key.attachment();
  176. //获得与SelectionKey关联的Sockethannel
  177. SocketChannel socketChannel = (SocketChannel) key.channel();
  178. //创建一个byteBuffer,用于存放读到的数据
  179. ByteBuffer readBuffer = ByteBuffer.allocate(32);
  180. socketChannel.read(readBuffer);
  181. readBuffer.flip();
  182.  
  183. //把buffer的极限设为容量
  184. buffer.limit(buffer.capacity());
  185. //把readBuffer中的内容拷贝到buffer中
  186. //假定buffer的容量足够大,不会出现缓冲区溢出异常
  187. buffer.put(readBuffer); // 把读到的数据放到buffer中
  188. }
  189.  
  190. /**
  191. * 编码 把字符串转换成自己序列
  192. *
  193. * @param string
  194. * @return
  195. */
  196. private ByteBuffer encode(String string) {
  197. return charset.encode(string);
  198. }
  199.  
  200. /**
  201. * 解码 把字节序列转换为字符串
  202. *
  203. * @param buffer
  204. * @return
  205. */
  206. private String decode(ByteBuffer buffer) {
  207. CharBuffer charBuffer = charset.decode(buffer);
  208. return charBuffer.toString();
  209. }
  210. /**
  211. * 主程序
  212. * @param args
  213. * @throws IOException
  214. */
  215. public static void main(String[] args) throws IOException {
  216. NZSEchoServer server=new NZSEchoServer();
  217. server.service();
  218. }
  219. }

非阻塞模式ServerSocketChannel 聊天室服务器端的更多相关文章

  1. Python编写基于socket的非阻塞多人聊天室程序(单线程&多线程)

    前置知识:socket非阻塞函数(socket.setblocking(False)),简单多线程编程 代码示例: 1. 单线程非阻塞版本: 服务端: #!/usr/bin/env python # ...

  2. NIO Socket非阻塞模式

    NIO主要原理和适用 NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有 事件发生时,他会通知我们 ...

  3. 深入 CSocket 编程之阻塞和非阻塞模式

    有时,花上几个小时阅读.调试.跟踪优秀的源码程序,能够更快地掌握某些技术关键点和精髓.当然,前提是对这些技术大致上有一个了解. 我通过几个采用 CSocket 类编写并基于 Client/Server ...

  4. JavaNIO非阻塞模式

    package com.java.NIO; import java.io.IOException; import java.net.InetSocketAddress; import java.nio ...

  5. 非阻塞模式(ioctlsocket)

    //Server.cpp #include <stdio.h> #include <winsock2.h> //winsock.h (2种套接字版本) #pragma comm ...

  6. 使用命名管道的OVERLAPPED方式实现非阻塞模式编程 .

    命令管道是进程间通讯的一种常用方式,对于命令管道的介绍可以参考别的资料和书籍,这里推荐一个<VC++下命名管道编程的原理及实现>这篇博文,写得比较清楚.但是都是介绍了阻塞模式的编程,我这里 ...

  7. PHP非阻塞模式 (转自 尘缘)

    让PHP不再阻塞当PHP作为后端处理需要完成一些长时间处理,为了快速响应页面请求,不作结果返回判断的情况下,可以有如下措施: 一.若你使用的是FastCGI模式,使用fastcgi_finish_re ...

  8. TCP同步与异步及阻塞模式,多线程+阻塞模式,非阻塞模式简单介绍

    首先我简单介绍一下同步TCP编程 与异步TCP编程. 在服务端我们通常用一个TcpListener来监听一个IP和端口.客户端来一个请求的连接,在服务端可以用同步的方式来接收,也可以用异步的方式去接收 ...

  9. UDP socket 设置为的非阻塞模式

    UDP socket 设置为的非阻塞模式 Len = recvfrom(SocketFD, szRecvBuf, sizeof(szRecvBuf), MSG_DONTWAIT, (struct so ...

随机推荐

  1. 使用Dom4j操作XML数据

    --------------siwuxie095                             dom4j 是一个非常优秀的 Java XML 的 API, 用来读写 XML 文件 和操作 ...

  2. vectors 使用应该注意到的问题

    ector1. vector的元素必须具备 assignable和 copyable . 2.vector的迭代器是随机存取迭代器. 3.要考虑到vector的大小(size)和容量(capacity ...

  3. 面试题:java实例变量,局部变量,类变量 背1

    一.实例变量 也叫对象变量.类成员变量:从属于类由类生成对象时,才分配存储空间,各对象间的实例变量互不干扰,能通过对象的引用来访问实例变量.但在Java多线程中,实例变量是多个线程共享资源,要注意同步 ...

  4. Marr的视觉计算理论

            Marr的视觉计算理论立足于计算机科学,系统地概括了心理物理学.神经生理学.临床神经病理学等方面已取得的所有重要成果,是迄今为止最为系统的视觉理论.Marr 的视觉计算理论虽然在细节甚 ...

  5. zynq qemu学习

    1,ubuntu给软件包降级,先安装aptitude sudo apt-get  install aptitude 2,强制降级,等号“=”前后不能有空格 sudo aptitude install ...

  6. 9.hive聚合函数,高级聚合,采样数据

    本文主要使用实例对Hive内建的一些聚合函数.分析函数以及采样函数进行比较详细的讲解. 一.基本聚合函数 数据聚合是按照特定条件将数据整合并表达出来,以总结出更多的组信息.Hive包含内建的一些基本聚 ...

  7. EZOJ #202

    传送门 分析 我们知道选一个点的代价就是他所有出边边权的异或和 由于一条边如果两个端点均选边权会异或两次变回0,所以不必担心重复的情况 于是直接跑线性基即可 代码 #include<bits/s ...

  8. CF1073F Choosing Two Paths

    发现从顶点入手不太方便,我们从这个“公共部分最长”开始考虑问题,因为要求这一条公共部分的链最长,可以联想到树的直径,那么本题就是要求一条类似于直径的东西使两个端点除了直径这一条链之外还有不少于两个的儿 ...

  9. eclipse——配置maven插件

    Step 1 配置installations installations:指定Maven核心程序的位置  从本地磁盘中找到本地maven的位置 Step 2 配置user settings user ...

  10. 黑盒测试实践-任务进度-Day03

    任务进度11-28 使用工具 selenium 小组成员 华同学.郭同学.穆同学.沈同学.覃同学.刘同学 任务进度 经过了前两天的学习任务的安排,以下是大家的任务进度: 华同学(任务1) 1.今天就接 ...