TCP(Transmission Control Protocol)傳輸控制協議
面向連接,可靠的基於字節流的傳輸通信協議,每次通信雙方都需要提前建立連接,因此需要三次握手(以下情景均以客戶端爲數據發出方,服務端爲數據接收方)
第一次【客戶端進入SYN_SEND狀態】
客戶端向服務端發送syn包,然後等待服務端回話。
李四:“喂,是張三嗎?”
第二次【服務端進入SYN_RECV狀態】
服務端收到syn包後,服務端向客戶端發送syn+ack包,然後等待客戶端回話。
張三:“我是張三,您是李四?”
第三次【雙方都進入ESTABLISHED狀態】
客戶端收到syn+ack包,確認服務端收到了syn包,然後將ack包發送給服務端進行確認。
李四:“我是李四,我有些事情要說,……”
第三次結束後,客戶端即開始向服務端傳輸數據,直到傳輸完畢後,進行四次揮手結束這個過程。
第一次【客戶端向服務端發起請求:我要掛斷連接了】
客戶端向服務端發送FIN=1的請求,要求斷開。
李四:“那就說到這裏,我忙去了”
第二次【服務端收到客戶端的請求,回覆:客戶端可以掛斷連接】
服務端向客戶端發送ACK=1進行確認。
張三:“去吧,這件事我知道了”
第三次【服務端首先斷開與客戶端的連接,回覆:服務端已經準備好】
服務端向客戶端發送FIN=1的請求,準備好斷開連接了。
張三:“拜拜”
第四次【客戶端收到服務端的請求,回覆:收到】
客戶端向服務端發送ACK=1的請求,然後斷開鏈接,服務端收到後也斷開了連接
李四:“拜拜”(掛斷)
TCP的Java示例
運行行時,先開啓服務端,再開啓客戶端。
可以發送被序列化的對象,常用Map集合進行封裝,或者用Json或Base64轉碼的字符串
服務端前兩步驟通常放在一個主線程的循環中,沒有請求時,程序停在accept()方法,一旦接收到請求,後續創建線程,在線程中完成第三步開始的步驟
@Test
public void serverTCP() {
//服務端
try {
//1、創建通信對象,指定端口
ServerSocket ss = new ServerSocket(8000);
//2、監聽客戶端請求
Socket s = ss.accept();//當前線程進入阻塞狀態,等待中
//3、開啓輸入流,接收消息
InputStream is = s.getInputStream();//發送方的信息都在s對象中
//4、讀取接收到的信息
ObjectInputStream ois = new ObjectInputStream(is);
List<String> list = (List<String>) ois.readObject();
for (String str : list) {
System.out.print(str);//輸出
}
//5、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
ois.close();
is.close();
s.close();
ss.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void clientTCP() {
//客戶端
try {
//1、創建通信對象,指定接收方的地址,IP和端口
Socket s = new Socket("127.0.0.1", 8000);
//2、開啓輸出流,發送請求
OutputStream os = s.getOutputStream();
//3、準備消息
List<String> list = new ArrayList<>();
list.add("TCP");
list.add("測試");
//4、發送消息
ObjectOutputStream oo = new ObjectOutputStream(os);
oo.writeObject(list);
oo.flush();//刷新緩存,信息開始發送
//5、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
oo.close();
os.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
UDP(User Datagram Protocol)用戶數據報協議
面向事務,適用於可靠性不高,延遲小的傳輸協議中,如常見的網絡直播,在線視頻,在線音頻,語音等,由於該協議是一種無連接的傳輸層協議,傳輸效率高,可靠性不好,網絡環境不是很好時容易出現中斷,如視頻暫停緩衝,語音模糊
發送方只關心誰來請求數據,然後將數據丟出去,至於數據是否完整無損的抵達接收方,不去判斷
至於發送方如何得到是誰需要請求數據,這些請求可以通過接收方的APP,通過TCP連接到服務端就可以很輕鬆的就可以得到了,然後發送方每隔一段時間更新發送的數據到接收方緩存,就可以完成在線直播了
相當於大學上課的情景,老師爲發送方,只管講課,醒着的同學聽到考試的重點,做了筆記,還有些睡着的同學什麼也沒聽到,老師當然不會走到那些學生面前叫醒然後重新強調一遍,老師會繼續講下去,一個又一個的知識點就講過去了
UDP的Java示例
運行時,先開啓接收方,再開啓發送方。
【無論服務端還是客戶端,都可以是接收方和發送方,比如開直播:Up主(發送方)與B站(接收方),B站(發送方)與粉絲(接收方)】
由於接收方不知道發送方的數據報的信息,接收方創建數據報時,應該大於發送方的數據容量,或者雙方約定好固定的容量。
@Test
public void sendUDP() {
//發送方
try {
//1、創建發送的信息
List<String> list = new ArrayList<>();
list.add("UDP");
list.add("測試");
//2、創建輸出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(list);
//3、信息存入流中
oos.flush();
byte[] b = baos.toByteArray();
if (b == null) return;//不能發送null
//4、創建數據報,數據,長度,接收方的目標地址,端口
DatagramPacket dp = new DatagramPacket(
// b.length=73
b, b.length, InetAddress.getByName("127.0.0.1"), 8000);
//5、創建通信對象
DatagramSocket ds = new DatagramSocket();
//6、發送數據
ds.send(dp);
//7、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
ds.close();
oos.close();
baos.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void receiveUDP() {
//接收方
try {
//1、創建通信對象,設置監聽端口
DatagramSocket ds = new DatagramSocket(8000);
//2、創建數據報
byte[] bytes = new byte[1024];
//最大和虛擬機配置有關,默認21_1917_2080,接收時,設置的長度不足會拋出EOF異常
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3、將接收到的數據寫入數據報
ds.receive(dp);
//4、創建流轉換爲對象
ByteArrayInputStream bais = new ByteArrayInputStream(dp.getData());
ObjectInputStream ois = new ObjectInputStream(bais);
//5、獲取數據
List<String> list = (List<String>) ois.readObject();
for (String str : list) {
System.out.print(str);//輸出
}
//6、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
ois.close();
bais.close();
ds.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}