溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java網(wǎng)絡(luò)編程Socket怎么創(chuàng)建

發(fā)布時(shí)間:2023-05-05 14:22:03 來(lái)源:億速云 閱讀:108 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容主要講解“Java網(wǎng)絡(luò)編程Socket怎么創(chuàng)建”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Java網(wǎng)絡(luò)編程Socket怎么創(chuàng)建”吧!

1|0構(gòu)造socket

在【客戶端/服務(wù)端】的通信模式中,客戶端需要主動(dòng)構(gòu)造與服務(wù)器連接的 Socket,構(gòu)造方法有以下幾種重載形式:

Socket()
Socket(InetAddress address, int port) throws UnknownHostException,IOException
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
Socket(String host, int port) throws UnknownHostException,IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
Socket(Proxy proxy)

除了第一個(gè)不帶參數(shù)的構(gòu)造方法,其他構(gòu)造方法都會(huì)試圖建立與服務(wù)器的連接,一旦連接成功,就返回 Socket 對(duì)象,否則拋出異常

1. 設(shè)定等待建立連接的超時(shí)時(shí)間

當(dāng)客戶端的 Socket 構(gòu)造方法請(qǐng)求與服務(wù)器連接時(shí),可能要等待一段時(shí)間。在默認(rèn)情況下,Socket 構(gòu)造方法會(huì)一直等待下去,直到連接成功,或者出現(xiàn)異常。Socket 構(gòu)造方法請(qǐng)求連接時(shí),受底層網(wǎng)絡(luò)的傳輸速度的影響,可能會(huì)處于長(zhǎng)時(shí)間的等待狀態(tài)。如果希望限定等待連接的時(shí)間,就需要使用第一個(gè)不帶參數(shù)的構(gòu)造方法

Socket socket = new Socket();
SocketAddress remoteAddr = new InetSocketAddress("1ocalhostn", 8000);
// 參數(shù)endpoint指定服務(wù)器的地址,參數(shù)timeout設(shè)定的超時(shí)時(shí)間(ms)
// 如果參數(shù)timeout被設(shè)為0則表示永遠(yuǎn)不會(huì)超時(shí)
socket.connect(remoteAddr, 60000);

以上代碼用于連接到本地機(jī)器上的監(jiān)聽(tīng) 8000 端口的服務(wù)器程序,等待連接的最長(zhǎng)時(shí)間為一分鐘。如果在一分鐘內(nèi)連接成功,則 connect() 方法順利返回,如果在一分鐘內(nèi)出現(xiàn)某種異常則拋出該異常,如果在一分鐘后既沒(méi)有連接成功,也沒(méi)有出現(xiàn)異常,那么會(huì)拋出 SocketTimeoutException

2. 設(shè)定服務(wù)器的地址

除了不帶參數(shù)的構(gòu)造方法,其他構(gòu)造方法都需要在參數(shù)中設(shè)定服務(wù)器的地城,包括服務(wù)器的 IP 或主機(jī)名,以及端口

Socket socket = new Socket();
SocketAddress remoteAddr = new InetSocketAddress("1ocalhostn", 8000);
// 參數(shù)endpoint指定服務(wù)器的地址,參數(shù)timeout設(shè)定的超時(shí)時(shí)間(ms)
// 如果參數(shù)timeout被設(shè)為0則表示永遠(yuǎn)不會(huì)超時(shí)
socket.connect(remoteAddr, 60000);

InetAddress 類表示主機(jī)的P地址,提供了一系列靜態(tài)工廠方法用于構(gòu)造自身實(shí)例

// 返回本地主機(jī)的IP地址、
InetAddress addr1 = inetAddress.getLocalHost();
// 返回代表 "222.34.57” 的 IPv4 地址
InetAddress addr2 = InetAddress.getByName("222.34.5.7");
// 返同代表 ”2001:DB8:2DE::E13" 的 IPv6 地址
InetAddress addr3 = InetAddress.getByName("2001:DB8:2DE::E13");
// 返回主機(jī)名為 "www.javathinker.net" 的 IP 地址
InetAddress addr4 = InetAddress.getByName ("www.javathinker.net");

3. 設(shè)定客戶端的地址

在一個(gè) Socket 對(duì)象中既包含遠(yuǎn)程服務(wù)器的 IP 地址和端口信息,也包含本地客戶端的 IP 地址和端口信息。在默認(rèn)情況下,客戶端的 IP 地址來(lái)自客戶程序所在的主機(jī),客戶端的端口則由操作系統(tǒng)隨機(jī)分配。Socket 類還有兩個(gè)構(gòu)造方法允許顯式地設(shè)置客戶端的 IP 地址和端口

Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException

如果一個(gè)主機(jī)同時(shí)屬于兩個(gè)以上的網(wǎng)絡(luò),它就可能擁有兩個(gè)以上 IP 地址,例如一個(gè)主機(jī)在 Internet 網(wǎng)絡(luò)中的 IP 地址為 “222.67,1.34”,在一個(gè)局域網(wǎng)中的 IP 地址為 “1125.4.3",假定這個(gè)主機(jī)上的客戶程序希望和同一個(gè)局城網(wǎng)上的一個(gè)地址為 “112.5.4.4:8000” 的服務(wù)器程序通信,客戶端可按照如下方式構(gòu)造 Socket 對(duì)象

InetAddress remoteAddr = InetAddress.getByName("112.5,4.45");
InetAddress localAddr = InetAddress.getByName("112.5.4.3");
//客戶端使用口2345
Socket socket = new Socket(remoteAddr, 8000, localAddr, 2345);

4. 客戶連接服務(wù)器時(shí)可能拋出的異常

當(dāng) Socket 的構(gòu)造方法請(qǐng)求連接服務(wù)器時(shí),可能會(huì)拋出以下異常:

  • UnknownHostException:無(wú)法識(shí)別主機(jī)的名字或 IP 地址

  • ConnectException:沒(méi)有服務(wù)器進(jìn)程監(jiān)聽(tīng)指定的端口,或者服務(wù)器進(jìn)程拒絕連接

  • SocketTimeoutException:等待連接超時(shí)

  • BindException:無(wú)法把Socket 對(duì)象與指定的本地 IP 地址或端口綁定

5. 使用代理服務(wù)器

在實(shí)際應(yīng)用中,有的客戶程序會(huì)通過(guò)代理服務(wù)器來(lái)訪問(wèn)遠(yuǎn)程服務(wù)器。代理服務(wù)器有許多功能,比如能作為防火墻進(jìn)行安全防范,或者提高訪問(wèn)速度,或者具有訪問(wèn)特定遠(yuǎn)程服務(wù)器的權(quán)限

String proxyIP = "myproxy.abc.oom"; // 代理服務(wù)器地址
int proxyPort = 1080; // 代理服務(wù)器端口
// 創(chuàng)建代理對(duì)象
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyIP, proxyPort));
Socket socket  new Socket(proxy);
//連接到遠(yuǎn)程服務(wù)器
socket.connect(new InetSocketAddress("www.javathinker.net", 80));

ProxyType 類表示代理服務(wù)器的類型,有以下可選值:

  • Proxy.Type.SOCKS:在分層的網(wǎng)絡(luò)結(jié)構(gòu)中,SOCKS 是位于會(huì)話層的代理類型

  • Proxy.Type.HTTP:在分層的網(wǎng)絡(luò)結(jié)構(gòu)中,HTTP 是位于應(yīng)用層的代理類型

  • Proxy.Type.DIRECT:不使用代理,直接連接遠(yuǎn)程服務(wù)器

6. InetAddress 地址類的用法

InetAddress 類表示主機(jī)的IP 地址,InetAddress 類的靜態(tài)工廠方法給 getByName() 用于構(gòu)造自身的實(shí)例

// 返回代表 "222.34.5.7" 的 IPv4 地址
InetAddress addr2 = InetAddress,getByName("222.34.5.7");
// 返回主機(jī)名為 "www.javathinker.net" 的 IP 地址
InetAddress addr4 = InetAddress.getByName("www.javathinker.net");

InetAddress 還提供了獲取相應(yīng)的主機(jī)名的兩種方法:

  • getHostname():首先從 DNS 緩存中查找與 IP 地址匹配的主機(jī)名,如果不存在,再通過(guò) DNS 服務(wù)器查找,如果找到,則返回主機(jī)名,否則返回 IP 地址

  • getCanonicalHostName():通過(guò) DNS 服務(wù)器查找與 IP 地址匹配的主機(jī)名,如果找到則返回主機(jī)名,否則返問(wèn) IP 地址

以上兩種方法的區(qū)別在于 getHostname() 會(huì)先查找 DNS 緩存,減少查找 DNS 服務(wù)器的概率,提高查找性能。而 getCanonicalHostName() 總是查找 DNS 服務(wù)器,確保獲得當(dāng)前最新版本的主機(jī)名

InetAddress 類還提供了兩個(gè)測(cè)試能否從本地主機(jī)連接到特定主機(jī)的方法:

public boolean isReachable(int timeout) throws IOException
public boolean isReachable(NefworkInterface interface, int ttl, int timeout) throws IOException

如果遠(yuǎn)程主機(jī)在參數(shù) timeout(ms)指定的時(shí)間內(nèi)做出回應(yīng),以上方法返回true,否則返回 false,如果出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤則拋出 IOException。第二種方法還允許從參數(shù)指定的本地網(wǎng)絡(luò)接口建立連接,以及 TTL(IP 數(shù)據(jù)包被丟棄前允許存在的時(shí)間)

7. NetworkInterface 類的用法

NetworkInterfiace 類表示物理上的網(wǎng)絡(luò)接口,它有兩種構(gòu)造自身實(shí)例的靜態(tài)工廠方法,這兩種方法都聲明拋出 SocketException

// 參數(shù) name 指定網(wǎng)絡(luò)接口的名字,如果不存在與名字對(duì)應(yīng)的網(wǎng)絡(luò)接口,就返回 null
getByName(String name)
// 參數(shù) address 指定網(wǎng)絡(luò)接口的 IP 地址,如果不存在與 IP 地址對(duì)應(yīng)的網(wǎng)絡(luò)接口,就返回 null
getByInetAddress(InetAddress address)

NetworkInterface 類的以下方法用于獲取網(wǎng)絡(luò)接口的信息

// 返回網(wǎng)絡(luò)接口的名字
public String getName()
// 返回和網(wǎng)絡(luò)接口綁定的所有 IP 地址,返回值為 Enumeration 類型,里面存放了表示 IP 地址的 InetAddress 對(duì)象
public Enumeration getInetAddresses()

2|0獲取 Socket 的信息

在一個(gè) Socket 對(duì)象中同時(shí)包含了遠(yuǎn)程服務(wù)器的 IP 地址和端口信息,以及客戶本地的 IP 地址和端口信息。此外,從 Socket 對(duì)象中還可以獲得輸出流和輸入流,分別用于向服務(wù)器發(fā)送數(shù)據(jù),以及接收從服務(wù)器端發(fā)來(lái)的數(shù)據(jù)

以下方法用于獲取 Socket 的有關(guān)信息

// 獲得遠(yuǎn)程被連接進(jìn)程的IP地址
getInetAddress()
// 獲得遠(yuǎn)程被連接進(jìn)程的端口
getPort()
// 獲得本地的IP地址
getLocalAddress()
// 獲得本地的端口
getLocalPort()
// 獲得輸入流,如果Socket還沒(méi)有連接,或者已經(jīng)關(guān)團(tuán),或者已經(jīng)通過(guò)shutdownInput()方法關(guān)閉輸入流,那么此方法會(huì)拋出IOException
getInputStream()
// 獲得輸出流,如果Socket還沒(méi)有連接,或者已經(jīng)關(guān)閉,或者已經(jīng)通過(guò)shutdownOutput()方法關(guān)閉輸出流,那么此方法會(huì)拋出 IOException
getOutputStream()

3|0關(guān)閉 Socket

當(dāng)客戶與服務(wù)器的通信結(jié)束時(shí),應(yīng)該及時(shí)關(guān)閉 Socket,以釋放 Socket 占用的包括端口在內(nèi)的各種資源。Socket 的 close() 方法負(fù)責(zé)關(guān)閉 Socket,如果一個(gè) socket 對(duì)象被關(guān)閉,就不能再通過(guò)它的輸入流和輸出流進(jìn)行 IO 操作,否則會(huì)導(dǎo)致 IOException

Socket 類提供了三個(gè)狀態(tài)測(cè)試方法

// 如果Socket沒(méi)有關(guān)閉,則返回false,否則返回true
isClosed()
// 如果Socket曾經(jīng)連接到遠(yuǎn)程主機(jī),不管當(dāng)前是否已經(jīng)關(guān)閉,都返回true。如果Socket從未連接到遠(yuǎn)程主機(jī),就返回false
isConnected()
// 如果Socket已經(jīng)與一個(gè)本地端口綁定,則返回true,否則返回false
isBound()

如果要判斷一個(gè) Socket 對(duì)象當(dāng)前是否處于連接狀態(tài),可采用以下方式

String isConnected = socket.isConnected() && !socket.isClosed();

4|0半關(guān)閉 Socket

進(jìn)程 A 與進(jìn)程 B 通過(guò) Socket 通信,假定進(jìn)程 A 輸出數(shù)據(jù),進(jìn)程 B 讀入數(shù)據(jù),進(jìn)程 A 如何告訴進(jìn)程 B 所有數(shù)據(jù)已經(jīng)輸出完畢呢?有幾種處理辦法:

  • 如果進(jìn)程 A 與進(jìn)程 B 交換的是字符流,并且都一行一行地讀寫數(shù)據(jù),那么可以事先約定以一個(gè)特殊的標(biāo)志作為結(jié)束標(biāo)志,例如以字符串 “bye” 作為結(jié)束標(biāo)志,當(dāng)進(jìn)程 A 向進(jìn)程 B 發(fā)送一行字符串 “bye”,進(jìn)程 B 讀到這一行數(shù)據(jù)后,就停止讀取數(shù)據(jù)

  • 進(jìn)程 A 先發(fā)送一個(gè)消息,告訴進(jìn)程 B 所發(fā)送的正文的長(zhǎng)度,然后發(fā)送正文。進(jìn)程 B 先獲知進(jìn)程 A 將發(fā)送的正文的長(zhǎng)度,接下來(lái)只要讀取該長(zhǎng)度的字符或者字節(jié),就停止讀取數(shù)據(jù)

進(jìn)程 A 發(fā)完所有數(shù)據(jù)后,關(guān)閉 Socket,當(dāng)進(jìn)程 B 讀入了進(jìn)程 A 發(fā)送的所有數(shù)據(jù)后,再次執(zhí)行輸入流的 read() 方法時(shí),該方法返回 “-1”,如果執(zhí)行 BufferedReader 的 readLine() 方法,那么該方法返回 null

ByteArrayOutputstream bufferenew = ByteArrayOutputstream();
byte[] buff = new byte[1024);
int len = -1;
while((len = socketIn.read(buff)) != -1) {
    buffer.write(buff, 0, len);   
}
  • 當(dāng)調(diào)用 Socke t的 close() 方法關(guān)閉 Socket 后,它的輸出流和輸入流也都被關(guān)閉。有的時(shí)候,可能僅僅希望關(guān)閉輸出流或輸入流之一,此時(shí)可以采用 Socket 類提供的半關(guān)閉方法

shutdownInput() // 關(guān)閉輸入流
shutdownOutput() // 關(guān)團(tuán)輸出流

假定進(jìn)程 A 執(zhí)行以下代碼,先向進(jìn)程 B 發(fā)送一個(gè)字符串,等到進(jìn)程 B 接收到這個(gè)字符串后,進(jìn)程 A 再調(diào)用 Socket 的 shutdownOutput() 方法關(guān)閉輸出流,接下來(lái)進(jìn)程 A 不允許再輸出數(shù)據(jù),但是仍可以通過(guò)輸入流讀入數(shù)據(jù)

// 發(fā)出請(qǐng)求信息
String data = ...;
OutputStream socketOut = socket.getOutputStream();
socketOut.write(data.getBytes());
socketOut.flush();
// 讀取響應(yīng)
InputStream socketIn = socket.getInputStream();
if(服務(wù)器端返回提示信息,表明已經(jīng)接收到客戶端的所有請(qǐng)求數(shù)據(jù))
    socket.shutdownOutput(); //關(guān)閉輸出流
//繼續(xù)通過(guò)socketIn讀取數(shù)據(jù)
...

值得注意的是,先后調(diào)用 Socket 的 shutdownInput() 和 shutdownOutput() 方法,僅僅關(guān)閉了輸入流和輸出流,并不等價(jià)于調(diào)用 Socket 的 close() 方法。在通信結(jié)束后,仍然要調(diào)用 Socket 的 close() 方法,因?yàn)橹挥性摲椒ú艜?huì)釋放 Socket 占用的資源,比如占用的本地端口等

Socket 類還提供了兩種狀態(tài)測(cè)試方法,用來(lái)判斷輸入流和輸出流是否關(guān)閉

public boolean isInputShutdown() // 如果輸入流關(guān)閉,則返回true,否則返回false
public boolean isOutputShutdown() // 如果輸出流關(guān)閉,則返回true,否則返回false

5|0設(shè)置 Socket 的選項(xiàng)

1. TCP_NODELAY

表示立即發(fā)送數(shù)據(jù)。在默認(rèn)情況,下發(fā)送數(shù)據(jù)采用 Negale 算法,發(fā)送方發(fā)送的數(shù)據(jù)不會(huì)立刻被發(fā)出,而是先放在緩沖區(qū)內(nèi),等緩沖區(qū)滿了再發(fā)出。發(fā)送完一批數(shù)據(jù)后,會(huì)等待接收方對(duì)這批數(shù)據(jù)的回應(yīng),然后發(fā)送下一批數(shù)據(jù)。此算法法適用于發(fā)送方需要發(fā)送大批量數(shù)據(jù)并且接收方會(huì)及時(shí)做出回應(yīng)的場(chǎng)合,這種算法通過(guò)減少傳輸數(shù)據(jù)的次數(shù)來(lái)提高通信效率

如巢發(fā)送方持續(xù)地發(fā)送小批量的數(shù)據(jù)。并且接收方不一定會(huì)立即發(fā)送響應(yīng)數(shù)據(jù),那么 Negale 算法會(huì)使發(fā)送方運(yùn)行得很慢,對(duì)于GU程序,比如網(wǎng)絡(luò)游戲程序(服務(wù)器需要實(shí)時(shí)跟蹤客戶端鼠標(biāo)的移動(dòng)),這個(gè)問(wèn)題尤其突出

TCP_NODEALY 的默認(rèn)值為 false,表示采用 Negale 算法,如果調(diào)用 setTcpNoDelay(true) 方法,就會(huì)關(guān)閉 Socket 的緩沖,確保數(shù)據(jù)被及時(shí)發(fā)送

if(!socket.getTcpNoDelay())
    socket.setTcpNoDelay(true);

2. SO_RESUSEADDR

表示是否允許重用 Socket 所綁定的本地地址。當(dāng)接收方通過(guò) Socket 的 close() 方法關(guān)閉 Socket 時(shí),如果網(wǎng)絡(luò)上還有發(fā)送到這個(gè) Socket 的數(shù)據(jù),那么底層的 Socket 不會(huì)立刻釋放本地端口,而是會(huì)等待一段時(shí)間,確保接收到了網(wǎng)絡(luò)上發(fā)送過(guò)來(lái)的延遲數(shù)據(jù),再釋放端口。Socket 接收到延遲數(shù)據(jù)后,不會(huì)對(duì)這些數(shù)據(jù)做任何處理。Socket 接收延遲數(shù)據(jù)的目的是,確保這些數(shù)據(jù)不會(huì)被其他碰巧綁定到同樣端口的新進(jìn)程接收到

客戶程序一般采用隨機(jī)端口,因此出現(xiàn)兩個(gè)客戶程序綁定到同樣端口的可能性不大。許多服務(wù)器程序都使用固定的端口。當(dāng)服務(wù)器程序被關(guān)閉后,有可能它的端口還會(huì)被占用一段時(shí)間,如果此時(shí)立刻在同一臺(tái)主機(jī)上重啟服務(wù)器程序,由于端口已經(jīng)被占用,使得服務(wù)感程序無(wú)法綁定到該端口,導(dǎo)致啟動(dòng)失敗

為了確保當(dāng)一個(gè)進(jìn)程關(guān)閉了 Socket 后,即便它還沒(méi)釋放端口,同一臺(tái)主機(jī)上的其他進(jìn)要也可以立刻重用該端口,可以調(diào)用 Socke 的 setResuseAddress(ture) 方法

if(!socket.getResuseAddress())
    socket.setResuseAddress(true);

值得注意的是 socket.setResuseAddress(true) 方法必須在 Socket 還沒(méi)有被綁定到一個(gè)本地端口之前調(diào)用,否則執(zhí)行無(wú)效

3. SO_TIMEOUT

表示接收數(shù)據(jù)時(shí)的等待超時(shí)時(shí)間。當(dāng)通過(guò) Socket 的輸入流讀數(shù)據(jù)時(shí),如果還沒(méi)有數(shù)據(jù),就會(huì)等待。Socket 類的SO_TIMEOUT 選項(xiàng)用于設(shè)定接收數(shù)據(jù)的等待超時(shí)時(shí)間,單位為 ms,它的默認(rèn)值為0,表示會(huì)無(wú)限等待,永遠(yuǎn)不會(huì)超時(shí)

以下代碼把接收數(shù)據(jù)的等待超時(shí)時(shí)間設(shè)為三分鐘

if(socket.getTimeout() == 0)
    socket.setTimeout(60000 * 3);

Socket 的 setTimeout() 方法必須在接收數(shù)據(jù)之前執(zhí)行才有效

4. SO_LINGER

表示與執(zhí)行 Socket 的 close() 方法時(shí),是否立即關(guān)閉底層的 Socket。在默認(rèn)情況下執(zhí)行 Socket 的 close() 方法,該方法會(huì)立即返回,但底層的 Socket 實(shí)際上并不立即關(guān)閉,它會(huì)延遲一段時(shí)間,直到發(fā)送完所有剩余的數(shù)據(jù),才會(huì)真正關(guān)閉 Socket

如果執(zhí)行以下方法

socket.setSoLinger(true,0);

那么執(zhí)行 Socket 的 close() 方法,該方法也會(huì)立即返回而且底層的 Socket 也會(huì)立即關(guān)閉,所有未發(fā)送完的數(shù)據(jù)被丟棄

如果執(zhí)行以下方法

socket.setSoLinger(true,3600);

那么執(zhí)行 Socket 的 close() 方法,該方法不會(huì)立即返回,而是進(jìn)入阻塞狀態(tài),同時(shí),底層的 Socket 會(huì)嘗試發(fā)送剩余的數(shù)據(jù)。只有滿足以下兩個(gè)條件之一,close() 方法才返回:

  • 底層的 Socket 已經(jīng)發(fā)送完所有的剩余數(shù)據(jù)

  • 盡管底層的 Socket 還沒(méi)有發(fā)送完所有的剩余數(shù)據(jù),但己經(jīng)阻塞了 3600s,此時(shí) close() 也會(huì)返回,未發(fā)送的數(shù)據(jù)被丟棄

5. SO_RCVBUF

表示接收數(shù)據(jù)的緩沖區(qū)的大小。一般說(shuō)來(lái),傳輸大的連續(xù)的數(shù)據(jù)塊,比如基于 HTTP 或 FTP 的通信,可以使用較大的緩沖區(qū),這可以減少傳輸數(shù)據(jù)的次數(shù),提高傳輸數(shù)據(jù)的效率。而對(duì)于交互式的通信方式,比如 Telnet 和網(wǎng)絡(luò)游戲,則應(yīng)該采用小的緩沖區(qū),確保小批量的數(shù)據(jù)能及時(shí)發(fā)送給對(duì)方

6. SO_SNDBUF

表示發(fā)送數(shù)據(jù)的緩沖區(qū)的大小

7. SO_KEEPALIVE

表示對(duì)于長(zhǎng)時(shí)間處于空閑狀態(tài)的 Socket,是否要自動(dòng)把它關(guān)團(tuán)。當(dāng) SO_KEEPALIVE 選項(xiàng)為 tue 時(shí),表示底層的 TCP 實(shí)現(xiàn)會(huì)監(jiān)視該連接是否有效連接處于空閑狀態(tài),即連接的兩端沒(méi)有互相傳送數(shù)據(jù)超過(guò)了 2 小時(shí),本地的 TCP 實(shí)現(xiàn)發(fā)送一個(gè)數(shù)據(jù)包給遠(yuǎn)程的 Socket,如果遠(yuǎn)程 Socke 沒(méi)有返回響應(yīng),TCP 實(shí)現(xiàn)就會(huì)持續(xù)嘗試發(fā)送 11 分鐘,直到接收到響應(yīng)為止。如果在 12 分鐘內(nèi)未收到響應(yīng),TCP 實(shí)現(xiàn)就會(huì)自動(dòng)關(guān)閉本地 Socket,斷開(kāi)連接

SO_KEEPALIVE 選項(xiàng)的默認(rèn)值為 false,表示 TCP 不會(huì)監(jiān)視連接是否有效,不活動(dòng)的客戶端可能會(huì)永久存在下去,而不會(huì)注意到服務(wù)器已經(jīng)崩潰

8. IP 服務(wù)類型選項(xiàng)

當(dāng)用戶通過(guò)郵局發(fā)送普通信、掛號(hào)信或者快件時(shí),實(shí)際上選擇了郵局提供的不同的服務(wù)。發(fā)送普通信的價(jià)格最低,但發(fā)送速度慢,并且可靠性沒(méi)有保證。發(fā)送掛號(hào)信的價(jià)格稚高,但可靠性有保證。發(fā)送快件的價(jià)格最高,發(fā)送速度最快,并且可靠性有保證

在 Internet 上傳輸數(shù)據(jù)也分為不同的服務(wù)類型,它們有不同的定價(jià)。用戶可以根據(jù)自己的需求,選擇不同的服務(wù)類型。例如發(fā)送視頻需要較高的帶寬,快速到達(dá)目的地,以保證接收方看到連續(xù)的畫面,而發(fā)送電子郵件可以使用較低的帶寬,延遲幾個(gè)小時(shí)到達(dá)目的地也沒(méi)關(guān)系

IP 規(guī)定了一些服務(wù)類型,用來(lái)定性地描述服務(wù)的質(zhì)量,舉例如下:

  • 低成本:發(fā)送成本低

  • 高可靠性:保證把數(shù)據(jù)可靠地送達(dá)目的地

  • 最高吞吐量:一次可以接收或發(fā)送大批量的數(shù)據(jù)

  • 最小延遲:傳輸數(shù)據(jù)的速度快,把數(shù)據(jù)快速送達(dá)目的地

這些服務(wù)類型還可以進(jìn)行組合,例如,可以同時(shí)要求獲得高可靠性和最小延遲。服務(wù)類型存儲(chǔ)在 IP 數(shù)據(jù)包頭部的名為 IP_TOS 的 8 位字段中,Socket 類中提供了設(shè)置和讀取服務(wù)類型的方法

// 設(shè)置服務(wù)類型
public void setTrafficClass(int trafficClass) throws SocketException
// 讀取服務(wù)類型
public int getTrafficClass() throws SocketException

服務(wù)類型用 1 字節(jié)來(lái)表示,取值范圍是 0 到 255 之間的整數(shù)。這個(gè)服務(wù)類型數(shù)據(jù)也會(huì)被復(fù)制到 TCP 數(shù)據(jù)包頭部的 8 位字段中。,在目前的網(wǎng)絡(luò)協(xié)議中,對(duì)這個(gè)表示服務(wù)類型的字節(jié)又做了進(jìn)一步的細(xì)分:

  • 高六位:表示 DSCP 值,即表示不同的服務(wù)類型代碼號(hào)。DSCP 允許最多有 64 種服務(wù)類型

  • 低兩位:表示 ECN 值,即顯式擁塞通知信息

64 個(gè) DSCP 值到底表示什么含義,這是由具體的網(wǎng)絡(luò)和路由器決定的。下面是比較常見(jiàn)的 DCSP 值:

  • 默認(rèn)服務(wù)類型:000000

  • 加速轉(zhuǎn)發(fā)類型:101110,特點(diǎn)是低損耗、低延遲、低抖動(dòng)

  • 保證轉(zhuǎn)發(fā)類型:共 12 個(gè)取值,保證以指定速率傳送,見(jiàn)下表

類型

第1類(最低轉(zhuǎn)發(fā)優(yōu)先)第2類第3類第4類(最高轉(zhuǎn)發(fā)優(yōu)先)

低丟包率

001010010010011010100010

中丟包率

001100010100011100100100

高丟包率

001110010110011110100110

其中第 1 類有最低轉(zhuǎn)發(fā)優(yōu)先級(jí),第 4 類有最高轉(zhuǎn)發(fā)優(yōu)先級(jí)。也就是說(shuō),當(dāng)網(wǎng)絡(luò)出現(xiàn)阻塞時(shí),第 4 類的數(shù)據(jù)包被優(yōu)先轉(zhuǎn)發(fā)。每一類又包含了 3 個(gè)取值,其中低丟包率的服務(wù)類型丟棄數(shù)據(jù)包的概率小,而高丟包率的服務(wù)類型丟棄數(shù)據(jù)包的概率大

加速轉(zhuǎn)發(fā)類型比其他服務(wù)類型有更高的優(yōu)先級(jí),例如以下代碼使得 Socket 采用加速轉(zhuǎn)發(fā)類型來(lái)收發(fā)數(shù)據(jù):

Socket socket = new Socket("www.javathinker.net", 80);
// 0xB8 對(duì)應(yīng)二進(jìn)制數(shù)據(jù) 10111000
// 低兩位表示顯式擁塞通知,取值為 00
socket.setTrafficClass(0xB8);

值得注意的是,DCSP 值僅僅為底層的網(wǎng)絡(luò)實(shí)現(xiàn)提供一個(gè)參考,有些底層 Socket 實(shí)現(xiàn)會(huì)忽略 DCSP 值,對(duì)它不進(jìn)行任何處理

9. 設(shè)定連接時(shí)間、延遲和帶寬的相對(duì)重要性

從 JDK1.5 開(kāi)始,為 Socket 類提供了一個(gè) setPerformancePreferences() 方法

public vold setPerformancePreferences (int connectionTime, int latency, int bandwidth)

以上方法的 3 個(gè)參數(shù)表示網(wǎng)絡(luò)傳輸數(shù)據(jù)的 3 項(xiàng)指標(biāo):

  • connectionTime:表示用最少時(shí)間建立連接

  • latency:表示最小廷遲

  • bandwidth:表示最高帶寬

setPerformancePreferences() 方法被用來(lái)設(shè)定這 3 項(xiàng)指標(biāo)之間的相對(duì)要性??梢詾檫@些參數(shù)賦予任意的整數(shù)。這些整數(shù)之間的相對(duì)大小就決定了相應(yīng)參數(shù)的相對(duì)重要性。例如,如果參數(shù) connectionTime 為 2,參數(shù) latency 為 1,而參數(shù) bandwidth 為 3,就表示最高帶寬最重要,其次是最少連接時(shí)間,最后是最小延遲

值得注意的是 setPerformancePreferences() 方法所做的設(shè)置僅僅為底層的網(wǎng)絡(luò)實(shí)現(xiàn)提供一個(gè)參考,有些底層 Socket 實(shí)現(xiàn)會(huì)忽略這一設(shè)置,對(duì)它不進(jìn)行任何處理

到此,相信大家對(duì)“Java網(wǎng)絡(luò)編程Socket怎么創(chuàng)建”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI