Java NIO 中 ServerSocketChannel和SocketChannel

1、服务端代码

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.nio.ByteBuffer;  
  4. import java.nio.channels.ClosedChannelException;  
  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.util.Iterator;  
  10.   
  11. public class Server {  
  12.     // 多路复用器(管理所有的通道)  
  13.     private Selector selector;  
  14.   
  15.     // 用于初始化Server配置  
  16.     private void init(int port) {  
  17.   
  18.         ServerSocketChannel servSocketChannel;  
  19.   
  20.         try {  
  21.             // 打开服务器通道  
  22.             servSocketChannel = ServerSocketChannel.open();  
  23.             // 设置非阻塞模式  
  24.             servSocketChannel.configureBlocking(false);  
  25.             // 绑定端口  
  26.             servSocketChannel.socket().bind(new InetSocketAddress(port));  
  27.   
  28.             // 打开路复用器  
  29.             selector = Selector.open();  
  30.             // 把服务器通道注册到多路复用器上,并且监听阻塞事件  
  31.             servSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
  32.             // 输出,服务端开启信息  
  33.             System.out.println("Server start, port :" + port);  
  34.         } catch (IOException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  
  38.   
  39.     // 用于一直轮询选择器中,管道的状态  
  40.     public void listen() {  
  41.         // 用于一直轮询选择器中,管道的状态  
  42.         while (true) {  
  43.             try {  
  44.                 // 1 必须要让多路复用器开始监听  
  45.                 this.selector.select();  
  46.                 // 2 返回多路复用器已经选择的结果集  
  47.                 Iterator<SelectionKey> keys = this.selector.selectedKeys()  
  48.                         .iterator();  
  49.                 // 3 进行遍历  
  50.                 while (keys.hasNext()) {  
  51.                     // 4 获取一个选择的元素  
  52.                     SelectionKey key = keys.next();  
  53.                     // 5 直接从容器中移除就可以了  
  54.                     keys.remove();  
  55.                     // 6 如果是有效的,对Key相应的事件进行处理  
  56.                     if (key.isValid()) {  
  57.                         handleKey(key);  
  58.                     }  
  59.                 }  
  60.             } catch (IOException e) {  
  61.                 e.printStackTrace();  
  62.             }  
  63.         }  
  64.     }  
  65.   
  66.     private void handleKey(SelectionKey key) throws IOException,  
  67.             ClosedChannelException {  
  68.   
  69.         if (key.isAcceptable()) {  
  70.             accept(key);  
  71.         } else if (key.isReadable()) {  
  72.             read(key);  
  73.         }  
  74.   
  75.     }  
  76.   
  77.     private void read(SelectionKey key) {  
  78.         SocketChannel channel = null;  
  79.         try {  
  80.             // 0 用于服务端接受客户端数据的缓冲区  
  81.             ByteBuffer readBuf = ByteBuffer.allocate(2048);  
  82.             // 1 清空缓冲区旧的数据  
  83.             readBuf.clear();  
  84.             // 2 获取之前注册的socket通道对象  
  85.             channel = (SocketChannel) key.channel();  
  86.             // 3 读取数据  
  87.             int count = channel.read(readBuf);  
  88.             // 4 如果有数据,就进行处理  
  89.             if (count > 0) {  
  90.                 // 5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)  
  91.                 // 进入读模式  
  92.                 readBuf.flip();  
  93.                 // 6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据  
  94.                 byte[] bytes = new byte[readBuf.remaining()];  
  95.                 // 7 接收缓冲区数据  
  96.                 readBuf.get(bytes);  
  97.                 // 8 打印结果  
  98.                 String requestInfo = new String(bytes).trim();  
  99.                 System.out.println("Server : " + requestInfo);  
  100.   
  101.                 // 9 写回给客户端数据  
  102.                 // 根据客户端的请求数据,获取相应数据,并回写给客户端  
  103.                 String response = getAnswer(requestInfo);  
  104.                 // 通过String的response,构造ByteBuffer,返回给客户端  
  105.                 ByteBuffer bufferResponse = ByteBuffer  
  106.                         .wrap(response.getBytes());  
  107.                 channel.write(bufferResponse);  
  108.             }  
  109.   
  110.         } catch (IOException e) {  
  111.             // 3 读取数据发生异常的时候,关闭连接,释放资源  
  112.             // int count = channel.read(readBuf);  
  113.             try {  
  114.                 key.channel().close();  
  115.                 key.cancel();  
  116.                 System.out.println("客户端退出");  
  117.             } catch (IOException e1) {  
  118.                 e1.printStackTrace();  
  119.             }  
  120.   
  121.         }  
  122.   
  123.     }  
  124.   
  125.     private void accept(SelectionKey key) {  
  126.         try {  
  127.             // 1 获取服务通道  
  128.             ServerSocketChannel ssc = (ServerSocketChannel) key.channel();  
  129.             // 2 执行阻塞方法  
  130.             SocketChannel sc = ssc.accept();  
  131.             // 3 设置阻塞模式  
  132.             sc.configureBlocking(false);  
  133.             // 4 注册到多路复用器上,并设置读取标识  
  134.             sc.register(this.selector, SelectionKey.OP_READ);  
  135.         } catch (IOException e) {  
  136.             e.printStackTrace();  
  137.         }  
  138.     }  
  139.   
  140.     // 根据用户的请求,获取返回信息  
  141.     // 实际的开发,可以到数据库获取数据  
  142.     private String getAnswer(String question) {  
  143.         String answer = "请输入 who, 或者what, 或者where";  
  144.         if ("who".equals(question)) {  
  145.             answer = "我是莉莉";  
  146.         } else if ("what".equals(question)) {  
  147.             answer = "我是来帮你解闷的";  
  148.         } else if ("where".equals(question)) {  
  149.             answer = "我来自外太空";  
  150.         } else {  
  151.             answer = "请输入 who, 或者what, 或者where";  
  152.         }  
  153.         // 实际开发,可以根据请求信息  
  154.         // 到数据库查询数据,并且返回给客户端  
  155.         System.out.println(answer);  
  156.         return answer;  
  157.     }  
  158.   
  159.     public static void main(String[] args) {  
  160.         Server server = new Server();  
  161.         server.init(8889);  
  162.         server.listen();  
  163.     }  
  164.   
  165. }  

2、客户端代码

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  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.   
  10. public class Client {  
  11.   
  12.     private SocketChannel channel;  
  13.     private Selector selector;  
  14.   
  15.     public static void main(String[] args) {  
  16.         Client c = new Client();  
  17.         c.connect("localhost"8889);  
  18.     }  
  19.   
  20.     public void connect(String ipaddress, int port) {  
  21.         try {  
  22.             channel = SocketChannel.open();  
  23.             channel.configureBlocking(false);  
  24.             // 请求连接  
  25.             channel.connect(new InetSocketAddress(ipaddress, port));  
  26.             selector = Selector.open();  
  27.             channel.register(selector, SelectionKey.OP_CONNECT);  
  28.             boolean isOver = false;  
  29.   
  30.             while (!isOver) {  
  31.                 selector.select();  
  32.                 Iterator<SelectionKey> ite = selector.selectedKeys().iterator();  
  33.                 while (ite.hasNext()) {  
  34.                     SelectionKey key = (SelectionKey) ite.next();  
  35.                     // 处理,并移除这个Key  
  36.                     ite.remove();  
  37.                     // 进行Key的处理  
  38.                     handle(key);  
  39.                 }  
  40.             }  
  41.         } catch (IOException e) {  
  42.             e.printStackTrace();  
  43.         } finally {  
  44.             CloseUtils.closeCloseable(channel, selector);  
  45.         }  
  46.     }  
  47.   
  48.     private void handle(SelectionKey key) {  
  49.         try {  
  50.             // 处理前,先判断,这个Key是有效的  
  51.             if (key.isValid()) {  
  52.                 if (key.isConnectable()) {  
  53.                     if (channel.isConnectionPending()) {  
  54.                         if (channel.finishConnect()) {  
  55.                             // 只有当连接成功后才能注册OP_READ事件  
  56.                             key.interestOps(SelectionKey.OP_READ);  
  57.                             // 客户端连接成功,给服务端发送请求  
  58.                             String request = "Hi I am client ...";  
  59.                             ByteBuffer buffer = ByteBuffer.wrap(request  
  60.                                     .getBytes());  
  61.                             channel.write(buffer);  
  62.                         } else {  
  63.                             key.cancel();  
  64.                         }  
  65.                     }  
  66.                 } else if (key.isReadable()) {  
  67.                     read(key);  
  68.                 }  
  69.             }  
  70.   
  71.         } catch (IOException e) {  
  72.             e.printStackTrace();  
  73.         }  
  74.     }  
  75.   
  76.     // 用于读取,客户端的请求数据  
  77.     // 并根据,客户端的请求数据。将相应的数据返回给客户端  
  78.     private void read(SelectionKey key) {  
  79.         try {  
  80.             // 2 用于服务端接受客户端数据的缓冲区  
  81.             ByteBuffer readBuf = ByteBuffer.allocate(2048);  
  82.             // 2 获取之前注册的socket通道对象  
  83.             SocketChannel channel = (SocketChannel) key.channel();  
  84.             // 3 读取数据  
  85.             int count = channel.read(readBuf);  
  86.             // 有数据,就读取;没数据,就不读取  
  87.             if (count > 0) {  
  88.                 // 5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)  
  89.                 // 进入读模式  
  90.                 readBuf.flip();  
  91.                 // 6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据  
  92.                 byte[] bytes = new byte[readBuf.remaining()];  
  93.                 // 7 接收缓冲区数据  
  94.                 readBuf.get(bytes);  
  95.                 // 8 打印结果  
  96.                 String question = new String(bytes).trim();  
  97.                 System.out.println("Server : " + question);  
  98.                 // 9.再次向服务端,发送数据  
  99.                 Scanner scanner = new Scanner(System.in);  
  100.                 String strData = scanner.nextLine();  
  101.                 ByteBuffer buffer = ByteBuffer.wrap(strData.getBytes());  
  102.                 channel.write(buffer);  
  103.   
  104.             } else {  
  105.                 // 3 读取数据,发生异常  
  106.                 // 关闭连接,释放资源  
  107.                 // int count = sc.read(readBuf);  
  108.                 channel.close();  
  109.                 key.cancel();  
  110.                 return;  
  111.             }  
  112.   
  113.         } catch (IOException e) {  
  114.             e.printStackTrace();  
  115.         }  
  116.   
  117.     }  
  118. }  

3、客户端关闭后,服务端抛出异常信息,java.io.IOException: 远程主机强迫关闭了一个现有的连接

      3.0 异常的详细信息

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. java.io.IOException: 远程主机强迫关闭了一个现有的连接。  
  2.     at sun.nio.ch.SocketDispatcher.read0(Native Method)  
  3.     at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)  
  4.     at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)  
  5.     at sun.nio.ch.IOUtil.read(IOUtil.java:197)  
  6.     at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:384)  
  7.     at bhz.nio2.Server.read(Server.java:89)  
  8.     at bhz.nio2.Server.handleKey(Server.java:74)  
  9.     at bhz.nio2.Server.listen(Server.java:59)  
  10.     at bhz.nio2.Server.main(Server.java:165)  

      3.1 异常产生的原因

             在服务端的private void read(SelectionKey key)方法里面的, 读取数据int count = channel.read(readBuf);在客户端,关闭后会发生异常。

      3.2 解决的方法

            捕获异常,并进行处理。关闭连接,释放资源。

      3.2 异常的原因,以及解决方法



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值