使用Java NIO实现异步的socket通信

来源:互联网 发布:大麦盒子dm1016网络锁 编辑:程序博客网 时间:2024/05/20 18:41

关于NIO的介绍,可以参考这一组文章,http://tutorials.jenkov.com/java-nio/index.html,这组教程写得比较好。

关于用旧IO实现的Socket通信,可以参考这一组文章:http://blog.csdn.net/kongxx/article/category/1077912


最近在学习NIO与Socket的时候,发现网上鲜见真正实现异步的socket的例子,大都仅仅使用Selector来管理服务端的ServerSocketChannel,事实上没有做到真正的异步,还是按部就班地接受连接,收发信息,然后释放连接。

更常规的方法是服务端使用两个线程,一个线程采用阻塞的方法来accept客户端,另一个线程用selector来异步地处理所有连接。

     

这里使用Selector来实现异步IO的一个简单实例。服务端在主线程中通过无限循环阻塞线程来监听将传入的连接、再启动另一个线程通过Selector来异步地处理已连接的通道。客户端启动100个线程来模拟100个用户同时连入服务端,从输出的结果可以看出客户端与服务端的通信是异步地完成的。

先贴出一个工具类,里面有收发消息的两个方法。


import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;public class SendAndReceiveUtil {public static String receiveData(SocketChannel channel) {// TODO Auto-generated method stub// TODO Auto-generated method stubByteBuffer bb = ByteBuffer.allocate(1024);StringBuilder msg = new StringBuilder();Charset charset = Charset.forName("UTF-8");  CharsetDecoder decoder = charset.newDecoder();try {while( (channel.read(bb) ) > 0 ){bb.flip();msg.append(decoder.decode(bb).toString());//System.out.println(msg.toString());bb.clear();}return msg.toString();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}public static  void sendData(SocketChannel socketChannel, String msg) {// TODO Auto-generated method stubtry {socketChannel.write(ByteBuffer.wrap(msg.getBytes()));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
然后贴出服务端的代码。


import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class ChatServer {/** * @param args * @throws IOException  */public static void main(String[] args) throws IOException {// TODO Auto-generated method stubfinal Selector selector = Selector.open();;ServerSocketChannel ssc = ServerSocketChannel.open();try{// Bind the server socket to the local host and port ssc.socket().bind(new InetSocketAddress("localhost", 8080));//start a thread to handle the wirte and readstartWRThread(selector);//block the main thread to accept clientwhile(true){  // will block the threadSocketChannel sc = ssc.accept();//Get the server socket and set to non blocking mode  sc.configureBlocking(false);sc.register(selector, SelectionKey.OP_READ);}}finally{selector.close();ssc.close();}}private static void startWRThread(final Selector selector) {// TODO Auto-generated method stubnew Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while(true){while(selector.selectNow() > 0){Iterator<SelectionKey> it = selector.selectedKeys().iterator();//// Walk through the ready keys collection and process date requests.while(it.hasNext()){SelectionKey readyKey = it.next();if(readyKey.isReadable()){SocketChannel sc = (SocketChannel) readyKey.channel(); String msg = SendAndReceiveUtil.receiveData(sc);           if(msg != null && !msg.equals("")) {         if(msg.equals("bye")){         System.out.println("Get a msg : " + msg);         sc.close();         }else{         System.out.println("Get a msg : " + msg);         SendAndReceiveUtil.sendData(sc,"Server have got you msg:"+ msg);         sc.shutdownOutput();         }                 }          it.remove(); }//execute((ServerSocketChannel) readyKey.channel());}}}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}).start();}}

最后贴出客户端代码,客户端启动100个线程模拟100个client来连接服务端,并发送线程id到服务端,随后发送”bye“来结束连接。


import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.channels.SocketChannel;public class ChatClient {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubfor (int i = 1; i < 100; i++) {              final int idx = i;              new Thread(new MyRunnable(idx)).start();          }  }private static class MyRunnable implements Runnable {private final int idx;              private MyRunnable(int idx) {              this.idx = idx;          }  @Overridepublic void run() {// TODO Auto-generated method stubSocketChannel socketChannel = null;              try {                  socketChannel = SocketChannel.open();                  SocketAddress socketAddress = new InetSocketAddress("localhost", 8080);                                  socketChannel.connect(socketAddress);                                  SendAndReceiveUtil.sendData(socketChannel, "My id is " + idx);                                    String msg = SendAndReceiveUtil.receiveData(socketChannel);                  if(msg != null) System.out.println("The server reply:"+msg);                                SendAndReceiveUtil.sendData(socketChannel,"bye");                      } catch (Exception ex) {                  ex.printStackTrace();            } finally {                  try {                              socketChannel.close();                  } catch(Exception ex) {                ex.printStackTrace();                }              }  }}}


0 0