java网络编程-NIO编程

NIO编程

传统的TCP和UDP通讯:Blocking I/O(阻塞,需要等待慢的一方,效率不高)

Non-Blocking I/O

  • 提供非阻塞通讯等方式
  • 避免同步I/O通讯效率过低
  • 一个线程可以管理多个连接
  • 减少线程多的压力

三个重要的类

  • Buffer缓存区
  • Channel通道
  • Selector多路选择器

Buffer缓存区,一个可以读写的内存区域

  • ByteBuffer, CharBuffer, DoubleBuffer, IntBuffer, LongBuffer, ShortBuffer(StringBuffer 不是Buffer缓冲区)
  • capacity容量,position读写位置
  • limit界限,mark标记,用于重复一个读/写操作

Channel通道

  • 全双工的,支持读/写(而Stream流是单向的)
  • 支持异步读写
  • 和Buffer配合,提高效率
  • ServerSocketChannel 服务器 TCP Socket接入通道,接收客户端
  • SocketChannel TCP Socket通道,可支持阻塞/非阻塞通讯
  • DatagramChannel UDP 通道
  • FileChannel 文件通道

Selector多路选择器

  • 每隔一段时间,不断轮询注册在其上的Channel
  • 如果有一个Channel有接入、读、写操作,就会被轮询出来
  • 根据SelectionKey可以获取相应的Channel,然后后续IO操作
  • 避免过多的线程
  • SelectionKey四种类型
    • OP_CONNECT
    • OP_ACCEPT
    • OP_READ
    • OP_WRITE

在这里插入图片描述

NIO Server 程序

package com.lihuan.network.demo05;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioServer {
    public static void main(String[] args) {
        int port = 8001;
        Selector selector = null;
        ServerSocketChannel serverChannel = null;

        try {
            //产生出一个多路选择器
            selector = Selector.open();
            //建立通道,等待客户端连接
            serverChannel = ServerSocketChannel.open();
            //配置为非阻塞模式
            serverChannel.configureBlocking(false);
            //驻守在8001端口,绑定端口
            serverChannel.socket().bind(new InetSocketAddress(port), 1024);
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务器在8001端口等待...");
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (true){
            try {
                //轮询
                selector.select(1000);
                //获取有响应的selectionKey
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()){
                    key = it.next();
                    it.remove();
                    try{
                        handleInput(selector, key);
                    }catch (Exception e){
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null)
                                key.channel().close();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void handleInput(Selector selector, SelectionKey key) throws IOException {
        if(key.isValid()){
            //处理新接入的请求消息
            if(key.isAcceptable()){
                //接收一个新的连接
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);
            }
            if(key.isReadable()){
                SocketChannel sc = (SocketChannel) key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if(readBytes > 0){
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String request = new String(bytes, "UTF-8");
                    System.out.println("client said: " + request);
                    String response = request + " 666";
                    doWrite(sc, response);
                }else if(readBytes < 0){
                    key.cancel();
                    sc.close();
                }else ;
            }
        }
    }

    private static void doWrite(SocketChannel sc, String response) throws IOException {
        if(response != null && response.trim().length() > 0){
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            sc.write(writeBuffer);
        }
    }
}

NIO Client 程序

package com.lihuan.network.demo05;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class NioClient {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 8001;

        Selector selector = null;
        SocketChannel socketChannel = null;

        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            //如果连接成功,则注册到多路复用器上,发送请求消息,读应答
            if(socketChannel.connect(new InetSocketAddress(host, port))){
                socketChannel.register(selector, SelectionKey.OP_READ);
                doWrite(socketChannel);
            }else{
                socketChannel.register(selector, SelectionKey.OP_CONNECT);
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (true){
            try {
                //轮询
                selector.select(1000);
                //获取有响应的selectionKey
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()){
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(selector, key);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        if (key != null){
                            key.cancel();
                            if(key.channel() != null){
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void doWrite(SocketChannel socketChannel) throws IOException {
        byte[] str = UUID.randomUUID().toString().getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);
        writeBuffer.put(str);
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
    }

    private static void handleInput(Selector selector, SelectionKey key) throws IOException, InterruptedException {
        if(key.isValid()){
            //判断是否连接成功
            SocketChannel sc = (SocketChannel) key.channel();
            if(key.isConnectable()){
                if(sc.finishConnect()){
                    sc.register(selector,SelectionKey.OP_READ);
                }
            }
            if(key.isReadable()){
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if(readBytes > 0){
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("server said: " + body);
                }else if(readBytes < 0){
                    key.cancel();
                    sc.close();
                }
            }
            Thread.sleep(3000);
            doWrite(sc);
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值