應用層至關於OSI中的會話層,表示層,應用層。html
區別參考:http://blog.chinaunix.net/uid-22166872-id-3716751.htmljava
(1)序號:Seq序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記。算法
(2)確認序號:Ack序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效,Ack=Seq+1。設計模式
(3)標誌位:共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義以下:瀏覽器
(A)URG:緊急指針(urgent pointer)有效。服務器
(B)ACK:確認序號有效。網絡
(C)PSH:接收方應該儘快將這個報文交給應用層。多線程
(D)RST:重置鏈接。架構
(E)SYN:發起一個新鏈接。socket
(F)FIN:釋放一個鏈接。
須要注意的是:
(A)不要將確認序號Ack與標誌位中的ACK搞混了。
(B)確認方Ack=發起方Req+1,兩端配對。
Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP /IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。
Socket鏈接創建和關閉,詳見:http://www.2cto.com/net/201310/251896.html
鏈接->傳輸數據->關閉鏈接
短鏈接是指SOCKET鏈接,發送數據,接收數據後,立刻斷開鏈接。
好比:
HTTP1.0默認是短鏈接,無狀態的,瀏覽器和服務器每進行一次HTTP操做,就創建一次鏈接,但任務結束就中斷鏈接。 、
Http1.1默認是長鏈接
無狀態:協議對於事務處理沒有記憶能力;
鏈接->傳輸數據->保持鏈接 -> 傳輸數據-> 。。。 ->關閉鏈接。
創建SOCKET鏈接後,不論是否使用,一致保持鏈接。
接受方沒有接受到一個完整的包,只接受了部分;
緣由:TCP爲提升傳輸效率,將一個包分配的足夠大,致使接受方並不能一次接受完。
影響:長鏈接和短鏈接中都會出現
發送方發送的多個包數據到接收方接收時粘成一個包,從接收緩衝區看,後一包數據的頭緊接着前一包數據的尾。
分類:一種是粘在一塊兒的包都是完整的數據包,另外一種狀況是粘在一塊兒的包有不完整的包
出現粘包現象的緣由是多方面的:
1)發送方粘包:由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一包數據。若連續幾回發送的數據都不多,一般TCP會根據優化算法把這些數據合成一包後一次發送出去,這樣接收方就收到了粘包數據。
2)接收方粘包:接收方用戶進程不及時接收數據,從而致使粘包現象。這是由於接收方先把收到的數據放在系統接收緩衝區,用戶進程從該緩衝區取數據, 若下一包數據到達時前一包數據還沒有被用戶進程取走,則下一包數據放到系統接收緩衝區時就接到前一包數據以後,而用戶進程根據預先設定的緩衝區大小從系統接 收緩衝區取數據,這樣就一次取到了多包數據。
分包(1):在出現粘包的時候,咱們的接收方要進行分包處理;
分包(2):一個數據包被分紅了屢次接收;
緣由:1. IP分片傳輸致使的;2.傳輸過程當中丟失部分包致使出現的半包;3.一個包可能被分紅了兩次傳輸,在取數據的時候,先取到了一部分(還可能與接收的緩衝區大小有關係)。
影響:粘包和分包在長鏈接中都會出現
出現粘包和半包現象,是由於TCP當中,只有流的概念,沒有包的概念。
UDP不會出現半包,粘包狀況,緣由是UDP是一個完整的數據包,發送時不進行合併,所以接收的時候就不存在粘包狀況。
固定長度:每次發送固定長度的數據;
特殊標示:以回車,換行做爲特殊標示;獲取到指定的標識時,說明包獲取完整。
字節長度:包頭+包長+包體的協議形式,當服務器端獲取到指定的包長時才說明獲取完整;
參考文章(有圖):http://blog.csdn.net/pi9nc/article/details/17165171
短鏈接:不用考慮粘包的狀況;
發送數據無結構,如文件傳輸,這樣發送方只管發送,接收方只管接收存儲,也不用考慮粘包;
長鏈接:須要在鏈接後一段時間內發送不一樣結構數據;
處理方式:接收方建立一預處理線程,對接收到的數據包進行預處理,將粘連的包分開;
一個包沒有固定長度,以太網限制在46-1500字節,1500就是以太網的MTU,超過這個量,TCP會爲IP數據報設置偏移量進行分片傳輸,如今通常可容許應用層設置8k(NTFS系)的緩衝區,8k的數據由底層分片,而應用看來只是一次發送。
對於UDP,就不要太大,通常在1024至10K。注意一點,你不管發多大的包,IP層和鏈路層都會把你的包進行分片發 送,通常局域網就是1500左右,廣域網就只有幾十字節。分片後的包將通過不一樣的路由到達接收方,對於UDP而言,要是其中一個分片丟失,那麼接收方的 IP層將把整個發送包丟棄,這就造成丟包。
TCP做爲流,發包是不會整包到達的,而是源源不斷的到,那接收方就必須組包。而UDP做爲消息或數據報,它必定是整包到達接收方。
關於接收,通常的發包都有包邊界,首要的就是你這個包的長度要讓接收方知道,因而就有個包頭信息,對於TCP,接收方先收這個包頭信息,而後再收包 數據。一次收齊整個包也能夠,可要對結果是否收齊進行驗證。這也就完成了組包過程。UDP,那你只能整包接收了。要是你提供的接收Buffer過 小,TCP將返回實際接收的長度,餘下的還能夠收,而UDP不一樣的是,餘下的數據被丟棄並返回WSAEMSGSIZE錯誤。注意TCP,要是你提供的 Buffer佷大,那麼可能收到的就是多個發包,你必須分離它們,還有就是當Buffer過小,而一次收不完Socket內部的數據,那麼Socket接 收事件(OnReceive),可能不會再觸發,使用事件方式進行接收時。
//創建ServerSocket對象,監聽綁定端口
ServerSocket server=new ServerSocket(1000);
//創建接收Socket,阻塞響應
Socket client=server.accept();
//得到輸入流,用於接收客戶端信息
InputStream in=server.getInputStream();
//得到輸出流,用於輸出客戶端信息
//對輸入流進行包裝,方便使用
BufferedReader inRead=new BufferedReader(new InputStreamReader(in));
//對輸出流進行包裝,方便使用
PrintWriter outWriter=new PrintWriter(out);
while(true)
{
String str=inRead.readLine();//讀入
outWriter.println("輸出到客戶端");
outWriter.flush();//輸出
if(str.equals("end"))
{
break;
}
}
//關閉Socket
client.close();
server.close();
//使用Socket,創建與服務端的鏈接
Socket client=new Socket("127.0.0.1",1000);
InputStream in=client.getInputStream();//得到輸入流
OutputStream out=client.getOutputStream();//得到輸出流
//包裝輸入流,輸出流
BufferedReader inRead=new BufferedReader(new InputStreamReader(in));
PrintWriter outWriter=new PrintWriter(out);
//得到控制檯輸入
BufferedReader inConsole=new BufferedReader(new InputStreamReader(in));
while(true)
{
String str=inConsole.readLine();//讀取控制檯輸入
outWriter.println(str);//輸出到服務端
outWriter.flush();//刷新緩衝區
if(str.equals("end"))
{
break;
}//退出
System.out.println(inRead.readLine())//讀取服務端輸出
}
client.close();
DOS下運行客戶端
java -classpath sockettest-0.0.1-SNAPSHOT.jar cn.com.gome.sockettest.basic.ClientTest
方法1:socket.sendUrgentData(0);//發送1個字節的緊急數據,默認狀況下,服務器端沒有開啓緊急數據處理,不影響正常通訊
try{
socket.sendUrgentData(0xFF);
}catch(Exception ex){
reconnect();
}
只要對方Socket的SO_OOBINLINE屬性沒有打開,就會自動捨棄這個字節,而SO_OOBINLINE屬性默認狀況下就是關閉的。
方法2:自定義心跳字符串
傳輸字節,字符,對象【ObjectInputStream ObjectOutputStream】(實例)