摘要:上,數(shù)據(jù)按有限大小的包傳輸,這些包成為數(shù)據(jù)報(bào),每個數(shù)據(jù)報(bào)包含一個首部和一個有效載荷。不過,由于數(shù)據(jù)報(bào)長度有限,通常必須將數(shù)據(jù)分解為多個包,再在目的地重新組合。這兩個構(gòu)造函數(shù),在返回之前會與遠(yuǎn)程主機(jī)建立一個活動的網(wǎng)絡(luò)連接。
Internet上,數(shù)據(jù)按有限大小的包傳輸,這些包成為數(shù)據(jù)報(bào)(datagram),每個數(shù)據(jù)報(bào)包含一個首部(header)和一個有效載荷(payload)。首部包含包發(fā)送到的地址和端口、包來自的地址和端口、檢測數(shù)據(jù)是否被破壞的校驗(yàn)和,以及用于保證可靠傳輸?shù)母鞣N其他管理信息。
有效載荷包含數(shù)據(jù)本身。
不過,由于數(shù)據(jù)報(bào)長度有限,通常必須將數(shù)據(jù)分解為多個包,再在目的地重新組合。也有可能一個包或多個包在傳輸中丟失或遭到破壞,需要重傳?;蛘甙鼇y序到達(dá),需要重新排序。所有這些(將數(shù)據(jù)分解為包、生成首部、解析入站包的首部、跟蹤哪些包已經(jīng)收到而哪些沒有收到等)是很繁重的工作,需要大量復(fù)雜的代碼。
Socket幫你掩蓋了這些底層細(xì)節(jié),如錯誤檢測、包大小、包分解、包重傳、網(wǎng)絡(luò)地址等。Socket允許程序員將網(wǎng)絡(luò)連接看作是另外一個可以讀寫字節(jié)的流。
Socket是兩臺主機(jī)之間的一個連接,它可以完成7個基本操作:
1)連接遠(yuǎn)程主機(jī)
2)發(fā)送數(shù)據(jù)
3)接收數(shù)據(jù)
4)關(guān)閉連接
5)綁定端口
6)監(jiān)聽入站數(shù)據(jù)
7)在綁定端口上接受來自遠(yuǎn)程機(jī)器的連接
一旦建立了socket連接,就可以使用輸入輸出流,這個連接是全雙工的(full-duplex),兩臺主機(jī)都可以同時發(fā)送和接收數(shù)據(jù)。
SMTP是服務(wù)器之間或郵件客戶端與服務(wù)器之間傳輸電子郵件所用的協(xié)議。
半關(guān)閉Socket:close方法同時關(guān)閉Socket的輸入流和輸出流,如果只希望關(guān)閉連接的一半(輸入/輸出),調(diào)用shutdownInput或shutdownOutput方法即可。這兩個方法并不關(guān)閉Scoket,實(shí)際上,它會調(diào)整與Socket連接的流,使它認(rèn)為已經(jīng)到了流的末尾。關(guān)閉輸入之后,再讀取輸入流會返回-1,關(guān)閉輸出流之后再寫入Socket則會拋出一個IOException異常。
即使半關(guān)閉了連接,或者將連接的兩半都關(guān)閉了,使用結(jié)束后還是需要關(guān)閉該Socket。shutdown只是影響了socket流,并不釋放與socket關(guān)聯(lián)的資源,如占用的端口等。
java.net.Socket是Java完成客戶端TCP操作的基礎(chǔ)類,它使用原生代碼與主機(jī)操作系統(tǒng)的本地TCP棧進(jìn)行通信。
public Socket(String host, int port) throws UnknownHostException, IOException
public Socket(InetAddress host, int port) throws IOException
這兩個構(gòu)造函數(shù),在返回之前會與遠(yuǎn)程主機(jī)建立一個活動的網(wǎng)絡(luò)連接。port在1~65535之間。
public Socket()
public Socket(Proxy proxy)
protected Socket(SocketImpl impl)
這三個函數(shù)可以創(chuàng)建未連接的Socket。
public Socket(String host, int port, InetAddress interface, int localPort)
throws IOException, UnknownHostException
public Socket(InetAddress host, int port, InetAddress interface, int localPort)
throws IOException
這兩個構(gòu)造函數(shù)可以用來指定從哪個接口和端口連接。
SocketAddress
SocketAddress表示一個連接端點(diǎn),理論上可以用于TCP和非TCP socket。實(shí)際上只支持TCP/IP Socket。
SocketAddress主要是為了暫時的socket連接信息(如IP地址和端口)提供一個方便的存儲,即使最初的socket已斷開并被垃圾回收,這些信息也可以重用來創(chuàng)建新的Socket。
boolean connected = socket.isConnected() && ! socket.isClosed();
isConnected表示是否連接過一個遠(yuǎn)程主機(jī),即使socket已經(jīng)關(guān)閉,因而要判斷socket是否打開著的,還要判斷是否已經(jīng)關(guān)閉了。
Socket選項(xiàng) 1)TCP_NODELAY設(shè)置為true,可確保包會盡可能快地發(fā)送,而不論包的大小。
正常情況下,小數(shù)據(jù)包在發(fā)送前會組合為更大的包,在發(fā)送另一個包之前,本地主機(jī)要等待遠(yuǎn)程系統(tǒng)對前一個包的確認(rèn),這稱為Nagle算法。
Nagle算法的問題是如果遠(yuǎn)程系統(tǒng)沒有足夠快地將確認(rèn)發(fā)送回本地系統(tǒng),那么依賴于小數(shù)據(jù)量信息穩(wěn)定傳輸?shù)膽?yīng)用程序會變慢。對于網(wǎng)絡(luò)或游戲等計(jì)算機(jī)應(yīng)用程序(服務(wù)器需要實(shí)時跟蹤客戶端鼠標(biāo)的移動),這個問題尤為嚴(yán)重,在一個相當(dāng)慢的網(wǎng)絡(luò)中,即使簡單地打字也會由于持續(xù)的緩沖而變得太慢。設(shè)置為true,可以關(guān)閉這種緩沖模式,這樣素有包一旦就緒就會發(fā)送。
2)SO_LINGER指定了Socket關(guān)閉時如何處理尚未發(fā)送的數(shù)據(jù)報(bào),默認(rèn)情況下,close方法將立即返回,但系統(tǒng)仍然會嘗試發(fā)送剩余的數(shù)據(jù),如果延遲時間設(shè)置為0,那么當(dāng)Socket關(guān)閉時,所有未發(fā)送的數(shù)據(jù)包都將被丟棄,如果SO_LINGER打開而且延遲時間設(shè)置為任意正數(shù),close方法會阻塞指定的時間,等待發(fā)送數(shù)據(jù)和接收確認(rèn)。指定時間一過,Socket關(guān)閉,所有剩余的數(shù)據(jù)都不會發(fā)送,也不會接收確認(rèn)。
返回-1表示該選項(xiàng)被禁用,會根據(jù)需要用更多的時間發(fā)送剩余的數(shù)據(jù)。
正常情況下,嘗試從Socket讀取數(shù)據(jù)時,read()調(diào)用會阻塞盡可能長的時間來得到足夠的字節(jié)。設(shè)置timeout確保這個調(diào)用會阻塞的時間不會超過指定的閾值,如果超出則拋異常,但是Socket仍然是連接的,可以再次嘗試肚餓去這個Socket,下一次調(diào)用可能會成功。
0表示無限超時。
TCP使用緩沖區(qū)來提升網(wǎng)絡(luò)性能,較大的緩沖區(qū)會提升快速連接(比如10M/s)的性能,而較慢的撥號連接利用較小的緩沖區(qū)會有更好地表現(xiàn)。
一般來講,傳輸連續(xù)的大數(shù)據(jù)塊時(在ftp和http中較為常見),可以從大緩沖區(qū)收益,而對于交互式會話的小數(shù)據(jù)量傳輸(比如telnet和很多游戲),大緩沖區(qū)則沒有多大幫助。如今128K字節(jié)已經(jīng)是一個常見的默認(rèn)設(shè)置。
可以達(dá)到的最大帶寬=緩沖區(qū)大小/延遲。例如,xp上,假設(shè)兩個主機(jī)之間的延遲為500ms,xp上的緩沖區(qū)大小為17520字節(jié),則帶寬=17520/0.5=273.75kb/s。這是Socket的最大速度,而不論網(wǎng)絡(luò)速度有多快。對于一個撥號連接來說,這樣的速度已經(jīng)很快了。
可以通過減少延遲來提升速度,不過,延遲與網(wǎng)絡(luò)硬件有關(guān),另外還取決于你的應(yīng)用控制之外的其他一些因素。
如果把緩沖區(qū)從17520字節(jié)提升到128K,則最大帶寬會增加到2Mb/s,如果加到256K時,最大帶寬會增大到4Mb/s。
當(dāng)然網(wǎng)絡(luò)本身對最大帶寬也是有限制的,如果將緩沖區(qū)設(shè)置的過高,程序會試圖以過高的速度發(fā)送和接收數(shù)據(jù),而網(wǎng)絡(luò)來不及處理,這就會導(dǎo)致?lián)砣?、丟包和性能下降。因此,要得到最大帶寬,需要讓緩沖區(qū)大小與連接的延遲匹配,是它稍小于網(wǎng)絡(luò)的帶寬。
可以用ping去檢測主機(jī)的延遲。
SO_RCVBUF控制用于網(wǎng)絡(luò)輸入的建議的接收緩沖區(qū)的大小,雖然可以獨(dú)立地設(shè)置接收和發(fā)送緩沖區(qū)的大小,但是實(shí)際上緩沖區(qū)通常會設(shè)置為二者中較小的一個。
Linux系統(tǒng)通常指定一個最大緩沖區(qū)大小,一般是64KB或256KB,而且不允許任何socket有更大的緩沖區(qū)。
一般情況下,如果你發(fā)送你的應(yīng)用不能充分利用可用帶寬(例如,你有一個25Mb/s的Internet連接,但是數(shù)據(jù)傳輸速率僅為1.5Mb/s),那么可以試著增加緩沖區(qū)的大?。幌喾?,如果存在丟包和擁塞現(xiàn)象,則要減少緩沖區(qū)大小。
不過,大部分情況,除非網(wǎng)絡(luò)在某個方向上負(fù)載過大,否則默認(rèn)值就很合適。具體來說,現(xiàn)代操作系統(tǒng)使用TCP窗口縮放來動態(tài)調(diào)整緩沖區(qū)的大小,以適應(yīng)網(wǎng)絡(luò)。
一般經(jīng)驗(yàn)是,除非你檢測到某個問題,否則不要進(jìn)行調(diào)整。一般調(diào)整操作系統(tǒng)的最大緩沖區(qū)比在Java里頭調(diào)整單個socket的緩沖區(qū)大小效益要高。
5)SO_KEEPALIVE如果打開這個,客戶端偶爾會通過一個空閑連接發(fā)送一個數(shù)據(jù)包(一般兩小時一次),以確保服務(wù)器沒有崩潰。如果服務(wù)器沒能響應(yīng)這個包,客戶端會持續(xù)嘗試11分鐘多的時間,直到接收到響應(yīng)為止。如果在12分鐘內(nèi)未收到響應(yīng),則客戶端就關(guān)閉socket。如果不打開這個,不活動的客戶端可能會永遠(yuǎn)存在下去,而不會注意到服務(wù)器是否已經(jīng)崩潰。
6)OOBINLINETCP包括一個可以發(fā)送單字節(jié)帶外(Out Of Band,OOB)緊急數(shù)據(jù)的特性。這個數(shù)據(jù)會立即發(fā)送,此外,當(dāng)接收方收到緊急數(shù)據(jù)時會得到通知,在處理其他已收到的數(shù)據(jù)之前可以選擇先處理這個緊急數(shù)據(jù)(必要時flush當(dāng)前緩沖區(qū))。
Java里對應(yīng)的方法是sendUrgentData
默認(rèn)情況下,Java會忽略從Socket接收的緊急數(shù)據(jù),如果希望接收到正常數(shù)據(jù)中的緊急數(shù)據(jù),需要setOOBInline為true。一旦開啟,到達(dá)的任何緊急數(shù)據(jù)將以正常方式放在Socket的輸入流中等待讀取。
7)SO_REUSEADDR一個Socket關(guān)閉時,可能不會立即釋放本地端口,尤其是當(dāng)Socket關(guān)閉時若仍有一個打開的連接,就不會釋放本地端口,有時會等待一小段時間,確保接收到所有要發(fā)送到這個端口的延遲數(shù)據(jù)包,Socket關(guān)閉時這些數(shù)據(jù)包可能仍在網(wǎng)絡(luò)上傳輸,系統(tǒng)不會對接收到的延遲包做任何處理,只是希望確保這些數(shù)據(jù)不會意外地傳入綁定到同一端口的新進(jìn)程。
如果使用隨機(jī)端口,則問題不大,但是如果Socket綁定到一個已知的端口,可能會有問題,因?yàn)檫@會阻止所有其他Socket同時使用這個端口,如果開啟這個參數(shù)(默認(rèn)是關(guān)閉),則允許另外一個Socket綁定到這個端口,即使此時仍有可能存在前一個Socket未接收的數(shù)據(jù)。
setReuseAddress必須在綁定新Socket之前調(diào)用。只有之前連接的Socket和重用老地址的新Scoket的這個值都設(shè)置為true,才能生效。
8)IP_TOS不同類型的Internet服務(wù)對性能有不同的需求,比如視頻要求相對較高的帶寬和較短的延遲,而email可以通過較低帶寬的連接傳遞等。
服務(wù)類型存儲在IP首部中的一個名為IP_TOS的8位字段中。在Java中使用setTrafficClass來設(shè)定,java里頭是0-255,但是TCP首部要求是8位,因而只能使用int的低字節(jié)。
Socket異常
1)BindException,端口被占用或沒有權(quán)限使用該端口
2)ConnectException,連接遠(yuǎn)程主機(jī)被拒絕(遠(yuǎn)程主機(jī)忙或者沒有進(jìn)程監(jiān)聽該端口)
3)NoRouteToHostException,連接超時
4)ProtocolException,違反TCP/IP規(guī)定
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/65923.html
SSL,Secure Sockets Layer,安全Socket層TLS,Transport Layer Security,傳輸層安全協(xié)議 package network.secure; import java.io.*; import javax.net.ssl.*; public class HTTPSClient { public static void main(Strin...
摘要:單個請求范圍之外的異??赡軙P(guān)閉服務(wù)器??蛻舳丝赡艹瑫r或崩潰,用戶可能取消事務(wù),網(wǎng)絡(luò)可能在流量高峰期間癱瘓,黑客可能發(fā)動拒絕服務(wù)攻擊。如果默認(rèn)長度不夠大,一些的構(gòu)造函數(shù)還允許改變這個隊(duì)列的長度,不能不能超過操作系統(tǒng)支持的最大值。 ServerSocket的生命周期 一個ServerSocket的基本生命周期:1)使用一個ServerSocket構(gòu)造函數(shù)在一個特定端口創(chuàng)建一個新的Serv...
閱讀 2516·2021-10-11 10:57
閱讀 1360·2021-10-09 09:59
閱讀 2080·2019-08-30 15:53
閱讀 3279·2019-08-30 15:53
閱讀 1088·2019-08-30 15:45
閱讀 828·2019-08-30 15:44
閱讀 3518·2019-08-30 14:24
閱讀 1024·2019-08-30 14:21