Java高併發網絡編程

一、OSI網絡七層模型

因特網是一個極爲複雜的網絡,分層有助於我們對網絡的理解 。分層也是一種標準,爲了使不同廠商的計算機能夠互相通信,以便在更大範圍內建立計算機網絡,有必要建立一個國際範圍的網絡體系結構標準。
【加羣】:857565362
文章分享,內容較長,大家可以先收藏。請持續關注:Java高級架構學習資料分享+架構師成長之路
【加羣】:857565362
ISO組織制定了OSI網絡七層模型
在這裏插入圖片描述
而因特網只用到了五層
在這裏插入圖片描述
低三層:
屏蔽底層網絡的複雜性
物理層:使原始的數據比特流能在物理介質上傳輸。
數據鏈路層:通過校驗、確認和反饋重發等手段,形成穩定的數據鏈路。(01010101)
網絡層:進行路由選擇和流量控制。(IP協議)
傳輸層:提供可靠的端口到端口的數據傳輸服務(TCP/UDP協議)。
高三層:
會話層:負責建立、管理和終止進程之間的會話和數據交換。
表示層:負責數據格式轉換、數據加密與解密、壓縮與解壓縮等。
應用層:爲用戶的應用進程提供網絡服務。
網絡通信協議

二、傳輸層控制協議TC

傳輸層控制協議(TCP)是Internet一個重要的傳輸層協議。TCP提供面向連接、可靠、有序、字節流傳輸服務。應用程序在使用TCP之前,必須先建立TCP連接。
在這裏插入圖片描述
在這裏插入圖片描述
1.TCP握手機制
檢測網絡是否通暢
在這裏插入圖片描述
在這裏插入圖片描述

三、用戶數據報協議UDP

用戶數據報協議UDP是Internet傳輸層協議。提供無連接、不可靠、數據盡力傳輸服務。
在這裏插入圖片描述
在這裏插入圖片描述
TCP和UDP比較
文章較長,可以先收藏起來,慢慢看。

四、Socket

1.Scoket概述
IP地址:InetAddress
唯一的標識Internet上的計算機
本地迴環地址(hostAddress):127.0.0.1 主機名(hostName):localhost
不易記憶
端口號標識正在計算機上運行的進程(程序)
不同的進程有不同的端口號
被規定爲一個16位的整數0-65535。其中,0-1023被預先定義的服務通信佔用(如MySql佔用端口3306,http佔用端口80等)。除非我們需要訪問這些特殊服務,否則,應該使用1024-65535這些端口中的某一個進行通信,以免發生端口衝突。
端口號與IP地址的組合得出一個網絡套接字
我們知道兩個進程如果需要進行通訊最基本的一個前提能能夠唯一的標示一個進程,在本地進程通訊中我們可以使用PID來唯一標示一個進程,但PID只在本地唯一,網絡中的兩個進程PID衝突機率很大,這時候我們需要另闢它徑了,我們知道IP層的ip地址可以唯一標示主機,而TCP層協議和端口號可以唯一標示主機的一個進程,這樣我們可以利用ip地址+協議+端口號唯一標示網絡中的一個進程。
能夠唯一標示網絡中的進程後,它們就可以利用socket進行通信了,什麼是socket呢?我們經常把socket翻譯爲套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通信。
在這裏插入圖片描述
網絡應用程序由成對的進程組成,這些進程通過網絡相互發送報文。在一對進程之間的通信會話場景中,發起通信的進程被標識爲客戶,在會話開始時等待聯繫的進程是服務器。進程通過一個稱爲套接字的軟件接口向網絡發送報文和從網絡接收報文。套接字是同一臺主機內應用層與傳輸層之間的接口,由於該套接字是建立網絡應用程序的可編程接口,因此套接字也稱爲應用程序和網路之間的應用程序編程接口(API)。應用程序開發者可以控制套接字在應用層端的一切,但是對該套接字的運輸層端幾乎沒有控制權,因此網絡編程實際上就是Socket編程。
Internet應用最廣的網絡應用編程接口,實現與3種底層協議接口:

  1. 數據報類型套接字SOCK_DGRAM(面向UDP接口)
  2. 流式套接字SOCK_STREAM(面向TCP接口)
  3. 原始套接字SOCK_RAW(面向網絡層協議接口IP、ICMP等)
    主要socket API及其調用過程
    在這裏插入圖片描述
    在這裏插入圖片描述
    socket是"打開—讀/寫—關閉"模式的實現,以使用TCP協議通訊的socket爲例,其交互流程大概如下:
    在這裏插入圖片描述
    2.Java中的Socket編程
    客戶端
public class SocketClient {
     public void client() {
        Socket sc = null;
        OutputStream os = null;
        InputStream is = null;
        try {
            sc = new Socket(InetAddress.getByName("127.0.0.1"), 9092);
            os = sc.getOutputStream();
            os.write("我是客戶端,我向你發送消息了".getBytes());
            sc.shutdownOutput(); // 顯式的告訴服務端發送完畢
            is = sc.getInputStream();
            byte[] b = new byte[30];
            int len;
            while ((len = is.read(b)) != -1) {
                String str = new String(b, 0, len);
                System.out.println(str);
            }
         } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        } finally {
             if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (sc != null) {
                try {
                    sc.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } 
       } 
   }
     public static void main(String[] args) {
        SocketClient client = new SocketClient();
        client.client();
    }
}

當Client程序需要從Server端獲取信息及其他服務時,應創建一個Socket對象
構造函數
在這裏插入圖片描述
new Socket(InetAddress.getByName(“127.0.0.1”), 9092)會打開一個套接字
一旦套接字被打開,java.net.Socket類中的getInputStream方法就會返回一個InputStream對象,該對象可以像其他任何流一樣使用,接收另一端傳來的數據
getOutPutStream則是輸出流,可以傳給另一端

套接字離不開IP地址, Java中有IP相關的類
InetAddress類沒有提供公共的構造方法,而是提供了兩個靜態方法來獲取InetAddress實例
在這裏插入圖片描述
InetAddress有如下常用方法:
在這裏插入圖片描述
服務器端

public class SocketServer {
     public void server() {
        ServerSocket ss = null;
        Socket s = null;
        InputStream is = null;
        OutputStream os = null;
        try {
            ss = new ServerSocket(9092);
            s = ss.accept();
            is = s.getInputStream();
            /**
             * 1、 read()方法,這個方法從輸入流中讀取數據的下一個字節。返回 0 到 255 範圍內的 int 字節值。如果因爲已經到達流末尾而沒有可用的字節,則返回值 -1。 2、read(byte[] b,int
             * off,int len)方法,將輸入流中最多 len 個數據字節讀入 byte 數組。嘗試讀取len 個字節,但讀取的字節也可能小於該值。以整數形式返回實際讀取的字節數。 3、read(byte[]
             * b)方法,從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組 b 中。以整數形式返回實際讀取的字節數。
             */
            byte[] b = new byte[30];
            int len;
            while ((len = is.read(b)) != -1) {
                /**
                 * new String(bytes, offset, length) bytes爲要解譯的字符串; offset爲要解譯的第一個索引,比如從0開始就是從字符串bytes的第一個字符開始;
                 * length爲要解譯的字符串bytes的長度
                 */
                String str = new String(b, 0, len);
                System.out.println(str);
            }
            os = s.getOutputStream();
            os.write("我是服務端,收到了你的信息".getBytes()); 
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (s != null) {
                try {
                    s.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (ss != null) {
                try {
                    ss.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
         }
    }
     public static void main(String[] args) {
        SocketServer server = new SocketServer();
        server.server();
    }
}

服務器需隨時待命,因爲不知道客戶端什麼時候會發來請求,此時,我們需要使用ServerSocket,對應的是java.net.ServerSocket類
構造函數
在這裏插入圖片描述
new ServerSocket(9092)建立一個監聽9092接口的服務器
ss.accept()告訴程序不停的等待,直到客戶端連接到這個接口。一旦有人通過網絡發送了正確的連接請求,並以此連接到了端口上,該方法就返回一個表示連接已經建立的Socket對象。
可以使用這個對象來得到輸入流和輸出流。
文章分享結束,覺得可以就點個關注唄~整理了一些Java學習資料,包含:Java進階學習路線,以及各技術知識總結,還有面試題目彙總。需要的朋友可以自行領取:Java高級架構學習資料分享+架構師成長之路

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章