1. 程式人生 > >網路程式設計(BIO、NIO、Netty )

網路程式設計(BIO、NIO、Netty )

簡介: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();
    }
}
NIOnon-blocking IO, NIO提供了與傳統BIO模型中的Socket和ServerSocket相對應的SocketChannel和ServerSocketChannel兩種不同的套接字通道實現。新增的著兩種通道都支援阻塞和非阻塞兩種模式。阻塞模式使用就像傳統中的支援一樣,比較簡單,但是效能和可靠性都不好;非阻塞模式正好與之相反。對於低負載、低併發的應用程式,可以使用同步阻塞I/O來提升開發速率和更好的維護性;對於高負載、高併發的(網路)應用,應使用NIO的非阻塞模式來開發。NIO程式設計是面向通道的(BIO是面向流的),流分為寫入/寫出,是單向的,意味著通道是可以進行雙向讀寫的。NIO所有基於channel的API對資料的操作都是間接通過操作緩衝區ByteBufferIn : 磁碟 --通道--> 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;
            }
        }
    }
}
NettyNetty的通訊管道:通過AOP的程式設計思想(責任鏈設計模式),實現訊息的編解碼。服務端程式設計:1)建立ServerBootstrap sbt=new ServerBootstrap();2)建立EventLoopGroup boss、worker3)關聯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 worker3)關聯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,希望對大家有所幫助!







相關推薦

網路程式設計BIONIONetty

簡介:BIO:同步阻塞式IO,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。 NIO:同步非阻塞式IO,伺服器實現模式為一個請求一個執行緒,即客戶端

基於TCP的socket套接字的網路程式設計客戶端/服務端模式

於資料完整性要求較高的場合,就應採用TCP協議。 IP網路層提供IP定址和路由。因為在網路上資料可以經由多條線路到達目的地,網路層負責找出最佳的傳輸線路。 IP地址與資料包:   IP層就是把資料分組從一個主機跨越千山萬水搬運到另外一主機, 並且這搬運服務一點都不可靠, 丟包、

網路程式設計——connectTCP和connectUDP的實現2.利用TCP/UDP完成檔案傳輸的設計和實現(下)

每個客戶與伺服器建立聯絡必須: 選擇協議(UDP或TCP) 查詢伺服器的機器名 查詢所期望的服務並將其對映到協議埠號 分配套接字並與之連線 將這部分工作進行封裝,置於某個過程當中,只需一次編碼。 我們想要實現以下抽象: socket = connectTCP(mac

基於訊息實現系統間的通訊BIONIO,AIO學習。

當系統之間要通訊時,就向外傳送訊息,訊息可以是位元組流,位元組陣列,甚至是java物件,其他系統接受訊息後,則進行相應的業務處理。 訊息方式的系統間通訊,通常基於網路協議來實現,常用的實現系統間的通訊協議有:TCP/IP 和UDP/IP。 TCP/IP是一種可靠的網路資料

系統之間通訊方式BIONIO的區別

4-3、NIO通訊框架 目前流行的NIO框架非常的多。在論壇上、網際網路上大家討論和使用最多的有以下幾種: 原生JAVA NIO框架:  JAVA NIO通訊框架基於多路複用IO原理,我們將詳細講解它的工作原理。 APACHE MINA 2:  是一個網路應用程

Java 網路IO程式設計總結BIONIOAIO均含完整例項程式碼

第一段內容轉載自:http://blog.51cto.com/stevex/1284437 先來個例子理解一下概念,以銀行取款為例: 同步 : 自己親自出馬持銀行卡到銀行取錢(使用同步IO時,Java自己處理IO讀寫)。 非同步

Java IO 程式設計BIONIOAIO完整例項程式碼

      本文會從傳統的BIO到NIO再到AIO自淺至深介紹,並附上程式碼講解。   原始碼地址: https://github.com/kkman2008/java-IO.git     下面程式碼中會使用這樣一個

BIONIOAIO及網路程式設計

一. 網路程式設計的一些基礎      1.先說明一下執行緒的掛起、阻塞、睡眠         執行緒從建立、執行到結束總是處於下面五個狀態之一:新建狀態、就緒狀態、執行狀     &nb

Java基礎:java網路程式設計IO總結(BIONIOAIO)

1.基本概念 在Java網路通訊中,最基本的概念就是Socket程式設計了。Socket又稱“套接字” 向網路發出請求或者應答網路請求。 Socket 和ServerSocket類庫位於 Java.net 包中。ServerSocket用於伺服器端,Socket是建立網路連線時使用的

網路程式設計BIONIOAIO

TCP直連Socket與ServerSocket通訊 Server.java import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class S

java架構之路-netty專題初步認識BIONIOAIO

  本次我們主要來說一下我們的IO阻塞模型,只是不多,但是一定要理解,對於後面理解netty很重要的 IO模型精講    IO模型就是說用什麼樣的通道進行資料的傳送和接收,Java共支援3種網路程式設計IO模式:BIO,NIO,AIO。 BIO   BIO(Blocking IO) 同步阻塞模型,一

網路程式設計InetAddress類Socket和ServerSocket實現客戶端和伺服器之間的雙向通訊

網路程式設計的底層是IO,通過IO將一臺計算機中的資料傳送到另一臺計算機中。傳送的時候,要知道接受方的地址,該地址即為IP地址。知道IP地址後即可進行傳送。A向B發訊息,訊息是發過去了,但是B要怎樣接受呢?因此定義了埠,B監聽了A所使用的埠。A發的訊息中含有埠號,當B接受到訊息時,知道了埠號

Java核心深入理解BIONIOAIO

導讀:本文你將獲取到:同/非同步 + 阻/非阻塞的效能區別;BIO、NIO、AIO 的區別;理解和實現 NIO 操作 Socket 時的多路複用;同時掌握 IO 最底層最核心的操作技巧。 BIO、NIO、AIO 的區別是什麼? 同/非同步、阻/非阻塞的區別是什麼? 檔案讀寫最優雅的實現方式是什

Linux IO模式BIONIOIO多路複用非同步IO及 selectpollepoll詳解

同步IO和非同步IO,阻塞IO和非阻塞IO分別是什麼,到底有什麼區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上下文。 本文討論的背景是Linux環境下的network IO。 一 概念說明 在進行解釋之前,首先要說明幾個概念: -

javaSE (四十網路程式設計TCP傳輸伺服器多執行緒網路程式設計練習:反轉字串上傳檔案

1、TCP傳輸: 1.客戶端 建立Socket連結服務端(指定ip地址,埠號),通過ip地址找到對應的伺服器 呼叫Socket的getInputStream和getOutputStream方法獲取和伺服器端相連的IO流 2.伺服器端 建立Se

javaSE (三十九網路程式設計網路程式設計三要素和SocketUDP傳輸多執行緒UDP傳輸

1、網路程式設計三要素: 地址:定位電腦 本地迴路地址:127.0.0.1 廣播地址:255.255.255.255 埠號:定位電腦中的程式 o~65525 儘量使用1024以上的 協議:資料交換的規則/標準 UDP: 面向無連線,資料不安全,速度快,不區分客戶端與服

Java中網路IO的實現方式-BIONIOAIO

在網路程式設計中,接觸到最多的就是利用Socket進行網路通訊開發。在Java中主要是以下三種實現方式BIO、NIO、AIO。 關於這三個概念的辨析以前一直都是好像懂,但是表達的不是很清楚,下面做個總結完全辨析清楚。 1. BIO方式

JAVA網路IO(BIONIOAIO)

例子一: “阻塞”與"非阻塞"和"同步"與“非同步"不能簡單的從字面理解,提供一個從分散式系統角度的回答。1.同步與非同步同步和非同步關注的是訊息通訊機制 (synchronous communic

學習Netty前對BIONIOAIO的理解

概念 Netty是一個提供了易於使用的API的客戶端/伺服器框架 高併發NIO(非阻塞IO) 傳輸快,零拷貝(在Java中,記憶體分為堆疊常量池等。假設現在我們有一些資料,我們需要從IO裡面讀取並且放到堆裡面,那麼一般都會從IO流將資料讀入緩衝區,然後再從緩衝區裡

Java中的BIONIOAIONIO2

搞清楚了以上概念以後,我們再回過頭來看看,Reactor模式和Proactor模式。      (其實阻塞與非阻塞都可以理解為同步範疇下才有的概念,對於非同步,就不會再去分阻塞非阻塞。對於使用者程序,接到非同步通知後,就直接操作程序使用者態空間裡的資料好了。)      首先來看看Reactor模式,Reac