Java
Socket/ServerSocket編程詳解(中文)
Socket server 和 client 通信流程圖: Socket三次握手連接圖: Socket
四次握手斷開連接圖:
Socket套接字: Socket 提供了在主機之間傳遞原始字節(jié)的功能,,以比較底層的方式訪問tcp/ip協(xié)議層,,可以在類似的文件i/o的方式實現(xiàn)這一功能。
Flush/刷新
如果向一個Socket
寫入數(shù)據(jù),,通常需要調(diào)用Flush 方法去把這個數(shù)據(jù)發(fā)送到網(wǎng)絡,。如果操作失敗,可能由于完整的請求未曾發(fā)送成功而導致持續(xù)等待響應,,如果使用穩(wěn)定的數(shù)據(jù)流方式,,不需要調(diào)用flush方法,因為數(shù)據(jù)流把先前的數(shù)據(jù)發(fā)送到了網(wǎng)絡,。
Blocking
Read /讀堵塞
由socket讀取數(shù)據(jù)時,,如果使用堵塞的讀操作,可能會導致永久的等待,。Socket的setSotimeOut方法控制了超時的期限,,在socket連接失敗的情況下,讀取數(shù)據(jù)的操作最終會被停止。 這種情況通常發(fā)生以下幾種情況: 1:本地關閉socket 2: 遠程主機/終端發(fā)送了斷開連接的信號,; 3:tcp協(xié)議實現(xiàn)在嘗試多次重發(fā)數(shù)據(jù)仍無法獲取的對方針對已發(fā)數(shù)據(jù)包的確認信息,,或者無法獲取得keep_alive的信息(如果tcp協(xié)議的keep-alive選項已經(jīng)被啟用)。另外不要和http協(xié)議的keep_alive參數(shù)相混淆(http的keep-alive選項是指客戶端和服務器之間建立有效的長連接,,避免重復建立連接的消耗,,尤其對提供靜態(tài)資源訪問的網(wǎng)站能夠很大的提高訪問效率)
Timeouts/超時
Socket 的java實現(xiàn)接口提供了setSoTimeout方法設置,希望等待完成讀取操作的時間期限,,提供了setSoLinger方法控制關閉等待期限(等待尚未發(fā)送的數(shù)據(jù),,然后關閉連接),。當一方關閉連接時,,另外一方仍會在讀取到緩沖區(qū)中的通知關閉連接數(shù)據(jù)以后關閉連接(理解存在誤差)setSoTimeout 和對方發(fā)送響應數(shù)據(jù)是否超時有關和對方何時接受數(shù)據(jù)無關。
KeepAlive/探測連接包
當網(wǎng)絡繁忙的時候,,tcp/ip無法發(fā)送數(shù)據(jù)包,,如果沒有設定socket 的setKeepalive(true),我們無法獲悉一個連接是否已經(jīng)關閉除非師徒再次進行發(fā)送操作(或者進行某些接受操作)。Java 通過設定socket的setKeepalive為true的方式要求tcp/ip協(xié)議進行心跳檢測,,不需要發(fā)送任何數(shù)據(jù)包或者應用級別的編程,。然而不幸的是我們不知道tcp/ip協(xié)議以怎樣的頻率發(fā)送心跳探測信號,如果另一方無法及時響應,,當你師徒進行讀取操作的時候就會產(chǎn)生socket的異常,,心跳包使雙方都能獲知對方是否保持連接,心跳包只是一個普通的tcp/ip的ack報文不需要搭載任何的其他數(shù)據(jù).
當應用處于閑暇狀態(tài)的時候,,你的而應用可以剪短的向彼此發(fā)送曉得心跳信息,,接受者可以完全忽視他們,但是他們強制tcp/ip協(xié)議去核實另一方是否存活,,這不是tcp/ip協(xié)議通信規(guī)范的一部分,,你需要建立自己的心跳協(xié)議,然而使用socket內(nèi)置的setkeepalive方法去要求tcp/ip進行心跳探測不使用任何數(shù)據(jù)包或者應用級別的編程實現(xiàn)看起來更加容易一些,每個終端只需間歇的發(fā)送一個包含當前些列的空的數(shù)據(jù)包,,確認信息和滑動口號就可以了,。 應用級別的心跳有點在于他們能夠使你了解兩端的應用都是否存活,而不在于只是通信軟件,。
我們知道,,TCP有一個連接檢測機制,就是如果在指定的時間內(nèi)(一般為2個小時)沒有數(shù)據(jù)傳送,,會給對端發(fā)送一 個Keep-Alive數(shù)據(jù)報,,使用的序列號是曾經(jīng)發(fā)出的最后一個報文的最后一個字節(jié)的序列號,對端如果收到這個數(shù)據(jù) ,,回送一個TCP的ACK,,確認這個字節(jié)已經(jīng)收到,這樣就知道此連接沒有被斷開。如果一段時間沒有收到對方的響應 ,,會進行重試,,重試幾次后,向?qū)Χ税l(fā)一個reset,,然后將連接斷掉,。 在Windows中,第一次探測是在最后一次數(shù)據(jù)發(fā)送的兩個小時,,然后每隔1秒探測一次,,一共探測5次,如果5次 都沒有收到回應的話,,就會斷開這個連接,。但兩個小時對于我們的項目來說顯然太長了。我們必須縮短這個時間,。 還有我發(fā)現(xiàn)我使用這API有點誤用,,因為getKeepAlive()的作用是返回:指示是否啟用?SO_KEEPALIVE?的?boolean?值。這明顯不是我的本意,,看來我只能自己寫心跳包了,,各路英雄好漢,你們有什么好的辦法嗎,?
客戶端Socket Socket socket
= new Socket(); SocketAddress remoteAddr = new InetAddress(“192.168.137.155”,8080); Socket.connet(remoteAddr,60000),;//等待建立連接的超時時間為1分鐘;
這里做一個解釋:連接到IP是192.168.137.155機器上監(jiān)聽8080端口的服務器程序,等待連接的最長時間為1分鐘,。如果一分鐘內(nèi)連接成功則connet()方法順利返回,。如果一分鐘內(nèi)出現(xiàn)某些異常,則拋出異常
SocketTimeOutException
客戶端連接服務器可能拋出的異常:
當Socket的構(gòu)造方法請求連接服務器時,,可能拋出一下異常:
1:UnKnownHostException :無法識別主機的名字或者IP地址,,就會拋出這種異常。
2:ConnectException :沒有指定的端口或者服務器進程禁止連接,,就會拋出何種異常,;
3:SocketTimeoutException :等待連接超時,
4:BindException 無法把scoket對象與指定IP和port進行綁定
獲取Socket 信息
在一個Socket對象中同時包含了遠程服務器的IP地址和端口信息,,以及客戶本地的IP地址和端口信息,,此外,從Socket對象中還可以獲得輸出流和輸入流,,分別用于向服務器發(fā)送數(shù)據(jù),,以及接受從服務器發(fā)來的數(shù)據(jù),一下方法用于獲取socket的有關信息:
1:getInetAddress():
獲取遠程服務器的IP地址 2:getPort():獲取遠程服務器的端口 3:getLocalAddress():獲取客戶端本地的IP地址,; 4:getLocalPort():獲取本地Port 5:getInputStream():獲取輸入流 6:shutdownInputStream():關閉輸入流,,此方法會拋出IOExceptin 7:
getOutputStream():獲取輸出流 8:
shutdownOutputStream():關閉輸出流
Socket
類提供了三個狀態(tài)測試方法: 1: isClosed()是否已經(jīng)關閉 2:isConnected():是否已經(jīng)連接上 3:isBound():是否綁定一個端口 未完待續(xù)……
|
|
來自: Sunny_Gql > 《網(wǎng)絡通訊》