java nio SocketChannel 服务器端与多客户端 信息交互(聊天功能)

标签: java nio socketchannel | 发表时间:2013-01-05 15:51 | 作者:你爸是李刚
出处:http://www.blogjava.net
服务器端: 
Java代码 : 
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.net.ServerSocket;  
  4. import java.net.Socket;  
  5. import java.nio.ByteBuffer;  
  6. import java.nio.channels.SelectionKey;  
  7. import java.nio.channels.Selector;  
  8. import java.nio.channels.ServerSocketChannel;  
  9. import java.nio.channels.SocketChannel;  
  10. import java.nio.charset.Charset;  
  11. import java.util.HashMap;  
  12. import java.util.Map;  
  13. import java.util.Set;  
  14.   
  15. public class NIOSServer {  
  16.     private int port = 8888;  
  17.     //解码buffer  
  18.     private Charset cs = Charset.forName("gbk");  
  19.     /*接受数据缓冲区*/  
  20.     private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);  
  21.     /*发送数据缓冲区*/  
  22.     private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);  
  23.     /*映射客户端channel */  
  24.     private Map<String, SocketChannel> clientsMap = new HashMap<String, SocketChannel>();  
  25.     private static Selector selector;  
  26.       
  27.     public NIOSServer(int port){  
  28.         this.port = port;  
  29.         try {  
  30.             init();  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.     }  
  35.     private void init() throws IOException{  
  36.         /* 
  37.          *启动服务器端,配置为非阻塞,绑定端口,注册accept事件 
  38.          *ACCEPT事件:当服务端收到客户端连接请求时,触发该事件 
  39.          */  
  40.         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
  41.         serverSocketChannel.configureBlocking(false);  
  42.         ServerSocket serverSocket = serverSocketChannel.socket();  
  43.         serverSocket.bind(new InetSocketAddress(port));  
  44.         selector = Selector.open();  
  45.         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
  46.         System.out.println("server start on port:"+port);  
  47.     }  
  48.     /** 
  49.      * 服务器端轮询监听,select方法会一直阻塞直到有相关事件发生或超时 
  50.      */  
  51.     private void listen(){  
  52.         while (true) {  
  53.             try {  
  54.                 selector.select();//返回值为本次触发的事件数  
  55.                 Set<SelectionKey> selectionKeys = selector.selectedKeys();  
  56.                 for(SelectionKey key : selectionKeys){  
  57.                     handle(key);  
  58.                 }  
  59.                 selectionKeys.clear();//清除处理过的事件  
  60.             } catch (Exception e) {  
  61.                 e.printStackTrace();  
  62.                 break;  
  63.             }  
  64.               
  65.         }  
  66.     }  
  67.     /** 
  68.      * 处理不同的事件 
  69.     */  
  70.     private void handle(SelectionKey selectionKey) throws IOException {  
  71.         ServerSocketChannel server = null;  
  72.         SocketChannel client = null;  
  73.         String receiveText=null;  
  74.         int count=0;  
  75.         if (selectionKey.isAcceptable()) {  
  76.             /* 
  77.              * 客户端请求连接事件 
  78.              * serversocket为该客户端建立socket连接,将此socket注册READ事件,监听客户端输入 
  79.              * READ事件:当客户端发来数据,并已被服务器控制线程正确读取时,触发该事件 
  80.              */  
  81.             server = (ServerSocketChannel) selectionKey.channel();  
  82.             client = server.accept();  
  83.             client.configureBlocking(false);  
  84.             client.register(selector, SelectionKey.OP_READ);  
  85.         } else if (selectionKey.isReadable()) {  
  86.             /* 
  87.              * READ事件,收到客户端发送数据,读取数据后继续注册监听客户端 
  88.              */  
  89.             client = (SocketChannel) selectionKey.channel();  
  90.             rBuffer.clear();  
  91.             count = client.read(rBuffer);  
  92.             if (count > 0) {  
  93.                 rBuffer.flip();  
  94.                 receiveText = String.valueOf(cs.decode(rBuffer).array());  
  95.                 System.out.println(client.toString()+":"+receiveText);  
  96.                 dispatch(client, receiveText);  
  97.                 client = (SocketChannel) selectionKey.channel();  
  98.                 client.register(selector, SelectionKey.OP_READ);  
  99.             }  
  100.         }   
  101.     }  
  102.       
  103.     /** 
  104.      * 把当前客户端信息 推送到其他客户端 
  105.      */  
  106.     private void dispatch(SocketChannel client,String info) throws IOException{  
  107.         Socket s = client.socket();  
  108.         String name = "["+s.getInetAddress().toString().substring(1)+":"+Integer.toHexString(client.hashCode())+"]";  
  109.         if(!clientsMap.isEmpty()){  
  110.             for(Map.Entry<String, SocketChannel> entry : clientsMap.entrySet()){  
  111.                 SocketChannel temp = entry.getValue();  
  112.                 if(!client.equals(temp)){  
  113.                     sBuffer.clear();  
  114.                     sBuffer.put((name+":"+info).getBytes());  
  115.                     sBuffer.flip();  
  116.                     //输出到通道  
  117.                     temp.write(sBuffer);  
  118.                 }  
  119.             }  
  120.         }  
  121.         clientsMap.put(name, client);  
  122.     }  
  123.     public static void main(String[] args) throws IOException {  
  124.         NIOSServer server = new NIOSServer(7777);  
  125.         server.listen();  
  126.     }  
  127. }  
客户端,可运行启动多个: 
Java代码:  
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.ByteBuffer;  
  6. import java.nio.channels.SelectionKey;  
  7. import java.nio.channels.Selector;  
  8. import java.nio.channels.SocketChannel;  
  9. import java.util.Date;  
  10. import java.util.Set;  
  11.   
  12. public class NIOClient {  
  13.     /*发送数据缓冲区*/  
  14.     private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);  
  15.     /*接受数据缓冲区*/  
  16.     private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);  
  17.     /*服务器端地址*/  
  18.     private InetSocketAddress SERVER;  
  19.     private static Selector selector;  
  20.     private static SocketChannel client;  
  21.     private static String receiveText;  
  22.     private static String sendText;  
  23.     private static int count=0;  
  24.       
  25.     public NIOClient(int port){  
  26.         SERVER = new InetSocketAddress("localhost", port);  
  27.         init();  
  28.     }  
  29.     public void init(){  
  30.         try {  
  31.            /* 
  32.              * 客户端向服务器端发起建立连接请求 
  33.              */  
  34.             SocketChannel socketChannel = SocketChannel.open();  
  35.             socketChannel.configureBlocking(false);  
  36.             selector = Selector.open();  
  37.             socketChannel.register(selector, SelectionKey.OP_CONNECT);  
  38.             socketChannel.connect(SERVER);  
  39.             /* 
  40.              * 轮询监听客户端上注册事件的发生 
  41.              */  
  42.             while (true) {  
  43.                 selector.select();  
  44.                 Set<SelectionKey> keySet = selector.selectedKeys();  
  45.                 for(final SelectionKey key : keySet){  
  46.                     handle(key);  
  47.                 };  
  48.                 keySet.clear();  
  49.             }  
  50.         } catch (Exception e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.     }  
  54.     public static void main(String[] args) throws IOException {  
  55.         NIOClient client = new NIOClient(7777);  
  56.     }  
  57.       
  58.     private void handle(SelectionKey selectionKey) throws IOException{  
  59.         if (selectionKey.isConnectable()) {  
  60.             /* 
  61.              * 连接建立事件,已成功连接至服务器 
  62.              */  
  63.             client = (SocketChannel) selectionKey.channel();  
  64.             if (client.isConnectionPending()) {  
  65.                 client.finishConnect();  
  66.                 System.out.println("connect success !");  
  67.                 sBuffer.clear();  
  68.                 sBuffer.put((new Date().toLocaleString()+" connected!").getBytes());  
  69.                 sBuffer.flip();  
  70.                 client.write(sBuffer);//发送信息至服务器  
  71.                 /* 原文来自 站长网
  72.                  * 启动线程一直监听客户端输入,有信心输入则发往服务器端 
  73.                  * 因为输入流是阻塞的,所以单独线程监听 
  74.                  */  
  75.                 new Thread(){  
  76.                     @Override  
  77.                     public void run() {  
  78.                         while(true){  
  79.                             try {  
  80.                                 sBuffer.clear();  
  81.                                 InputStreamReader input = new InputStreamReader(System.in);  
  82.                                 BufferedReader br = new BufferedReader(input);  
  83.                                 sendText = br.readLine();  
  84.                                 /* 
  85.                                  * 未注册WRITE事件,因为大部分时间channel都是可以写的 
  86.                                  */  
  87.                                 sBuffer.put(sendText.getBytes());  
  88.                                 sBuffer.flip();  
  89.                                 client.write(sBuffer);  
  90.                             } catch (IOException e) {  
  91.                                 e.printStackTrace();  
  92.                                 break;  
  93.                             }  
  94.                       }  
  95.                     };  
  96.                 }.start();  
  97.             }  
  98.             //注册读事件  
  99.             client.register(selector, SelectionKey.OP_READ);  
  100.         } else if (selectionKey.isReadable()) {  
  101.             /* 
  102.              * 读事件触发 
  103.              * 有从服务器端发送过来的信息,读取输出到屏幕上后,继续注册读事件 
  104.              * 监听服务器端发送信息 
  105.              */  
  106.             client = (SocketChannel) selectionKey.channel();  
  107.             rBuffer.clear();  
  108.             count=client.read(rBuffer);  
  109.             if(count>0){  
  110.                 receiveText = new String( rBuffer.array(),0,count);  
  111.                 System.out.println(receiveText);  
  112.                 client = (SocketChannel) selectionKey.channel();  
  113.                 client.register(selector, SelectionKey.OP_READ);  
  114.             }  
  115.         }   
  116.     }  
  117. }  原文来自java教程网 http://www.software8.co/wzjs/java/   欢迎java爱好者前来投稿


本文链接

相关 [java nio socketchannel] 推荐:

java nio SocketChannel 服务器端与多客户端 信息交互(聊天功能)

- - BlogJava_首页
    //解码buffer  .     /*接受数据缓冲区*/  .     /*发送数据缓冲区*/  .     /*映射客户端channel */  .          *启动服务器端,配置为非阻塞,绑定端口,注册accept事件 .          *ACCEPT事件:当服务端收到客户端连接请求时,触发该事件 .

Java BIO、NIO、AIO

- - zzm
先来个例子理解一下概念,以银行取款为例:. 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API).

java nio和io的比较

- - 互联网 - ITeye博客
第一部分:简单介绍NIO.     服务器在合理时间内处理大量客户机的请求的能力取决于服务器使用I/O流的效率,同时为成百上千的客户提供服务的服务器必须能并发的使用I/O服务.     用Java语言写的服务器,由于其线程与客户机之比几乎是一比一,因而易受到大量线程开销的影响,其结果是即导致性能问题,又缺乏伸缩性.

Java NIO服务器实例

- - ImportNew
我一直想学习如何用Java写一个 非阻塞IO服务器,但无法从网上找到一个满足要求的服务器. 我找到了 这个示例,但仍然没能解决我的问题. 还可以选择 Apache MINA框架. 但我的要求相对简单,MINA对我来说还稍微有点复杂. 所以在MINA和一些教程(参见 这篇和 这篇)的帮助下,我自己写了一个非阻塞IO服务器.

Java NIO 系列教程

- - 编程语言 - ITeye博客
Java NIO提供了与标准IO不同的IO工作方式:. Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中. Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情.

Java NIO编程的技巧和陷阱

- 小丑鱼 - 淘宝JAVA中间件团队博客
去年做的分享,一直上传slideshare失败,今天又试了下,成功了. 这个主题主要介绍Java NIO编程的技巧和陷阱,解读了一些NIO框架的源码,以及编写高性能NIO网络框架所需要注意的技巧和缺陷. 去年写了篇blog提供了pdf版本的下载,看这里.

Grizzly 2.2发布,开源Java NIO框架

- - ITeye资讯频道
Grizzly框架近日 发布了2.2版本,该版本带来了相当多新特性与改进,而且加入了最新WebSocket规范的实现. Grizzly是一个应用程序框架,专门用于解决编写成千上万用户访问服务器时候产生的各种问题. Grizzly框架诞生于GlassFish项目,能够帮助开发人员利用Java NIO API构建可扩展、高性能、健壮的服务器,编写出可伸缩的服务器端应用.

攻破JAVA NIO技术壁垒

- - CSDN博客推荐文章
现在使用NIO的场景越来越多,很多网上的技术框架或多或少的使用NIO技术,譬如Tomcat,Jetty. 学习和掌握NIO技术已经不是一个JAVA攻城狮的加分技能,而是一个必备技能. 再者,现在互联网的面试中上点level的都会涉及一下NIO或者AIO的问题(AIO下次再讲述,本篇主要讲述NIO),掌握好NIO也能帮助你获得一份较好的offer.

NIO与传统IO的区别

- - CSDN博客推荐文章
传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大. 使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数量线程的线程池,甚至一个线程来为任意数量的连接服务. 由于线程数量小于连接数量,所以每个线程进行IO操作时就不能阻塞,如果阻塞的话,有些连接就得不到处理,NIO提供了这种非阻塞的能力.

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).