• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 網絡編程(BIO、NIO、Netty )

    標簽: NIO  BIO  Netty  網絡編程  socket

    簡介:
    BIO:同步阻塞式IO,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。 
    NIO:同步非阻塞式IO,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。 
     
    BIO
    網絡編程的基本模型是C/S模型,即兩個進程間的通信。
     服務端提供IP和監聽端口,客戶端通過連接操作想服務端監聽的地址發起連接請求,通過三次握手連接,如果連接成功建立,雙方就可以通過套接字進行通信。
        傳統的同步阻塞模型開發中,ServerSocket負責綁定IP地址,啟動監聽端口;Socket負責發起連接操作。連接成功后,雙方通過輸入和輸出流進行同步阻塞式通信。
    簡單的描述一下BIO的服務端通信模型:采用BIO通信模型的服務端,通常由一個獨立的Acceptor線程負責監聽客戶端的連接,它接收到客戶端連接請求之后為每個客戶端創建一個新的線程進行鏈路處理沒處理完成后,通過輸出流返回應答給客戶端,線程銷毀。即典型的一請求一應答通宵模型。

    【傳統BIO通信模型圖】


    該模型最大的問題就是缺乏彈性伸縮能力,當客戶端并發訪問量增加后,服務端的線程個數和客戶端并發訪問數呈1:1的正比關系,Java中的線程也是比較寶貴的系統資源,線程數量快速膨脹后,系統的性能將急劇下降,隨著訪問量的繼續增大,系統最終就掛了。這種通信模型在高并發的場景是沒法應用的。
    同步阻塞式I/O創建的Server源碼:
    package com.cherry.socket.bio;
    import java.io.*;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    /**
     * @author [email protected]
     * @create 2018/4/13 9:24
     * @desc
     **/
    public class BIOServerBootStrap {
        public static void main(String[] args) throws IOException {
            //1.Create ServerSocket
            ServerSocket serverSocket = new ServerSocket();
            //2.Set up the service listener port
            serverSocket.bind(new InetSocketAddress(9999));
            //Create a thread pool
            ExecutorService threadPool = Executors.newFixedThreadPool(150);
            while (true){
                //3.Waitting for request
                System.out.println("----waitting for request----");
                final Socket socket = serverSocket.accept();
                threadPool.submit(new Runnable() {
                    public void run() {
                        try {
                            //4.Obtain user intent
                            InputStream is = socket.getInputStream();
                            InputStreamReader isr = new InputStreamReader(is);
                            BufferedReader br = new BufferedReader(isr);
                            StringBuilder sb = new StringBuilder();
                            String line = null;
                            while ((line=br.readLine())!=null){
                                sb.append(line);
                            }
                            System.out.println("----the message get by server---- : "+sb);
                            //5.client response
                            OutputStream os = socket.getOutputStream();
                            PrintWriter pw = new PrintWriter(os);
                            pw.println("server time"+new Date().toLocaleString());
                            pw.flush();
                            socket.shutdownOutput();//Tell the client to write the cut-off
                            //6.release the resource
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
    同步阻塞式I/O創建的Client源碼:
    package com.cherry.socket.bio;
    import java.io.*;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    /**
     * @author [email protected]
     * @create 2018/4/13 11:07
     * @desc
     **/
    public class BIOClientBootStrap {
        public static void main(String[] args) throws IOException {
            //1.Create client socket
            Socket socket = new Socket();
            //2.connect to the server
            socket.connect(new InetSocketAddress("127.0.0.1",9999));
            //3.makes requests to the server
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os);
            pw.println("Hello,This is Client 2");
            pw.flush();
            socket.shutdownOutput();//Tell the Server to write the cut-off
            //4.get the response
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            StringBuilder sb = new StringBuilder();
            String line =null;
            while ((line=br.readLine())!=null){
                sb.append(line);
            }
            System.out.println("----The message get by client---- : "+sb);
            //5.release the resource
            socket.close();
        }
    }
    NIO
    non-blocking IO, NIO提供了與傳統BIO模型中的Socket和ServerSocket相對應的SocketChannel和ServerSocketChannel兩種不同的套接字通道實現。
    新增的著兩種通道都支持阻塞和非阻塞兩種模式。
    阻塞模式使用就像傳統中的支持一樣,比較簡單,但是性能和可靠性都不好;非阻塞模式正好與之相反。
    對于低負載、低并發的應用程序,可以使用同步阻塞I/O來提升開發速率和更好的維護性;對于高負載、高并發的(網絡)應用,應使用NIO的非阻塞模式來開發。
    NIO編程是面向通道的(BIO是面向流的),流分為寫入/寫出,是單向的,意味著通道是可以進行雙向讀寫的。NIO所有基于channel的API對數據的操作都是間接通過操作緩沖區ByteBuffer
    In : 磁盤 --通道--> ByteBuffer(內存)-->數據(內存)
    Out: 數據(內存)-->ByteBuffer(內存) --> 通道 --> 磁盤
         
    NIO編程涉及ServerSocketChannelSocketChannelByteBufferSelector(通道選擇器)

    服務端源碼:
    package com.cherry.socket.nio;
    import java.io.ByteArrayOutputStream;
    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;
    /**
     * @author [email protected]
     * @create 2018/4/13 11:34
     * @desc
     **/
    public class NIOServerBootStrap {
        public static void main(String[] args) throws IOException {
            //1.Create ServerSocketChannel
            ServerSocketChannel ssc = ServerSocketChannel.open();
            //2.Set up the service listener port
            ssc.socket().bind(new InetSocketAddress(9999));
            //3.Set up the channel to be non-blocking
            ssc.configureBlocking(false);
            //4.Create channel selector
            Selector selector =Selector.open();
            //5.注冊事件類型
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            //6.遍歷事件處理列表
            while (true){
                int num = selector.select();
                if (num>0){
                    //獲取所有待處理的keys
                    Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                    while (keys.hasNext()){
                        SelectionKey key = keys.next();
                        //處理事件
                        if(key.isAcceptable()){
                            System.out.println("----key isAcceptable----");
                            ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                            SocketChannel socketChannel = channel.accept();
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector,SelectionKey.OP_ACCEPT);
                        }else if (key.isReadable()){
                            System.out.println("----key isReadable(read)----");
                            SocketChannel channel = (SocketChannel) key.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            while (true){
                                buffer.clear();
                                int n = channel.read(buffer);
                                if (n==1)break;
                                baos.write(buffer.array(),0,n);
                            }
                            channel.register(selector,SelectionKey.OP_WRITE,baos);
                        } else if (key.isWritable()){
                            System.out.println("----key isWritable(write)----");
                            SocketChannel channel = (SocketChannel) key.channel();
                            ByteArrayOutputStream baos = (ByteArrayOutputStream) key.attachment();
                            baos.write("_服務器追加".getBytes());
                            ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
                            channel.write(buffer);
                            channel.socket().shutdownOutput();
                            channel.close();
                        }
                        //移除時間列表
                        keys.remove();
                    }
                } else {
                    continue;
                }
            }
        }
    }
    Netty
    Netty的通訊管道:
    通過AOP的編程思想(責任鏈設計模式),實現消息的編解碼。
    服務端編程:
    1)創建ServerBootstrap sbt=new ServerBootstrap();
    2)創建EventLoopGroup boss、worker
    3)關聯boss和worker> sbt.group(boss,worker);
    4)設置ServerSocketChannel實現類sbt.channel(NioServerSocketChannel);
    5)初始化通道sbt.childHandler(初始化通道);
    6)綁定端口并啟動服務ChannelFuture future=sbt.bind(port).sync();
    7)等待服務關閉 future.channel().closeFuture().sync();
    8) 釋放線程資源boss、worker # shutdownGraceFully();
    服務端源碼:
    package com.cherry.socket.netty.server;
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    /**
     * @author [email protected]
     * @create 2018/4/16 9:51
     * @desc
     **/
    public class ServerBootStrap {
        public static void main(String[] args) throws InterruptedException {
            //1.Create ServerBootstrap
            ServerBootstrap sbt = new ServerBootstrap();
            //2.Create thread pool(boss & worker)
            EventLoopGroup boss = new NioEventLoopGroup();
            EventLoopGroup worker = new NioEventLoopGroup();
            //3.關聯線程池(boss負責請求轉發,worker負責事件處理)
            sbt.group(boss,worker);
            //4.Set up the server
            sbt.channel(NioServerSocketChannel.class);
            //5.Initialize the communication channel (key point)
            sbt.childHandler(new ServerChannelInitizlizer());
            //6.Bind listening port
            System.out.println("我在@9999端口監聽...");
            ChannelFuture channelFuture = sbt.bind(9999).sync();
            //7.Wait for the server to close
            channelFuture.channel().closeFuture().sync();
            //l
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
    package com.cherry.socket.netty.server;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    /**
     * @author [email protected]
     * @create 2018/4/16 10:26
     * @desc
     **/
    public class ServerChannelHander extends ChannelHandlerAdapter {
    
        /*捕獲數據在通道傳輸過程中的異常*/
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.err.println("錯誤:"+cause.getMessage());
        }
        /*接收數據,并響應*/
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ChannelFuture channelFuture = ctx.writeAndFlush(msg);
            //關閉通道
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }
    package com.cherry.socket.netty.server;
    import com.cherry.socket.netty.server.ServerChannelHander;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    /**
     * @author [email protected]
     * @create 2018/4/16 10:02
     * @desc
     **/
    public class ServerChannelInitizlizer extends ChannelInitializer<SocketChannel> {
    
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            //communication channel
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast(new ServerChannelHander());
        }
    }
    客戶端編程:
    1)創建Bootstrap sbt=new Bootstrap();
    2)創建EventLoopGroup worker
    3)關聯boss和worker> sbt.group(worker);
    4)設置SocketChannel實現類sbt.channel(NioSocketChannel);
    5)初始化通道sbt.handler(初始化通道);
    6)綁定端口并啟動服務ChannelFuture future=sbt.connect(host,port).sync();
    7)等待服務關閉 future.channel().closeFuture().sync();
    8)釋放線程資源worker # shutdownGraceFully();
    客戶端源碼:
    package com.cherry.socket.netty.client;
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    /**
     * @author [email protected]
     * @create 2018/4/16 11:15
     * @desc
     **/
    public class ClientBootStrap {
        public static void main(String[] args) throws InterruptedException {
            //1.Create Bootstrap
            Bootstrap bt = new Bootstrap();
            //2.Create thread pool(worker)
            EventLoopGroup worker = new NioEventLoopGroup();
            //3.關聯線程池
            bt.group(worker);
            //4.Set up the client
            bt.channel(NioSocketChannel.class);
            //5.Initialize the communication channel (key point)
            bt.handler(new ClientChannelInitializer());
            //6.連接
            ChannelFuture channelFuture = bt.connect("127.0.0.1",9999).sync();
            //7.Wait for the server to close
            channelFuture.channel().closeFuture().sync();
            //8.release the resource
            worker.shutdownGracefully();
        }
    }
    package com.cherry.socket.netty.client;
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.CharsetUtil;
    import javax.xml.transform.Source;
    /**
     * @author [email protected]
     * @create 2018/4/16 11:22
     * @desc
     **/
    public class ClientChannelHander extends ChannelHandlerAdapter {
        /*捕獲數據在通道傳輸過程的異常*/
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            //super.exceptionCaught(ctx, cause);
            System.out.println("錯誤:"+cause.getMessage());
        }
        /*接收數據*/
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            //super.channelRead(ctx, msg);
            ByteBuf byteBuf = (ByteBuf) msg;
            System.out.println(((ByteBuf)msg).toString(CharsetUtil.UTF_8));
        }
        /*鏈接服務器時發送數據*/
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            //super.channelActive(ctx);
            ByteBuf buf = Unpooled.buffer();
            buf.writeBytes("你好,我是客戶端!".getBytes());
            ctx.writeAndFlush(buf);
        }
    }
    package com.cherry.socket.netty.client;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    /**
     * @author [email protected]
     * @create 2018/4/16 11:20
     * @desc
     **/
    public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            //communication channel
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast(new ClientChannelHander());
        }
    }
    上述BIO NIO Netty所用的pom.xml
    <!-- socket 相關  注意版本,不然有些方法不能用-->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>5.0.0.Alpha2</version>
    </dependency>
    通過對Netty的分析,我們將他的優點總結如下:
    1.API使用簡單,開發門檻低;
    2.功能強大,預設了很多編碼功能,支持多種主流協議;
    3.定制能力強,可以通過ChannelHandler對通信框架進行靈活擴展;
    4.性能高,通過與其他的主流NIO框架對比,Netty的綜合性能最優;
    5.成熟,穩定,Netty修復了已經發現的所有JDK NIO BUG,業務開發人員不需要再為NIO的BUG煩惱;
    6.社區活躍,版本迭代周期短;
    7.經過大規模的商業應用考驗,質量得到驗證。Netty在互聯網、大數據、網絡游戲、企業應用、電信軟件等眾多領域已經得到了成功商用,也 證明他已經完全能滿足不同行業的商應用了;
    基于上述這些,我將在下一篇文章中寫一些關于如何實現高性能可擴展RPC,希望對大家有所幫助!








    版權聲明:本文為Zhang_Pengcheng原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/Zhang_Pengcheng/article/details/80048134

    智能推薦

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    猜你喜歡

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    基于TCP/IP的網絡聊天室用Java來實現

    基于TCP/IP的網絡聊天室實現 開發工具:eclipse 開發環境:jdk1.8 發送端 接收端 工具類 運行截圖...

    19.vue中封裝echarts組件

    19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...

    精品国产乱码久久久久久蜜桃不卡