socket通讯常见模式分析

socket通讯常见模式分析(二)

  1. 非阻塞 I/O通信模型(java NIO

Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:

  由一个专门的线程来处理所有的 IO 事件,并负责分发。

  事件驱动机制:事件到的时候触发,而不是同步的去监视事件。

  线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

  阅读过一些资料之后,下面贴出我理解的java NIO的工作原理图:

  (注:每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。)

  Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:

  事件名对应值

  服务端接收客户端连接事件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时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:

具体实现代码如下:

Server端:

 

public class SocketReceiver{
    private int port;
//    private int poolSize;
    private int timeOut;
    private Selector selector=null;
//    private ExecutorService executor;
//    private ServerSocket serverSocket;
    private ServerSocketChannel serverChannel;
    private String hostAddress;
    private DealService dealService;
    private MessageDealService messageDealService;
    
    public void setPort(int port) {
        this.port = port;
    }
//    public void setPoolSize(int poolSize) {
//        this.poolSize = poolSize;
//    }
    public void setTimeOut(int timeOut) {
        this.timeOut = timeOut;
    }
    
    public void setHostAddress(String hostAddress) {
        this.hostAddress = hostAddress;
    }
    
    public void setMessageDealService(MessageDealService messageDealService) {
        this.messageDealService = messageDealService;
    }
    /**
     * spring加载
     */
    private void init(){
        System.out.println("init");
        String uuid = UUID.randomUUID().toString();
        try {
            //新建selector
            selector = Selector.open();
            //新建serversocket
            serverChannel = ServerSocketChannel.open();
            //绑定本地端口
            serverChannel.socket().bind(new InetSocketAddress(hostAddress, port));
            //设置非阻塞模式
            serverChannel.configureBlocking(false);
            //注册至soket服务器至selector
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            new SocketServerThread().start();
            LogManager.Transfer_Receiver.debug("UUID:"+uuid+"\nSocketReceiver init succ");
        } catch (IOException e) {
            LogManager.Transfer_Receiver.debug("UUID:"+uuid+"\nSocketReceiver init fail"+e.getStackTrace().toString());
            System.exit(0);
        }
    }
    
    public class SocketServerThread extends Thread{
        String uuid = UUID.randomUUID().toString();
        SocketReceiverHander hander = null;
        
        public SocketServerThread() {
            hander = new SocketReceiverHander(selector, serverChannel, dealService,uuid, messageDealService);
        }
        
        @Override
        public void run() {
            try {
                while(selector.select(timeOut)>0){
                    hander.listener();
                }
            } catch (IOException e) {
                LogManager.Transfer_Receiver.debug("UUID:"+uuid+"\nSocketServerThread executing fail"+e.getStackTrace().toString());
            }
        }
    }
} 

 Hander类:

 

public class SocketReceiverHander{
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private Charset charset=Charset.forName("UTF-8");
    private ByteBuffer buffer=ByteBuffer.allocate(MopfConfig.DATA_LENGTH);
    private DealService dealService;
    private MessageDealService messageDealService;
    private String uuid;
    
    public SocketReceiverHander(Selector selector,
            ServerSocketChannel serverChannel,
            DealService dealService,String uuid,MessageDealService messageDealService) {
        super();
        this.selector = selector;
        this.serverChannel = serverChannel;
        this.dealService = dealService;
        this.uuid = uuid;
        this.messageDealService = messageDealService;
    }
    
    public void listener() throws IOException{
        Iterator<SelectionKey> it=selector.selectedKeys().iterator();
        while(it.hasNext()) {
            SelectionKey sk=it.next();
            messageDealService.getRequest(sk, uuid, serverChannel, null, buffer, charset, selector, dealService);
            messageDealService.getResponse(sk, uuid, charset, dealService);
            //去除已经处理过的key
            it.remove();
        }
    }
}

 Client类:public class SocketSender {

    private InetSocketAddress address;
    private String host;
    private int port;
    private SocketChannel socketChannel;
    private ServerSocketChannel serverChannel; 
    private Selector selector;
    private DealService dealService;
    private MessageDealService messageDealService;
    
    public void setMessageDealService(MessageDealService messageDealService) {
        this.messageDealService = messageDealService;
    }
    public SocketSender(String host, int port) {
        address = new InetSocketAddress(host,port);
    }
    
    public SocketSender(){
        
    }
    
    public void send(String req){
        String uuid = UUID.randomUUID().toString();
        try {
            //打开socket通道并将其连接到远程地址。
            SocketChannel socketChannel = SocketChannel.open(address);
            //设置非阻塞模式
            socketChannel.configureBlocking(false);
            //设置缓冲区
            ByteBuffer byteBufer = ByteBuffer.allocate(MopfConfig.DATA_LENGTH);
            //将byte数组包装进缓冲区中
            socketChannel.write(ByteBuffer.wrap(req.getBytes()));
            dealService = new DealSenderService();
            LogManager.SendMessage_Info.debug("UUID:"+uuid+"\n请求报文"+req.toString());
            while(true){
                /**
                 * clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。 
                 * flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。 
                 * rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。 
                 */
                byteBufer.clear();
                int readBytes = socketChannel.read(byteBufer);
                if(readBytes>0){
                    byteBufer.flip();
                    LogManager.SendMessage_Info.debug("UUID:"+uuid+"\n响应报文"+new String(byteBufer.array(),0,readBytes));
                    dealService.deal(new String(byteBufer.array(),0,readBytes));
                    socketChannel.close();
                    break;
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        String hostname = "192.168.0.101";
        int port =10222;
        String req ="client=2342342342342";
        new SocketSender(hostname,port).send(req);
    }
} 

猜你喜欢

转载自whatisjava1234.iteye.com/blog/1736223