Java 网络编程基础

1 篇文章 0 订阅

目录

一、同步阻塞IO

示例代码

二、同步非阻塞IO

NIO 服务端代码示例代码

NIO多路复用服务端示例代码

三、Reactor模式

Reactor示例代码

四、AIO

AIO示例代码


IO模型就是说用什么样的通道进行数据的发送和接收,首先要明确一点:IO是操作系统与其他网络进行数据交互,JDK底层并没有实现IO,而是对操作系统内核函数做的一个封装,IO代码进入底层其实都是native形式的。Java共支持3种网络编程IO模式:BIO,NIO,AIO

一、同步阻塞IO

        同步阻塞IO也成为BIO(Blocking IO),是指用户线程发起IO,需要等待系统内核IO操作彻底完成才能返回到用户线程继续执行;在IO操作过程中,用户线程处于阻塞状态;这就是为什么成为阻塞IO的原因.

 

BIO缺点   

  1. 如果BIO使用单线程接受连接,则会阻塞其他连接,效率较低。如果使用多线程虽然减弱了单线程带来的影响,但当有大并发进来时,会导致服务器线程太多,压力太大而崩溃。
  2. 多线程也会有线程切换带来的消耗,高并发场景下性能差

示例代码

/**
 * BIO代码中连接事件和读写数据事件都是阻塞的,所以这种模式的缺点非常的明显
 *
 * 1、如果我们连接完成以后,不做读写数据操作会导致线程阻塞,浪费资源
 *
 * 2、如果没来一个连接我们都需要启动一个线程处理,那么会导致服务器线程太多,压力太大,比如C10K;
 *
 */
public class SocketServer {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true) {
            System.out.println("wait....");
            //BIO 阻塞等待链接
            Socket accept = serverSocket.accept();

            System.out.println("handle request");

            new Thread(() -> {
                try {
                    handleRequest(accept);
                } catch (Exception e) {

                }
            }).start();
        }
    }

    private static void handleRequest(Socket clientSocket) throws Exception {
        byte[] bytes = new byte[1024];
        System.out.println("prepare read.....");
        int read = clientSocket.getInputStream().read(bytes);
        System.out.println("read end...");
        if (read != -1) {
            System.out.println("receive client request is " + new String(bytes, 0, read));
        }
        clientSocket.getOutputStream().write("hello".getBytes());
        clientSocket.getOutputStream().flush();
    }
}


public class SocketClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 9000);
        socket.getOutputStream().write("hello".getBytes());
        socket.getOutputStream().flush();
        System.out.println("send request end ..");
        byte[] bytes = new byte[1024];
        int read = socket.getInputStream().read(bytes);
        System.out.println(" response is " + new String(bytes));
        socket.close();
    }
}

二、同步非阻塞IO

为了解决BIO问题,在JDK1.4引入同步非阻塞IO也称为NIO(Non-Blocking IO),是指用户线程发起IO操作时不会处于阻塞状态。

NIO的多路复用底层主要用的是Linux 内核函数(select,poll,epoll)来实现的。windows不支持epoll实现,windows底层是基于winsock2的select函数实现的(不开源)。三种内核模型的区别如下所示!

selectpollepoll(jdk 1.5及以上)
操作方式遍历 遍历回调
底层实现数组数组链表哈希表
IO效率每次调用都进行线性遍历,时间复杂度为O(n)  每次调用都进行线性遍历,时间复杂度为O(n) 事件通知方式,每当有IO事件就绪,系统注册的回调函数就会被调用,时间复杂度O(1)
最大连接有上限(1024个)无上限无上限

             
        通过下文示例代码可以发现,传统NIO会一直轮询占用大量CPU资源,在高并发场景下性能不够好。

NIO 服务端代码示例代码

public class NioServer {
    static List<SocketChannel> channelList=new ArrayList<>();

    public static void main(String[] args) throws IOException {
        //创建NIO
        ServerSocketChannel serverChannel=ServerSocketChannel.open();
        //绑定端口
        serverChannel.bind(new InetSocketAddress(9000));
        //设置非阻塞
        serverChannel.configureBlocking(false);

        System.out.println("server  start ....");
        while(true){
            SocketChannel socketChannel = serverChannel.accept();
            if(socketChannel !=null){
                System.out.println("链接成功。。。");
                socketChannel.configureBlocking(false);
                channelList.add(socketChannel);
            }
            Iterator<SocketChannel> iterator = channelList.iterator();
            while(iterator.hasNext()){
                SocketChannel sc = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                int read = sc.read(byteBuffer);
                if (read>0){
                    System.out.println("receive request is "+new String(byteBuffer.array()));
                }else if(read ==-1){
                    iterator.remove();
                    System.out.println(" close link");
                }

            }
        }
    }
}

NIO多路复用服务端示例代码

public class NioSelectorServer {

    public static void main(String[] args) throws Exception {
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9000));
        serverSocketChannel.configureBlocking(false);
        //创建多路复用
        Selector selector = Selector.open();
        //将channel 注册到多路复用器
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("server start...");
        while(true){
            // 阻塞等待需要处理到事件
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while(selectionKeyIterator.hasNext()){
                SelectionKey selectionKey = selectionKeyIterator.next();
                if(selectionKey.isAcceptable()){
                    ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector,SelectionKey.OP_READ);
                    System.out.println("client connect success");
                }else if(selectionKey.isReadable()){
                    SocketChannel socketChannel=(SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int read = socketChannel.read(byteBuffer);
                    if (read>0){
                        System.out.println("receive request is "+new String(byteBuffer.array()));
                    }else if(read ==-1){
                        socketChannel.close();
                        System.out.println(" close link");
                    }
                }
                selectionKeyIterator.remove();
            }

        }
    }
}

三、Reactor模式

        为了解决传统BIO轮询导致的性能问题引入了Reactor模式,底层使用epoll模式

Reactor示例代码


public class ReactorServer {

    public static void main(String[] args) throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9000));
        serverSocketChannel.configureBlocking(false);
        //创建多路复用
        Selector selector = Selector.open();
        //将channel 注册到多路复用器
        SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        selectionKey.attach(new AcceptHandler(selector, serverSocketChannel));
        System.out.println("server start...");
        while (true) {
            // 阻塞等待需要处理到事件
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while (selectionKeyIterator.hasNext()) {
                SelectionKey sk = selectionKeyIterator.next();
                System.out.println(" acceptable=" + sk.isAcceptable() + ", readable=" + sk.isReadable());
                Runnable runnable = (Runnable) sk.attachment();
                runnable.run();
                selectionKeyIterator.remove();
            }

        }
    }

    private static class AcceptHandler implements Runnable {

        private final Selector selector;
        private ServerSocketChannel serverSocketChannel;

        public AcceptHandler(Selector selector, ServerSocketChannel serverSocketChannel) {
            this.selector = selector;
            this.serverSocketChannel = serverSocketChannel;
        }

        @Override
        public void run() {
            try {
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    new EchoHandler(selector, socketChannel);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    private static class EchoHandler implements Runnable {
        private SocketChannel socketChannel;
        private SelectionKey selectionKey;

        private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        public EchoHandler(Selector selector, SocketChannel channel) {
            try {
                this.socketChannel = channel;
                socketChannel.configureBlocking(false);
                selectionKey = channel.register(selector, SelectionKey.OP_READ);
                selectionKey.attach(this);
                selector.wakeup();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        @Override
        public void run() {
            try {
                int read = socketChannel.read(byteBuffer);
                if (read > 0) {
                    System.out.println("receive request is " + new String(byteBuffer.array(), 0, read));
                } else if (read == -1) {
                    socketChannel.close();
                    System.out.println(" close link");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

四、AIO

AIO示例代码

public class AIOClient {
    public static void main(String[] args) throws Exception {
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 9001)).get();
        socketChannel.write(ByteBuffer.wrap("HelloServer".getBytes()));

        ByteBuffer buffer = ByteBuffer.allocate(512);
        Integer len = socketChannel.read(buffer).get();
        if (len != -1) {
            System.out.println("客户端接收到信息是:" + new String(buffer.array(), 0, len));
        }
    }
}


public class AIOServer {

    public static void main(String[] args) throws Exception {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9001));

        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                try {
                    System.out.println("2--"+Thread.currentThread().getName());
                    serverSocketChannel.accept(attachment,this);
                    System.out.println(socketChannel.getRemoteAddress());
                    ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                    socketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer attachment) {
                            System.out.println("3--"+Thread.currentThread().getName());
                            byteBuffer.flip();
                            System.out.println(new String(byteBuffer.array(),0,result));
                            socketChannel.write(ByteBuffer.wrap("HelloClient".getBytes()));
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer attachment) {
                            exc.printStackTrace();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {

            }
        });

        System.out.println("1--"+Thread.currentThread().getName());
        Thread.sleep(Integer.MAX_VALUE);

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈脩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值