nio通信代码实例

本文通过实例代码展示了Java NIO在服务端和客户端的使用,包括服务端启动、客户端连接及双方的控制台输出结果。
摘要由CSDN通过智能技术生成

服务端代码

package com.li.springboot.nio.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * nio通信服务端
 * @author 张宗海
 *
 */
public class NioServer {

	/**
	 * 接收
	 */
	public void accept(){
		try {
			//记录套接字通道事件
			Selector selector = Selector.open();
			//定义一个异步socket对象
			ServerSocketChannel ssc = ServerSocketChannel.open();
			ssc.configureBlocking(false);   //设置异步
			
			ServerSocket socket = ssc.socket();  //获取socket对象
			//绑定端口
			InetSocketAddress address = new InetSocketAddress(8083);
			socket.bind(address);
			//将事件注册selector对象内
			ssc.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("端口注册完毕!");
			
			while(true){
				//查询事件如果一个事件都没有就阻塞
	            selector.select();
	            //定义一个byte缓冲区来存储收发的数据
	            ByteBuffer echoBuffer=ByteBuffer.allocate(10);	            
	            //处理数据
	            this.accept(selector,echoBuffer);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 处理数据
	 * @param selector
	 * @param echoBuffer
	 */
	public void accept(Selector selector,ByteBuffer echoBuffer){
		SocketChannel sc;
		try{
			//此循环遍例所有产生的事件
	        for (SelectionKey key : selector.selectedKeys()){
	        	//如果产生的事件为接受客户端连接(当有客户端连接服务器的时候产生)
	        	if((key.readyOps()&SelectionKey.OP_ACCEPT)==SelectionKey.OP_ACCEPT){
	        		selector.selectedKeys().remove(key);
	                //定义一个服务器socket通道
	                ServerSocketChannel subssc=(ServerSocketChannel)key.channel();
	                //将临时socket对象实例化为接收到的客户端的socket
	                sc=subssc.accept();
	                //将客户端的socket设置为异步
	                sc.configureBlocking(false);
	                //将客户端的socket的读取事件注册到事件选择器中
	                sc.register(selector, SelectionKey.OP_READ);
	                //将本此事件从迭带器中删除
	                System.out.println("有新连接:"+sc);
	                
	            //如果产生的事件为读取数据(当已连接的客户端向服务器发送数据的时候产生)
	        	}else if((key.readyOps()&SelectionKey.OP_READ)==SelectionKey.OP_READ){
	        		//将本次事件删除
	                selector.selectedKeys().remove(key);
	                //临时socket对象实例化为产生本事件的socket
	                sc=(SocketChannel) key.channel();
	                //定义一个用于存储byte数据的流对象
	                ByteArrayOutputStream bos=new ByteArrayOutputStream();
	                //先将客户端的数据清空
	                echoBuffer.clear();
	                //a为读取到数据的长度
	                 try{
	                    //循环读取所有客户端数据到byte缓冲区中,当有数据的时候read函数返回数据长度
	                    //NIO会自动的将缓冲区一次容纳不下的自动分段
	                    int readInt=0;
	                    while((readInt=sc.read(echoBuffer)) > 0){
	                      //如果获得数据长度比缓冲区大小小的话
	                      if(readInt<echoBuffer.capacity()){
	                          //建立一个临时byte数组,将齐长度设为获取的数据的长度
	                          byte[] readByte=new byte[readInt];
	                          //循环向此临时数组中添加数据
	                          for(int i=0;i<readInt;i++){
	                            readByte[i]=echoBuffer.get(i);
	                          }
	                          //将此数据存入byte流中
	                          bos.write(readByte);
	                       }else{
	                          //将读取到的数据写入到byte流对象中
	                          bos.write(echoBuffer.array());
	                       }
	                       //将缓冲区清空,以便进行下一次存储数据
	                       echoBuffer.clear();
	                    }
	                    //当循环结束时byte流中已经存储了客户端发送的所有byte数据
	                    System.out.println("接收数据: "+new String(bos.toByteArray()));
	                 }catch(Exception e){
	                   //当客户端在读取数据操作执行之前断开连接会产生异常信息
	                   e.printStackTrace();
	                   //将本socket的事件在选择器中删除
	                   key.cancel();
	                   break;
	                 }
	                 //获取byte流对象的标准byte对象
	                 byte[] b=bos.toByteArray();
	                 //建立这个byte对象的ByteBuffer,并将数据存入
	                 ByteBuffer byteBuffer=ByteBuffer.allocate(b.length);
	                 byteBuffer.put(b);
	                 //向客户端写入收到的数据
	                 Write(byteBuffer,sc);
	                 //关闭客户端连接
	                 sc.close();
	                 //将本socket的事件在选择器中删除
	                 key.cancel();
	        	}
	        }
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			System.out.println("连接结束");
            System.out.println("=============================");
		}
	}
	
	/**
	 * 写数据
	 * @param echoBuffer
	 * @param sc
	 * @return
	 */
	public boolean Write(ByteBuffer echoBuffer,SocketChannel sc){
      //将缓冲区复位以便于进行其他读写操作
      echoBuffer.flip();
      try{
         //向客户端写入数据,数据为接受到数据
         sc.write(echoBuffer);
      }catch (IOException e){
         e.printStackTrace();
         return false;
      }
      System.out.println("返回数据: "+new String(echoBuffer.array()));
      return true;
	}
	
	/**
	 * 启动服务
	 * @param args
	 */
	public static void main(String[] args) {
		NioServer nioServer=new NioServer();
		nioServer.accept();
	}
}

客户端代码

package com.li.springboot.nio.client;

import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

/**
 * nio通信客户端
 * @author 张宗海
 *
 */
public class NioClient {

	/**
	 * 发送
	 * @param mssage
	 */
	public void send(String mssage){
		try {
			 //定义一个记录套接字通道事件的对象
	         Selector selector = Selector.open();
	         //定义一个服务器地址的对象
	         SocketAddress address = new InetSocketAddress("localhost",8083);
	         //定义异步客户端
	         SocketChannel client=SocketChannel.open(address);
	         //将客户端设定为异步
	         client.configureBlocking(false);
	         //在轮讯对象中注册此客户端的读取事件(就是当服务器向此客户端发送数据的时候)
	         client.register(selector, SelectionKey.OP_READ);
	         //定义用来存储发送数据的byte缓冲区
	         ByteBuffer sendbuffer=ByteBuffer.allocate(mssage.length());
	         //定义用于接收服务器返回的数据的缓冲区
	         ByteBuffer readBuffer=ByteBuffer.allocate(mssage.length());
	         //将数据put进缓冲区
	         sendbuffer.put(mssage.getBytes("utf-8"));
	         //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
	         sendbuffer.flip();
	         //向服务器发送数据
	         client.write(sendbuffer);
	         System.out.println("发送数据: "+new String(sendbuffer.array()));
	         
	         //利用循环来读取服务器发回的数据
	         while(true){
	            //如果客户端连接没有打开就退出循环
	            if(!client.isOpen()) break;
	            //此方法为查询是否有事件发生如果没有就阻塞,有的话返回事件数量
	            int shijian=selector.select();
	            //如果没有事件返回循环
	            if(shijian==0){
	               continue;
	            }
	            this.send(selector,readBuffer,client);
	         }
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 发送数据
	 * @param selector
	 * @param readBuffer
	 * @param client
	 */
	public void send(Selector selector,ByteBuffer readBuffer,SocketChannel client){
		try{
			//遍例所有的事件
	        for (SelectionKey key : selector.selectedKeys()){
	           //删除本次事件
	           selector.selectedKeys().remove(key);
	           //如果本事件的类型为read时,表示服务器向本客户端发送了数据
	           if (key.isReadable()){
	              //将临时客户端对象实例为本事件的socket对象
	        	  SocketChannel sc=(SocketChannel) key.channel();
	              //定义一个用于存储所有服务器发送过来的数据
	              ByteArrayOutputStream bos=new ByteArrayOutputStream();
	              //将缓冲区清空以备下次读取
	              readBuffer.clear();
	              //此循环从本事件的客户端对象读取服务器发送来的数据到缓冲区中
	              while(sc.read(readBuffer) > 0){
	                 //将本次读取的数据存到byte流中
	                 bos.write(readBuffer.array());   
	                 //将缓冲区清空以备下次读取
	                 readBuffer.clear();
	              }
	              //如果byte流中存有数据
	              if(bos.size()>0){
	                 //建立一个普通字节数组存取缓冲区的数据
	                 byte[] b=bos.toByteArray();
	                 
	                 System.out.println("接收数据: " + new String(b));
	                 //关闭客户端连接,此时服务器在read读取客户端信息的时候会返回-1
	                 client.close();
	                 System.out.println("连接关闭!");
	              }
	           }
	        }
		}catch(Exception e){
			e.printStackTrace();
		}		
	}
	
	/**
	 * 启动
	 * @param args
	 */
	public static void main(String[] args) {
		NioClient nioClient=new NioClient();
		nioClient.send("123456789");
	}
}

测试结果

服务端控制台输出
在这里插入图片描述
客户端控制台输出
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值