Netty雜記1—BIO網絡編程

前言

網絡編程的基本模型是Client/Server模型,也就是兩個進程之間進行相互通訊,其中服務端提供位置信息(綁定的IP地址和監聽端口),客戶端經過鏈接操做向服務器監聽的地址發起鏈接請求,若是鏈接創建成功 ,雙方就能夠經過Socket進行通訊。java

在傳統的BIO網絡編程中,ServerSocket負責綁定IP地址,啓動監聽端口;Socket負責發起鏈接操做。鏈接成功以後,雙方經過輸入和輸出流進行同步阻塞式通訊。編程

下面咱們會一步一步的實現BIO的網絡編程,而且對代碼進行不斷的修改和優化。服務器

BIO網絡編程

正如上文所言,進行BIO網絡編程須要Server和Client,客戶端咱們只編寫一個版本,保證可以向服務器發送請求,而服務端咱們會從最基本的只可以進行一次請求處理的版本開始,不斷的完善。網絡

客戶端

package nio;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;

public class BIOClient {
    public static void main(String[] args) throws Exception{
        for (int i=0;i<=99;i++){
            System.out.println(sendMsg("你好"+i));
        }
    }

    public static String sendMsg(String msg) throws Exception{
        // 建立socket 只負責發送請求 不須要監聽
        Socket socket = new Socket();

// 鏈接服務器
        socket.connect(new InetSocketAddress("localhost",9999));

// 寫入數據給服務器
        OutputStream outputStream = socket.getOutputStream();

// java 基礎的IO操做
        PrintWriter printWriter = new PrintWriter(outputStream);
        printWriter.println(msg);
        printWriter.flush();
// 關閉輸出流
        socket.shutdownOutput();

// 接受服務器的響應信息
        InputStream inputStream = socket.getInputStream();

// java 基礎的IO操做
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

        StringBuffer stringBuffer = new StringBuffer();
        String value = null;
        while ((value=bufferedReader.readLine())!=null){
            stringBuffer.append(value);
        }

        socket.close();
        return "客戶端收到:"+ stringBuffer.toString();
    }
}

複製代碼

服務端

初版

服務器啓動一次就關閉多線程

優化方向:保證可以連續的處理請求併發

public static void test1() throws Exception{
        // 建立一個ServerSocket
        ServerSocket ss = new ServerSocket();
// 綁定服務器監聽端口
        ss.bind(new InetSocketAddress(9999));

        //表明服務器和客戶端的會話 (InputStream = HttpServletRequest | OutputStream = HttpServletResponse)
// socket阻塞的 accept會監聽有沒有請求
        System.out.println("我在9999監聽");
        Socket socket = ss.accept();
        System.out.println("請求處理");
        //獲取請求數據 InputStream
        InputStream inputStream = socket.getInputStream();

// java 基礎的IO操做
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

        StringBuffer stringBuffer = new StringBuffer();
        String value = null;
        while ((value=bufferedReader.readLine())!=null){
            stringBuffer.append(value);
        }

        System.out.println("服務器收到:"+ stringBuffer.toString());

// 獲取響應 OutputStream
        OutputStream outputStream = socket.getOutputStream();

// java 基礎的IO操做
        PrintWriter printWriter = new PrintWriter(outputStream);
        printWriter.println("你好,我是服務器");
        printWriter.flush();

// 關閉資源
        socket.close();
    }
複製代碼
第二版

使用死循環,保證能夠一直處理請求。app

缺點:socket

  • 雖然能夠處理屢次請求,可是全部的請求都是依次執行,並非實際意義上的併發處理,也就是說沒有實現高併發
  • 串行執行若是第一個請求沒有處理結束,不能夠處理第二個請求

升級方向:多線程ide

public static void test2() throws Exception{
        // 建立一個ServerSocket
        ServerSocket ss = new ServerSocket();
// 綁定服務器監聽端口
        ss.bind(new InetSocketAddress(9999));

        //表明服務器和客戶端的會話 (InputStream = HttpServletRequest | OutputStream = HttpServletResponse)
// socket阻塞的 accept會監聽有沒有請求
        while (true){
            System.out.println("我在9999監聽");
            Socket socket = ss.accept();
            System.out.println("請求處理");
            //獲取請求數據 InputStream
            InputStream inputStream = socket.getInputStream();

// java 基礎的IO操做
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            StringBuffer stringBuffer = new StringBuffer();
            String value = null;
            while ((value=bufferedReader.readLine())!=null){
                stringBuffer.append(value);
            }

            System.out.println("服務器收到:"+ stringBuffer.toString());

// 獲取響應 OutputStream
            OutputStream outputStream = socket.getOutputStream();

// java 基礎的IO操做
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.println("你好,我是服務器");
            printWriter.flush();

// 關閉資源
            socket.close();
        }
    }
複製代碼
版本三

多線程處理 一旦有請求過來就交給一個新的線程處理 缺點:高併發

  • 雖然實現了線程併發處理,可是並無對線程建立作限制,若是每次處理10秒,併發量10萬,服務器會阻塞成千上萬個線程
  • 直接new線程比較耗費系統的資源,在高併發服務器中會使用多線程,不可能無限制的new線程,會耗盡服務器的資源

改進方向:使用線程池

public static void test3() throws Exception{
        // 建立一個ServerSocket
        ServerSocket ss = new ServerSocket();
// 綁定服務器監聽端口
        ss.bind(new InetSocketAddress(9999));

        //表明服務器和客戶端的會話 (InputStream = HttpServletRequest | OutputStream = HttpServletResponse)
// socket阻塞的 accept會監聽有沒有請求
        while (true){
            System.out.println("我在9999監聽");
            Socket socket = ss.accept();
// 多線程處理 一旦有請求過來就交給一個新的線程處理
            SocketProcessRunable socketProcessRunable = new SocketProcessRunable(socket);
            Thread thread = new Thread(socketProcessRunable);
            thread.start();

        }
    }

class SocketProcessRunable implements Runnable{
    private Socket socket;

    public SocketProcessRunable(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            //獲取請求數據 InputStream
            InputStream inputStream = socket.getInputStream();

// java 基礎的IO操做
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            StringBuffer stringBuffer = new StringBuffer();
            String value = null;
            while ((value=bufferedReader.readLine())!=null){
                stringBuffer.append(value);
            }

            System.out.println("服務器收到:"+ stringBuffer.toString());

// 獲取響應 OutputStream
            OutputStream outputStream = socket.getOutputStream();

// java 基礎的IO操做
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.println("你好,我是服務器");
            printWriter.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}
複製代碼
版本四

使用線程池管理線程

public static void main(String[] args) throws Exception{
// 建立線程池 Executors建立線程的缺點
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 建立一個ServerSocket
        ServerSocket ss = new ServerSocket();
// 綁定服務器監聽端口
        ss.bind(new InetSocketAddress(9999));

        //表明服務器和客戶端的會話 (InputStream = HttpServletRequest | OutputStream = HttpServletResponse)
// socket阻塞的 accept會監聽有沒有請求
        while (true){
            System.out.println("我在9999監聽");
            Socket socket = ss.accept();
// 多線程處理 一旦有請求過來就交給一個新的線程處理
            SocketProcessRunable socketProcessRunable = new SocketProcessRunable(socket);
// 將任務交給線程池處理
            executorService.submit(socketProcessRunable);
        }
    }

class SocketProcessRunable implements Runnable{
    private Socket socket;

    public SocketProcessRunable(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            //獲取請求數據 InputStream
            InputStream inputStream = socket.getInputStream();

// java 基礎的IO操做
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            StringBuffer stringBuffer = new StringBuffer();
            String value = null;
            while ((value=bufferedReader.readLine())!=null){
                stringBuffer.append(value);
            }

            System.out.println("服務器收到:"+ stringBuffer.toString());

// 獲取響應 OutputStream
            OutputStream outputStream = socket.getOutputStream();

// java 基礎的IO操做
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.println("你好,我是服務器");
            printWriter.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}
複製代碼

我不能保證每個地方都是對的,可是能夠保證每一句話,每一行代碼都是通過推敲和斟酌的。但願每一篇文章背後都是本身追求純粹技術人生的態度。

永遠相信美好的事情即將發生。

相關文章
相關標籤/搜索