java NIO通信(IO、AIO)

  一.socket编程

    socket是最基础的网络通信服务,其他的可网络通信服务基本都是封装了socket通信,比如http通信,nio通信.作为最基本的通信协议,有两个要素,Socket,ServerSocket.比如:

    服务器端   

    //打开ServerSocket,port自定义,int类型,一般大于1024 
      ServerSocket server = new ServerSocket(port);  
    //server尝试接收其他Socket的连接请求,这里的accept方法是阻塞式的,即没有连接会在这等待直至连接到来
      Socket socket1 = server.accept();

    客户端

      //打开socket连接,其中server ip为服务器<em>ip,若是本机,则可以填入127.0.0.1,port为对应端口</em>
      Socket socket2 = new Socket("server ip",port)

    这样当服务器打开服务,客户端连接时会建立连接,而当服务器接收到连接后,可以使用如下方法获取输入流输出流   

      socket1.getInputStream();
      socket1.getOutputStream();

                同样客户端也可以通过以上方式获得输入输出流 
    socket2.getInputStream();
    socket2.getOutputStream();

              使用如下方法发送与接收

      Writer writer = new OutputStreamWriter(socketi.getOutputStream());
      writer.write("hello , I am hero"); //发送信息
      Reader reader = new InputStreamReader(socketi.getInputStream()); 
    char data[] = new char[1024];
      reader.read(data); //接收信息
     下面给一个完整的socket服务器与客户端程序

//服务器程序
public class Server {
	
	static Socket socket = null;
	static DataInputStream input = null;
	static DataOutputStream output = null;
	
	public static void main(String args[]) throws IOException {  
	      ServerSocket server = new ServerSocket(8888);  
	      while(true){
	    	  socket = server.accept();  
	    	  System.out.print("wait for data\n");
	    	  input = new DataInputStream(socket.getInputStream());  
                String ret = input.readUTF(); 
                // 处理客户端数据    
          System.out.println("receive content:" + ret);
	    	  if(ret.equals("exit"))
	    		 break;
          //返回信息
           System.out.print("write data back\n");
	    	  output=new DataOutputStream(socket.getOutputStream());
	    	  output.writeUTF("hello world");
	      }
	      input.close();
	      output.close();
	      socket.close();  
	      server.close();  
	   }  
}

//客户端程序
public class client{
    static Socket socket =  null;
    public static void main(String args[]) throws IOException{
        try {
            socket    =  new Socket("127.0.0.1",8888);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //向服务器端发送数据    
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());    
        System.out.print("请输入:  ");    
        String str = new BufferedReader(new InputStreamReader(System.in)).readLine();    
        out.writeUTF(str); 
        //接收服务器消息
        DataInputStream input = new DataInputStream(socket.getInputStream());  
        String ret = input.readUTF(); 
        System.out.println("接收消息为:"+ret);
    }
}
 

       二.IO、NIO、AIO区别

                NIO即New IO的简称,是相对于IO而言的,并不是Non blocking IO的简称。但NIO确实是非阻塞方式的IO,AIO也是非阻塞方式的IO。

                应用程序在处理IO的时候分为一般分为等待数据准备、数据从内核拷贝到应用程序。也就是这两个阶段才产生了 不同的IO方式。

                首先用一个场景来描述IO、NIO、AIO的区别。以快递员为例,此时快递员相当于一个线程。IO方式好比一个快递员一次只传送一件货物,送完后返回继续;NIO则是一个分发机制,一个收发站聚集所有此时到来的货物,由负责人进行统一管理,同样也是一个快递员负责一件货物,不同的是负责人会对货物进行筛选,确定一个用户的所有货物到齐后将货物交由快递员一起配送,在货物未到齐前,快递员只需处理当前已完全到达的货物。AIO采用的是回调机制,快递员不用担心货物是否到齐,收发站会确认货物到齐,并通知快递员揽件。

                NIO在调用系统IO命令后便返回,但是需要不停的询问系统IO是否完成;而AIO在调用系统IO命令后大可去喝杯茶,等待系统告知说IO已完成,然后便去处理。

                NIO除了是非阻塞IO,也使用了IO多路复用(IO multiplexing)。NIO的select主线程会轮询请求的所有request,将request分为两个队列存储,一个是IO已准备好的队列,一个是IO未准备好的队列;一旦发现IO已经准备好的队列不为空,则接受该队列的所有请求(此时也是一个线程一个请求)。一个线程控制着所有的IO请求便是IO多路复用了。

                从以上可以看出,NIO并不一定会比IO好很多,NIO的适用场景一般是连接数较多但是连接时间较短的场景(比如聊天工具)。

                更多关于IO的内容请查看 http://blog.csdn.net/zhangzeyuaaa/article/details/42609723


       三.NIO通信相关内容

    NIO是将socket封装的一类非阻塞方式的通信模式.何为非阻塞通信,非阻塞即线程在处理一个IO时,若IO未准备好,则线程不会停留在这等待IO.先谈谈NIO的四个要素ServerSocketChannel,SocketChannel,Selector,SelectionKey.这四个东西是如何协调工作呢?

    服务器端

    //服务器初始化
       private ServerSocketChannel serverSocketChannel = null;  
    private Selector            selector            = null;  

    selector = Selector.open();  
       serverSocketChannel = ServerSocketChannel.open();  
       serverSocketChannel.configureBlocking(false);    //设置成非阻塞式
       serverSocketChannel.socket().bind(new InetSocketAddress(8888));  
       serverSocketChannel.register(selector,  SelectionKey.OP_ACCEPT);   //通道设置权限
       System.out.println("服务器启动正常!");  

       //非阻塞式轮询每个selector获得的连接,对每个连接进行处理
       while(true){
           selector.select();
           Iterator it = this.selector.selectedKeys().iterator();
           while(it.hasNext()){
               SelectionKey key = (SelectionKey)it.next();
               it.remove();
               if(key.isAcceptable()){                    <span>
                   ServerSocketChannel server = (ServerSocketChannel)key.channel();</span>
                   SocketChannel channel = server.accept(); 
           channel.configureBlocking(false); 
           channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
                   channel.register(this>.selector, SelectionKey.OP_READ);
               }else if(key.isReadable()){
                   doRead();
               }
           }
       }

    我还是先上传一份可以直接运行的代码

//服务器端程序
public class SimpleNIOServer {
	private static ServerSocketChannel serverSocketChannel = null;  
     private static Selector            selector            = null;  
	
	public static void main(String args[]) throws IOException{
	       selector = Selector.open();  
	       serverSocketChannel = ServerSocketChannel.open();  
	       serverSocketChannel.configureBlocking(false);  
	       serverSocketChannel.socket().bind(new InetSocketAddress(8888));  
	       serverSocketChannel.register(selector,  SelectionKey.OP_ACCEPT);   
	       System.out.println("服务器启动正常!");  

	       while(true){
	           selector.select();
	           Iterator<SelectionKey> it = selector.selectedKeys().iterator();
	           
	           while(it.hasNext()){
	               SelectionKey key = (SelectionKey)it.next();
	               it.remove();
	               if(key.isAcceptable()){                                       
	            	   ServerSocketChannel server = (ServerSocketChannel)key.channel();
	                   SocketChannel channel = server.accept();                    
	                   channel.configureBlocking(false);                    
	                channel.write(ByteBuffer.wrap(new String("Hello I am server").getBytes()));
	                   channel.register(selector, SelectionKey.OP_READ);
	               }else if(key.isReadable()){
	            	    //获取消息
	            	    SocketChannel channel1 = (SocketChannel) key.channel();  
	                    ByteBuffer buffer = ByteBuffer.allocate(1024);  
	                    channel1.read(buffer);  
	                    byte[] data = buffer.array();  
	                    String msg = new String(data).trim();  
	                    System.out.println("server get :"+msg);  
	                    //发送消息
	                    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());  
	                    channel1.write(outBuffer);
	               }
	           }
	       }
	}

}


//客户端程序
public class SimpleNIOClient {
    
    private static Selector selector; 
    
    public static void main(String args[]) throws IOException{
        SocketChannel channel = SocketChannel.open();  
        channel.configureBlocking(false);  
        selector = Selector.open();  
        channel.connect(new InetSocketAddress("127.0.0.1",8888));   
        channel.register(selector, SelectionKey.OP_CONNECT); 

        while (true) {  
            selector.select();  
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();  
            
            while (ite.hasNext()) {  
                SelectionKey key = (SelectionKey) ite.next();  
                ite.remove();  
                
                if (key.isConnectable()) {  
                    SocketChannel channel1 = (SocketChannel) key.channel();  
                    // 如果正在连接,则完成连接  
                    if(channel1.isConnectionPending()){  
                        channel1.finishConnect();  
                    }  
                    //设置成非阻塞  
                    channel1.configureBlocking(false);  
                    //给服务端发送信息哦  
                    channel1.write(ByteBuffer.wrap(new String("hello I am client").getBytes()));  
                    //给通道设置读的权限。  
                    channel1.register(selector, SelectionKey.OP_READ);  
                       
                } else if (key.isReadable()) {  
                    SocketChannel channel1 = (SocketChannel) key.channel();  
                    ByteBuffer buffer = ByteBuffer.allocate(1024);  
                    channel1.read(buffer);  
                    byte[] data = buffer.array();  
                    String msg = new String(data).trim();  
                    System.out.println("client get :"+msg);  
                    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());  
                    channel1.write(outBuffer);
                }  
            }  
        } 
    }
}

注:该代码参考了部分http://weixiaolu.iteye.com/blog/1479656中代码
 

    从代码中可以看出,NIO对事件依赖度很大,在绑定通道后需要注册事件类型,而NIO有比较常用的四种事件

事件名对应值
服务端接收客户端连接事件SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件SelectionKey.OP_CONNECT(8)
读事件SelectionKey.OP_READ(1)
写事件SelectionKey.OP_WRITE(4)

    服务端和客户端各自维护一个管理通道的对象,即selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直轮询访问selector.(http://weixiaolu.iteye.com/blog/1479656)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值