福利 => 每天都推送
不多說,直接上干貨!
TCP和UDP是兩個傳輸層協議,廣泛應用於網絡中不同主機之間傳輸數據。對任何程序員來說,熟悉TCP和UDP的工作方式都是至關重要的。這就是為什么TCP和UDP是一個流行的Java編程面試問題。
Java開發人員應該理解這兩個協議的理由是, java廣泛用於編寫多線程、並行以及可伸縮的服務器程序。Java還提供了豐富的基於TCP和UDP套接字編程API。
什是TCP?
TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議。
TCP是面向連接的,可靠的,緩慢的,可靠交付以及保證消息順序的。
所謂千言不勝一圖。

TCP充分實現了數據傳輸時各種控制功能,可以進行丟包的重發控制,還可以對次序亂掉的分包進行順序控制。而這些在UDP中都沒有。此外,TCP作為一種面向有連接的協議,只有在確認通信對端存在時才會發送數據,從而可以控制通信流量的浪費。TCP通過檢驗和、序列號、確認應答、重發控制、連接管理以及窗口控制等機制實現可靠性傳輸。
什么是UDP?
UDP(User Datagram Protocol 用戶數據報協議)是一種面向無連接的、不可靠的、基於報文的傳輸層通信協議。
UDP是無連接的,不可靠的,沒有序列保證,但是一個快速傳輸的協議。
既然有了保證可靠傳輸的TCP協議,為什么還要非可靠傳輸的UDP協議呢?
主要的原因有兩個。
(1)可靠的傳輸是要付出代價的,對數據內容正確性的檢驗必然占用計算機的處理時間和網絡的帶寬,因此TCP傳輸的效率不如UDP高。
(2)在許多應用中並不需要保證嚴格的傳輸可靠性,比如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些.
TCP和UDP的異同點:
它倆都是處於計算機網絡OSI模型的第四層(傳輸層)中。
TCP層是位於IP層之上。同樣,對於UPD也是位於IP層之上。
如果你不想丟失任何消息,使用TCP協議,而UDP能夠高速傳輸數據,並且丟失少量的數據包是可以接受的,如視頻流或在線多玩家游戲。
我將從十大點UDP和TCP之間的差異,例如,連接步驟,排序,速度,可靠性,開銷,頭大小,擁塞控制,應用以及基於TCP和UDP協議不同,它們如何傳輸數據等方面進行深入剖析。
(1)基於連接vs無連接
它們之間的第一點並且最重要的區別是:TCP是面向連接的協議,而UDP是無連接的協議。
對於TCP,這意味着當一個客戶端和一個服務器通過TCP發送數據之前,必須先建立連接,他們可以通過TCP發送數據。建立連接的過程也被稱為TCP握手,它通過控制消息在客戶端和服務器之間互換來實現。下面的圖形象描述了TCP握手過程。客戶端,它也是TCP連接的發起者,發送一個SYN消息給服務器,該服務器端正在監聽某個TCP端口。服務器接收該消息並發送一個SYN-ACK消息,客戶端接受到該消息之后會再回一個ACK消息。一旦服務器收到ACK消息,TCP連接就建立成功,准備數據傳輸了。
另一方面,UDP是無連接的協議,和點對點連接之前不需要發送消息。這就是為什么,UDP更加適合消息的多播發布,從單個點向多個點傳輸消息。

TCP握手的過程圖
(2)可靠性
TCP提供交付保證,這意味着一個使用TCP協議發送的消息是保證交付給客戶端的。如果消息在傳輸過程中丟失,那么它將重發,這是由TCP協議本身控制的。
另一方面,UDP是不可靠的,它不提供任何交付的保證。一個數據報包在運輸途中可能會丟失,這就是為什么UDP是不適合保證交付的項目。
(3)有序性
除了提供交付保證,為TCP也保證了消息的有序性。該消息將以從服務器端發出的同樣的順序發送到客戶端,盡管這些消息到網絡的另一端時可能是無序的。TCP協議將會為你排好序。UDP不提供任何有序性或序列性的保證。數據包將以任何可能的順序到達。這就是為什么TCP是適合需要順序交付方式的應用,盡管有基於UDP的協議通過使用序列號和重傳來提供有序和可靠性的應用,如TIBCO Rendezvous,它實際上就是一個基於UDP的應用。
(4)數據邊界
TCP不保存數據的邊界,而UDP保證。TCP在傳輸控制協議,數據以字節流的形式發送,並沒有明顯的標志表明傳輸信號消息(段)的邊界。
在UDP中,數據包單獨發送的,只有當它們到達時,才會再次集成。包有明確的界限來哪些包已經收到,這意味着在消息發送后,在接收器接口將會有一個讀操作,來生成一個完整的消息。雖然TCP也將在收集所有字節之后生成一個完整的消息,但是這些信息在傳給傳輸給接受端之前將儲存在TCP緩沖區,以確保更好的使用網絡帶寬。
(5)速度
總而言之,TCP速度比較慢,而UDP速度比較快,因為TCP必須創建連接,以保證消息的可靠交付和有序性,他需要做比UDP多的多的事。這就是為什么UDP更適用於對速度比較敏感的應用,例如:在線視頻媒體,電視廣播和多人在線游戲。
(6)重量級vs輕量級
由於上述的開銷,TCP被認為是重量級的協議,而與之相比,UDP協議則是一個輕量級的協議。因為UDP傳輸的信息中不承擔任何間接創造連接,保證交貨或秩序的的信息。這也反映在用於承載元數據的頭的大小。
(7)頭大小
TCP具有比UDP更大的頭。一個TCP數據包報頭的大小是20字節,UDP數據報報頭是8個字節。TCP報頭中包含序列號,ACK號,數據偏移量,保留,控制位,窗口,緊急指針,可選項,填充項,校驗位,源端口和目的端口。而UDP報頭只包含長度,源端口號,目的端口,和校驗和。下圖是TCP和UDP頭:

(8)擁塞控制和流量控制
這里,我先談及擁塞控制和流量控制的區別。
a. 擁塞控制和流量控制的區別
流量控制是在一對給定的發送方和接收方之間點對點通信量的控制。流量控制所要做的就是抑制發送端發送數據的速率,以便使接收端來得及接收。
擁塞控制是一個全局性的過程,涉及到網絡中所有的主機、所有的路由器,以及與降低網絡傳輸性能有關的所有因素。
b. 擁塞控制和流量控制的聯系
流量控制限制了進入網絡中的信息總量,可以在一定程度上減緩擁塞的作用。
TCP有流量控制。在任何用戶數據可以被發送之前,TCP需要三數據包來設置一個套接字連接。TCP處理的可靠性和擁塞控制。另一方面,UDP不能進行流量控制。
以下可以簡單地說明,為什么TCP有流量控制,UDP沒有流量控制。
(1)可靠的傳輸是要付出代價的,對數據內容正確性的檢驗必然占用計算機的處理時間.和網絡的帶寬,因此TCP傳輸的效率不如UDP高。
(2)在許多應用中並不需要保證嚴格的傳輸可靠性,比如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。
(9)使用和應用
在互聯網中,TCP和UDP都運行在哪些環境中了?在了解了TCP和UDP之間的關鍵差異之后,我們可以很容易地得出結論,哪種情況適合他們。
由於TCP提供可靠交付和有序性的保證,它是最適合需要高可靠並且對傳輸時間要求不高的應用。
UDP是更適合的應用程序需要快速,高效的傳輸的應用,如游戲、視頻流或在線多玩家游戲、傳輸視頻等。
UDP是無狀態的性質,在服務器端需要對大量客戶端產生的少量請求進行應答的應用中是非常有用的。在實踐中,TCP被用於金融領域,如FIX協議是一種基於TCP的協議,而UDP是大量使用在游戲和娛樂場所。
(10)基於TCP和UDP的協議
基於TCP協議的最好例子是HTTP協議和HTTPS協議,他們幾乎存在於互聯網的任何地方,實際上,絕大多數你所熟悉的通常協議,都是基於TCP的,例如:Telnet,FTP以及SMTP協議。
更具體,可見
牛客網Java刷題知識點之UDP協議是否支持HTTP和HTTPS協議?為什么?TCP協議支持嗎?
UDP協議沒有TCP協議那么受歡迎,但是也被廣泛應用,比如DHCP以及DNS協議,其他還有一些基於UDP的協議如SNMP,TFTP,BOOTP以及NFS(早期版本)。



TCP和UDP典型的應用(筆試面試常考)
TCP的三次握手
UDP提供服務的應用:RPC、RTP。
由於TCP提供可靠交付和有序性的保證,它是最適合需要高可靠並且對傳輸時間要求不高的應用。
UDP是更適合的應用程序需要快速,高效的傳輸的應用,如游戲、視頻流或在線多玩家游戲、傳輸視頻等。
UDP是無狀態的性質,在服務器端需要對大量客戶端產生的少量請求進行應答的應用中是非常有用的。在實踐中,TCP被用於金融領域,如FIX協議是一種基於TCP的協議,而UDP是大量使用在游戲和娛樂場所。
騰訊QQ在傳輸消息有采用UDP,當時好奇UDP傳輸協議不可靠為啥騰訊要用這個協議,聯想到我們在用qq過程中會發現qq消息其實算很快的,所以可能是想應用UDP協議效率高,速度快,占用資源少的特點吧,但是其實QQ也不完全是純粹的UDP協議,至於這個這里就不討論了。
QQ 為什么以 UDP 協議為主,以 TCP 協議為輔?具體,大家可以見http://blog.csdn.net/chenlycly/article/details/52344582
socket是什么?(筆試面試常考)
這是為了實現以上的通信過程而建立成來的通信管道,其真實的代表是客戶端和服務器端的一個通信進程,雙方進程通過socket進行通信,而通信的規則采用指定的協議。
socket只是一種連接模式,不是協議,socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用接口(API)。
通過Socket,我們才能使用TCP/IP協議。tcp、udp,簡單的說(雖然不准確)是兩個最基本的協議。
很多其它協議都是基於這兩個協議。如http就是基於tcp的,用socket可以創建tcp連接,也可以創建udp連接。
這意味着,用socket可以創建任何協議的連接,因為其它協議都是基於此的。
一個Socket實例由一個IP地址和一個端口號唯一確定。
Socket是一種用於網絡通信的低層開發接口,借助於通信兩端的應用程序(實際上是Socket)可以利用輸入輸出流完成數據交換。


Socket 傳輸的優點:
1) 傳輸數據為字節級,傳輸數據可自定義,數據量小(對於手機應用講:費用低);
2)傳輸數據時間短,性能高;
3)適合於客戶端和服務器端之間信息實時交互;
4)可以加密,數據安全性強。
Socket 傳輸的缺點:
1)需對傳輸的數據進行解析,轉化成應用級的數據;
2)對開發人員的開發水平要求高;
3)相對於Http協議傳輸,增加了開發量。
Java中的Socket編程接口介紹
Java為Socket編程封裝了幾個重要的類。
1.1 Socket類
Socket類實現了一個客戶端socket,作為兩台機器通信的終端,默認采用的傳輸層協議為TCP,是一個可靠傳輸的協議。Socket類除了構造函數返回一個socket外,還提供了connect, getOutputStream, getInputStream和close方法。connect方法用於請求一個socket連接,getOutputStream用於獲得寫socket的輸出流,getInputStream用於獲得讀socket的輸入流,close方法用於關閉一個流。
1.2 DatagramSocket類
DatagramSocket類實現了一個發送和接收數據報的socket,傳輸層協議使用UDP,不能保證數據報的可靠傳輸。DataGramSocket主要有send, receive和close三個方法。send用於發送一個數據報,Java提供了DatagramPacket對象用來表達一個數據報。receive用於接收一個數據報,調用該方法后,一直阻塞接收到直到數據報或者超時。close是關閉一個socket。
1.3 ServerSocket類
ServerSocket類實現了一個服務器socket,一個服務器socket等待客戶端網絡請求,然后基於這些請求執行操作,並返回給請求者一個結果。ServerSocket提供了bind、accept和close三個方法。bind方法為ServerSocket綁定一個IP地址和端口,並開始監聽該端口。accept方法為ServerSocket接受請求並返回一個Socket對象,accept方法調用后,將一直阻塞直到有請求到達。close方法關閉一個ServerSocket對象。
1.4 SocketAddress
SocketAddress提供了一個socket地址,不關心傳輸層協議。這是一個虛類,由子類來具體實現功能、綁定傳輸協議。它提供了一個不可變的對象,被socket用來綁定、連接或者返回數值。
1.5 InetSocketAddress
InetSocketAddress實現了IP地址的SocketAddress,也就是有IP地址和端口號表達Socket地址。如果不制定具體的IP地址和端口號,那么IP地址默認為本機地址,端口號隨機選擇一個。
1.6. DatagramPacket
DatagramSocket是面向數據報socket通信的一個可選通道。數據報通道不是對網絡數據報socket通信的完全抽象。socket通信的控制由DatagramSocket對象實現。DatagramPacket需要與DatagramSocket配合使用才能完成基於數據報的socket通信。

TCP的網絡編程(筆試面試常考)
使用TCP套接字編程可以實現基於TCP/IP協議的面向連接的通信,它分為服務器端和客戶端兩部分,其主要實現過程如下:


TCP編程的客戶端一般步驟
1、創建一個socket,用函數socket();
2、設置socket屬性,用函數setsockopt();* 可選
3、綁定IP地址、端口等信息到socket上,用函數bind();* 可選
4、設置要連接的對方的IP地址和端口等屬性;
5、連接服務器,用函數connect();
6、收發數據,用函數send()和recv(),或者read()和write();
7、關閉網絡連接;
TCP編程的服務器端一般步驟
1、創建一個socket,用函數socket();
2、設置socket屬性,用函數setsockopt(); * 可選
3、綁定IP地址、端口等信息到socket上,用函數bind();
4、開啟監聽,用函數listen();
5、接收客戶端上來的連接,用函數accept();
6、收發數據,用函數send()和recv(),或者read()和write();
7、關閉網絡連接;
8、關閉監聽;
下面是基於TCP連接的Socket通信。是Server端等待從Client端接收一條消息,然后再給客戶端發送一個消息。
基於TCP服務端的思路:服務器端首先實例化ServerSocket對象,然后為其綁定一個本機地址,並開始監聽。一直阻塞狀態下等待客戶端請求,當獲得客戶端連接請求后,返回一個socket對象。然后用這個socket接收一條消息,並發送一條消息。
代碼如下:
package server.socket.java; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; public class SocketTcp { static private String TAG = "SocketTcp: "; public static void main(String[] args){ try { ServerSocket server = new ServerSocket(); SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 10001); server.bind(address); System.out.println("==waiting for being connected..."); Socket client = server.accept(); System.out.println("==connected with " + client.getRemoteSocketAddress() ); PrintWriter socketOut = new PrintWriter(client.getOutputStream()); System.out.println("==waiting message from client..."); byte buf[] = new byte[1024]; if ( client.getInputStream().read(buf) > 0 ) { System.out.println("Receive Message: " + new String(buf)); } System.out.println("==sending message to client..."); String sendStr = "This is the message for client."; socketOut.write(sendStr); socketOut.flush(); socketOut.close(); client.close(); server.close(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } } }
基於TCP的客戶端思路:客戶端首先實例化一個socket對象,用這個對象連接服務器端。連接成功后,發送一條消息,然后等待接收一條消息。代碼如下:
package client.socket.java; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; public class SocketTcp { static private String TAG = "SocketTcp: "; public static void main(String[] args){ try { final Socket socket = new Socket(); SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 10001); System.out.println("==connecting to server ..."); socket.connect(address); PrintWriter socketOut = new PrintWriter(socket.getOutputStream()); BufferedReader socketIn = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); String sendStr = "This is the message for server."; System.out.println("==sending message to server ..."); socketOut.write(sendStr); socketOut.flush(); System.out.println("==waiting message from server ..."); String receiveStr = socketIn.readLine(); System.out.println("Receive Message: " + receiveStr); socketOut.close(); socketIn.close(); socket.close(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } finally { } } }
UDP的網絡編程(筆試面試常考)
使用UDP套接字編程可以實現基於UDP/IP協議的面向連接的通信,它分為服務器端和客戶端兩部分,其主要實現過程如下:

UDP編程的客戶端一般步驟
1、創建一個socket,用函數socket();
2、設置socket屬性,用函數setsockopt();* 可選
3、綁定IP地址、端口等信息到socket上,用函數bind();* 可選
4、設置對方的IP地址和端口等屬性;
5、發送數據,用函數sendto();
6、關閉網絡連接;
UDP編程的服務器端一般步驟
1、創建一個socket,用函數socket();
2、設置socket屬性,用函數setsockopt();* 可選
3、綁定IP地址、端口等信息到socket上,用函數bind();
4、循環接收數據,用函數recvfrom();
5、關閉網絡連接;
基於UDP的Socket編程與基於TCP的socket編程稍有不同,socket server和client都用DatagramSocket實現。
下面例子是Server端等待從Client端接收一條消息,然后再給客戶端發送一個消息。
基於UDP的服務端思路:服務器端首先實例化DatagramSocket對象,然后為其綁定一個本機地址,並開始監聽。一直阻塞狀態下等待從客戶端接收數據報。然后從數據報中獲取數據報的源地址,然后用這個源地址作為目的地址打包一個數據報,然后發送出去。
代碼如下:
package server.socket.java; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; public class SocketUdp { final private static String TAG = "SocketUdp: "; public static void main(String args[]) { DatagramSocket socket = null; DatagramPacket datapacket = null; InetSocketAddress address = null; try { address = new InetSocketAddress(InetAddress.getLocalHost(), 7778); socket = new DatagramSocket(address); // socket.bind(address); byte buf[] = new byte[1024]; datapacket = new DatagramPacket(buf, buf.length); System.out.println("==block for receive messages..."); socket.receive(datapacket); buf = datapacket.getData(); InetAddress addr = datapacket.getAddress(); int port = datapacket.getPort(); System.out.println("Message Content: " + new String(buf) ); System.out.println("Receive From " + addr + ":" + port); SocketAddress toAddress = datapacket.getSocketAddress(); String sendStr = "I'm Server, this is the message for client."; buf = sendStr.getBytes(); datapacket = new DatagramPacket(buf, buf.length); datapacket.setSocketAddress(toAddress); socket.send(datapacket); System.out.println("==message sended"); } catch (UnknownHostException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (SocketException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } } }
基於UDP的客戶端思路: 客戶端首先實例化一個DatagramSocket對象。利用服務器地址和端口號作為目的地址打包一個數據報,並發送。然后等待從服務器回復的數據報。
代碼如下:
package client.socket.java; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; public class SocketUdp { final private static String TAG = "SocketUdp: "; public static void main(String args[]) { try { DatagramSocket getSocket = new DatagramSocket(); DatagramPacket datapacket = null; InetSocketAddress toAddress = new InetSocketAddress(InetAddress.getLocalHost(), 7778); String sendStr = "I'm client, this is the message for server."; byte buf[] = sendStr.getBytes(); datapacket = new DatagramPacket(buf, buf.length); datapacket.setSocketAddress(toAddress); getSocket.send(datapacket); System.out.println("==message sended"); System.out.println("==block for receive messages..."); getSocket.receive(datapacket); buf = datapacket.getData(); System.out.println("Message Content: " + new String(buf)); } catch (SocketException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (UnknownHostException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } } }
同時,大家可以關注我的個人博客:
http://www.cnblogs.com/zlslch/ 和 http://www.cnblogs.com/lchzls/ http://www.cnblogs.com/sunnyDream/
詳情請見:http://www.cnblogs.com/zlslch/p/7473861.html
人生苦短,我願分享。本公眾號將秉持活到老學到老學習無休止的交流分享開源精神,匯聚於互聯網和個人學習工作的精華干貨知識,一切來於互聯網,反饋回互聯網。
目前研究領域:大數據、機器學習、深度學習、人工智能、數據挖掘、數據分析。 語言涉及:Java、Scala、Python、Shell、Linux等 。同時還涉及平常所使用的手機、電腦和互聯網上的使用技巧、問題和實用軟件。 只要你一直關注和呆在群里,每天必須有收獲
對應本平台的討論和答疑QQ群:大數據和人工智能躺過的坑(總群)(161156071)







打開百度App,掃碼,精彩文章每天更新!歡迎關注我的百家號: 九月哥快訊


