欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java面試Socket編程常用參數(shù)設(shè)置源碼問題分析

 更新時(shí)間:2022年03月10日 16:08:33   作者:Q.E.D.  
這篇文章主要為大家介紹了Java編程中關(guān)于Socket結(jié)構(gòu)分析,常用參數(shù)設(shè)置源碼示例以及面試中的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助

引導(dǎo)語

Socket 中文翻譯叫套接字,可能很多工作四五年的同學(xué)都沒有用過這個(gè) API,但只要用到這個(gè) API 時(shí),必然是在重要的工程的核心代碼處。

大家平時(shí)基本都在用開源的各種 rpc 框架,比如說 Dubbo、gRPC、Spring Cloud 等等,很少需要手寫網(wǎng)絡(luò)調(diào)用,以下三小節(jié)可以幫助大家補(bǔ)充這塊的內(nèi)容,當(dāng)你真正需要的時(shí)候,可以作為手冊(cè)示例。

本文和《ServerSocket 源碼及面試題》一文主要說 Socket 和 ServerSocket 的源碼,《工作實(shí)戰(zhàn):Socket 結(jié)合線程池的使用》這章主要說兩個(gè) API 在實(shí)際工作中如何落地。

1、Socket 整體結(jié)構(gòu)

Socket 的結(jié)構(gòu)非常簡(jiǎn)單,Socket 就像一個(gè)殼一樣,將套接字初始化、創(chuàng)建連接等各種操作包裝了一下,其底層實(shí)現(xiàn)都是 SocketImpl 實(shí)現(xiàn)的,Socket 本身的業(yè)務(wù)邏輯非常簡(jiǎn)單。

Socket 的屬性不多,有套接字的狀態(tài),SocketImpl,讀寫的狀態(tài)等等,源碼如下圖:

圖片描述

 套接字的狀態(tài)變更都是有對(duì)應(yīng)操作方法的,比如套接字新建(createImpl 方法)后,狀態(tài)就會(huì)更改成 created = true,連接(connect)之后,狀態(tài)更改成 connected = true 等等。

2、初始化

Socket 的構(gòu)造器比較多,可以分成兩大類:

指定代理類型(Proxy)創(chuàng)建套節(jié)點(diǎn),一共有三種類型為:DIRECT(直連)、HTTP(HTTP、FTP 高級(jí)協(xié)議的代理)、SOCKS(SOCKS 代理),三種不同的代碼方式對(duì)應(yīng)的 SocketImpl 不同,分別是:PlainSocketImpl、HttpConnectSocketImpl、SocksSocketImpl,除了類型之外 Proxy 還指定了地址和端口;

默認(rèn) SocksSocketImpl 創(chuàng)建,并且需要在構(gòu)造器中傳入地址和端口,源碼如下:

// address 代表IP地址,port 表示套接字的端口
// address 我們一般使用 InetSocketAddress,InetSocketAddress 有 ip+port、域名+port、InetAddress 等初始化方式
public Socket(InetAddress address, int port) throws IOException {
    this(address != null ? new InetSocketAddress(address, port) : null,
         (SocketAddress) null, true);
}

這里的 address 可以是 ip 地址或者域名,比如說 127.0.0.1 或者 www.wenhe.com。

我們一起看一下這個(gè)構(gòu)造器調(diào)用的 this 底層構(gòu)造器的源碼:

// stream 為 true 時(shí),表示為stream socket 流套接字,使用 TCP 協(xié)議,比較穩(wěn)定可靠,但占用資源多
// stream 為 false 時(shí),表示為datagram socket 數(shù)據(jù)報(bào)套接字,使用 UDP 協(xié)議,不穩(wěn)定,但占用資源少
private Socket(SocketAddress address, SocketAddress localAddr,
               boolean stream) throws IOException {
    setImpl();
    // backward compatibility
    if (address == null)
        throw new NullPointerException();
    try {
        // 創(chuàng)建 socket
        createImpl(stream);
        // 如果 ip 地址不為空,綁定地址
        if (localAddr != null)
            // create、bind、connect 也是 native 方法
            bind(localAddr);
        connect(address);
    } catch (IOException | IllegalArgumentException | SecurityException e) {
        try {
            close();
        } catch (IOException ce) {
            e.addSuppressed(ce);
        }
        throw e;
    }
}

從源碼中可以看出:

  • 在構(gòu)造 Socket 的時(shí)候,你可以選擇 TCP 或 UDP,默認(rèn)是 TCP;
  • 如果構(gòu)造 Socket 時(shí),傳入地址和端口,那么在構(gòu)造的時(shí)候,就會(huì)嘗試在此地址和端口上創(chuàng)建套接字;
  • Socket 的無參構(gòu)造器只會(huì)初始化 SocksSocketImpl,并不會(huì)和當(dāng)前地址端口綁定,需要我們手動(dòng)的調(diào)用 connect 方法,才能使用當(dāng)前地址和端口;
  • Socket 我們可以理解成網(wǎng)絡(luò)溝通的語言層次的抽象,底層網(wǎng)絡(luò)創(chuàng)建、連接和關(guān)閉,仍然是 TCP 或 UDP 本身網(wǎng)絡(luò)協(xié)議指定的標(biāo)準(zhǔn),Socket 只是使用 Java 語言做了一層封裝,從而讓我們更方便地使用。

3、connect 連接服務(wù)端

connect 方法主要用于 Socket 客戶端連接上服務(wù)端,如果底層是 TCP 層協(xié)議的話,就是通過三次握手和服務(wù)端建立連接,為客戶端和服務(wù)端之間的通信做好準(zhǔn)備,底層源碼如下:

public void connect(SocketAddress endpoint, int timeout) throws IOException {
}

connect 方法要求有兩個(gè)入?yún)?,第一個(gè)入?yún)⑹?SocketAddress,表示服務(wù)端的地址,我們可以使用 InetSocketAddress 進(jìn)行初始化,比如:new InetSocketAddress(“www.wenhe.com”, 2000)。

第二入?yún)⑹浅瑫r(shí)時(shí)間的意思(單位毫秒),表示客戶端連接服務(wù)端的最大等待時(shí)間,如果超過當(dāng)前等待時(shí)間,仍然沒有成功建立連接,拋 SocketTimeoutException 異常,如果是 0 的話,表示無限等待。

4、Socket 常用設(shè)置參數(shù)

Socket 的常用設(shè)置參數(shù)在 SocketOptions 類中都可以找到,接下來我們來一一分析下,以下理解大多來自類注釋和網(wǎng)絡(luò)。

4.1、setTcpNoDelay

此方法是用來設(shè)置 TCP_NODELAY 屬性的,屬性的注釋是這樣的:此設(shè)置僅僅對(duì) TCP 生效,主要為了禁止使用 Nagle 算法,true 表示禁止使用,false 表示使用,默認(rèn)是 false。

對(duì)于 Nagle 算法,我們引用維基百科上的解釋:

納格算法是以減少數(shù)據(jù)包發(fā)送量來增進(jìn) [TCP/IP] 網(wǎng)絡(luò)的性能,它由約翰·納格任職于Ford Aerospace時(shí)命名。

納格的文件[注 1]描述了他所謂的“小數(shù)據(jù)包問題”-某個(gè)應(yīng)用程序不斷地提交小單位的數(shù)據(jù),且某些常只占1字節(jié)大小。因?yàn)門CP數(shù)據(jù)包具有40字節(jié)的標(biāo)頭信息(TCP與IPv4各占20字節(jié)),這導(dǎo)致了41字節(jié)大小的數(shù)據(jù)包只有1字節(jié)的可用信息,造成龐大的浪費(fèi)。這種狀況常常發(fā)生于Telnet工作階段-大部分的鍵盤操作會(huì)產(chǎn)生1字節(jié)的數(shù)據(jù)并馬上提交。更糟的是,在慢速的網(wǎng)絡(luò)連線下,這類的數(shù)據(jù)包會(huì)大量地在同一時(shí)點(diǎn)傳輸,造成壅塞碰撞。

納格算法的工作方式是合并(coalescing)一定數(shù)量的輸出數(shù)據(jù)后一次提交。特別的是,只要有已提交的數(shù)據(jù)包尚未確認(rèn),發(fā)送者會(huì)持續(xù)緩沖數(shù)據(jù)包,直到累積一定數(shù)量的數(shù)據(jù)才提交。

總結(jié)算法開啟關(guān)閉的場(chǎng)景:

如果 Nagle 算法關(guān)閉,對(duì)于小數(shù)據(jù)包,比如一次鼠標(biāo)移動(dòng),點(diǎn)擊,客戶端都會(huì)立馬和服務(wù)端交互,實(shí)時(shí)響應(yīng)度非常高,但頻繁的通信卻很占用不少網(wǎng)絡(luò)資源;如果 Nagle 算法開啟,算法會(huì)自動(dòng)合并小數(shù)據(jù)包,等到達(dá)到一定大?。∕SS)后,才會(huì)和服務(wù)端交互,優(yōu)點(diǎn)是減少了通信次數(shù),缺點(diǎn)是實(shí)時(shí)響應(yīng)度會(huì)低一些。

Socket 創(chuàng)建時(shí),默認(rèn)是開啟 Nagle 算法的,可以根據(jù)實(shí)時(shí)性要求來選擇是否關(guān)閉 Nagle 算法。

4.2、setSoLinger

setSoLinger 方法主要用來設(shè)置 SO_LINGER 屬性值的。

注釋上大概是這個(gè)意思:在我們調(diào)用 close 方法時(shí),默認(rèn)是直接返回的,但如果給 SO_LINGER 賦值,就會(huì)阻塞 close 方法,在 SO_LINGER 時(shí)間內(nèi),等待通信雙方發(fā)送數(shù)據(jù),如果時(shí)間過了,還未結(jié)束,將發(fā)送 TCP RST 強(qiáng)制關(guān)閉 TCP 。

我們看一下 setSoLinger 源碼:

// on 為 false,表示不啟用延時(shí)關(guān)閉,true 的話表示啟用延時(shí)關(guān)閉
// linger 為延時(shí)的時(shí)間,單位秒
public void setSoLinger(boolean on, int linger) throws SocketException {
    // 檢查是否已經(jīng)關(guān)閉
    if (isClosed())
        throw new SocketException("Socket is closed");
    // 不啟用延時(shí)關(guān)閉
    if (!on) {
        getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on));
    // 啟用延時(shí)關(guān)閉,如果 linger 為 0,那么會(huì)立即關(guān)閉
    // linger 最大為 65535 秒,約 18 小時(shí)
    } else {
        if (linger < 0) {
            throw new IllegalArgumentException("invalid value for SO_LINGER");
        }
        if (linger > 65535)
            linger = 65535;
        getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger));
    }
}

4.3、setOOBInline

setOOBInline 方法主要使用設(shè)置 SO_OOBINLINE 屬性。

注釋上說:如果希望接受 TCP urgent data(TCP 緊急數(shù)據(jù))的話,可以開啟該選項(xiàng),默認(rèn)該選項(xiàng)是關(guān)閉的,我們可以通過 Socket#sendUrgentData 方法來發(fā)送緊急數(shù)據(jù)。

查詢了很多資料,都建議盡可能的去避免設(shè)置該值,禁止使用 TCP 緊急數(shù)據(jù)。

4.4、setSoTimeout

setSoTimeout 方法主要是用來設(shè)置 SO_TIMEOUT 屬性的。

注釋上說:用來設(shè)置阻塞操作的超時(shí)時(shí)間,阻塞操作主要有:

  • ServerSocket.accept() 服務(wù)器等待客戶端的連接;
  • SocketInputStream.read() 客戶端或服務(wù)端讀取輸入超時(shí);
  • DatagramSocket.receive()。

我們必須在必須在阻塞操作之前設(shè)置該選項(xiàng), 如果時(shí)間到了,操作仍然在阻塞,會(huì)拋出 InterruptedIOException 異常(Socket 會(huì)拋出 SocketTimeoutException 異常,不同的套接字拋出的異??赡懿煌?。

對(duì)于 Socket 來說,超時(shí)時(shí)間如果設(shè)置成 0,表示沒有超時(shí)時(shí)間,阻塞時(shí)會(huì)無限等待。

4.5、setSendBufferSize

setSendBufferSize 方法主要用于設(shè)置 SO_SNDBUF 屬性的,入?yún)⑹?int 類型,表示設(shè)置發(fā)送端(輸出端)的緩沖區(qū)的大小,單位是字節(jié)。

入?yún)?size 必須大于 0,否則會(huì)拋出 IllegalArgumentException 異常。

一般我們都是采取默認(rèn)的,如果值設(shè)置太小,很有可能導(dǎo)致網(wǎng)絡(luò)交互過于頻繁,如果值設(shè)置太大,那么交互變少,實(shí)時(shí)性就會(huì)變低。

4.6、setReceiveBufferSize

setReceiveBufferSize 方法主要用來設(shè)置 SO_RCVBUF 屬性的,入?yún)⑹?int 類型,表示設(shè)置接收端的緩沖區(qū)的大小,單位是字節(jié)。

入?yún)?size 必須大于 0,否則會(huì)拋出 IllegalArgumentException 異常。

一般來說,在套接字建立連接之后,我們可以隨意修改窗口大小,但是當(dāng)窗口大小大于 64k 時(shí),需要注意:

必須在 Socket 連接客戶端之前設(shè)置緩沖值;必須在 ServerSocket 綁定本地地址之前設(shè)置緩沖值。

4.7、setKeepAlive

setKeepAlive 方法主要用來設(shè)置 SO_KEEPALIVE 屬性,主要是用來探測(cè)服務(wù)端的套接字是否還是存活狀態(tài),默認(rèn)設(shè)置是 false,不會(huì)觸發(fā)這個(gè)功能。

如果 SO_KEEPALIVE 開啟的話,TCP 自動(dòng)觸發(fā)功能:如果兩小時(shí)內(nèi),客戶端和服務(wù)端的套接字之間沒有任何通信,TCP 會(huì)自動(dòng)發(fā)送 keepalive 探測(cè)給對(duì)方,對(duì)方必須響應(yīng)這個(gè)探測(cè)(假設(shè)是客戶端發(fā)送給服務(wù)端),預(yù)測(cè)有三種情況:

服務(wù)端使用預(yù)期的 ACK 回復(fù),說明一切正常;服務(wù)端回復(fù) RST,表示服務(wù)端處于死機(jī)或者重啟狀態(tài),終止連接;沒有得到服務(wù)端的響應(yīng)(會(huì)嘗試多次),表示套接字已經(jīng)關(guān)閉了。

4.8、setReuseAddress

setReuseAddress 方法主要用來設(shè)置 SO_REUSEADDR 屬性,入?yún)⑹遣紶栔?,默認(rèn)是 false。

套接字在關(guān)閉之后,會(huì)等待一段時(shí)間之后才會(huì)真正的關(guān)閉,如果此時(shí)有新的套接字前來綁定同樣的地址和端口時(shí),如果 setReuseAddress 為 true 的話,就可以綁定成功,否則綁定失敗。

5、總結(jié)

如果平時(shí)一直在做業(yè)務(wù)代碼,Socket 可能用到的很少,但面試問到網(wǎng)絡(luò)協(xié)議時(shí),或者以后有機(jī)會(huì)做做中間件的時(shí)候,就會(huì)有大概率會(huì)接觸到 Socket,所以多學(xué)學(xué),作為知識(shí)儲(chǔ)備也蠻好的。

以上就是Java編程Socket結(jié)構(gòu)常用參數(shù)設(shè)置源碼及面試題的詳細(xì)內(nèi)容,更多關(guān)于Java編程Socket結(jié)構(gòu)常用參數(shù)面試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論