網路傳輸--TCP
阿新 • • 發佈:2018-11-19
TCP網路程式設計
一、TCP簡介TCP
1.TCP的簡介
TCP通訊需要通過建立連結, 資料傳送, 終止連結3個步驟
2.TCP的特點
面向連線: 建立間接, 通訊, 關閉連線. 這種連線方式是一對一的, 所以不支援廣播模式
可靠傳輸
應答機制: TCP傳送的報文段必須都得到接受方的應答,才可以進行下步傳輸
超時重傳: TCP傳送報文段的時候, 會啟動定時器, 在規定時間內沒有迴應, 就進行重發
錯誤校驗: 由傳送端計算,然後由接收端驗證, 在此過程中出現差誤的話, 則會直接丟棄這個包
流量控制和阻礙管理: 根據實際情況來進行調整發送速度
優缺點:
優點: 可靠, 穩定傳輸資料
缺點: 傳輸速度慢, 佔用系統資源高, 不可以進行廣播傳輸
TCP和UDP的區別:
TCP 面向連線; UDP 是不面向連線;
TCP 提供可靠的資料傳輸,也就是說,通過 TCP 連線傳送的資料,無差錯,不丟失,不重複,且按序到達; UDP 不保證可靠的資料傳輸,容易出現丟包情況;
TCP 需要連線傳輸速度慢,UDP 不需要連線傳輸速度快
TCP 不支援發廣播; UDP 支援發廣播
TCP 對系統資源要求較多,UDP 對系統資源要求較少。
TCP 適合傳送大量資料,UDP 適合傳送少量資料
TCP 有流量控制,UDP 沒有流量控制
二、TCP網路程式--客戶端
***注意***
在使用windows的網路除錯助手的過程中, 此軟體傳出的位元組是GBK的編碼, 但接收的過程中是Unicode的編碼形式
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import socket # 1.買個電話 -- 建立TCP套接字 引數是 地址協議版本 套接字型別 tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)客戶端# 2.撥號 建立和伺服器的連結 tcp_socket.connect(('192.168.33.68', 8080)) # 3.傳送資料 choose = input(">>>:") tcp_socket.send(choose.encode()) # 4.接受資料 阻塞等待資料 recv返回值一般情況下 就是對方傳送的資料; 如果對方斷開了連結 返回值就是 '' recv_data = tcp_socket.recv(1024) if not recv_data: print("對方斷開連結") else: print(recv_data.decode('gbk')) # 5.關閉套接字 tcp_socket.close()
三、TCP網路程式--服務端
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import socket # 1.總機 - 建立TCP套接字<伺服器套接字 監聽套接字> server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2.固定號碼 - 固定埠 server_socket.bind(('', 8888)) # 3.安裝客戶服務系統 - 主動 -> 被動監聽模式 server_socket.listen(128) while True: # 4.從等待服務區取出一個客戶端用以服務 轉接到分機 - 接受連結 # (<socket.socket 和客戶端關聯起來的套接字物件), raddr=('172.17.99.129', 53614)>, ('172.17.99.129', 53614)) client_socket, client_addr = server_socket.accept() print("接受來自%s的資料請求" % str(client_socket)) while True: # 5.使用分機進行深入的交流 - echo 回射伺服器 recv_data = client_socket.recv(1024) print("接受資料:%s" % recv_data.decode('gbk')) client_socket.send(recv_data.decode('gbk').encode()) if not recv_data: print("客戶端下線了") break # 6.分機掛機 client_socket.close() # 7.主機掛機 server_socket.close()服務端
四、TCP知識總結
1.TCP 伺服器一般情況下都需要繫結埠號,否則客戶端找不到這個伺服器
2.TCP 客戶端一般不繫結埠號,使用隨機生成的埠號即可
3.TCP 伺服器中通過 listen 可以將 socket 創建出來的主動套接字變為被動的,這是做 TCP 伺服器時必須要做的
4.當 TCP 客戶端和服務端建立好連線才可以收發資料,UDP 是不需要建立連線,直接就可以傳送資料
5.當一個 TCP 客戶端和服務端連線成功後,伺服器端會有1個新的套接字,這個套接字用來標記這個客戶端,單獨為這個客戶端服務
6.listen 後的套接字是被動套接字,用來接收新的客戶端的連結請求的,而accept返回的新套接字是標記這個新客戶端的
7.關閉 listen 後的套接字意味著被動套接字關閉了,會導致新的客戶端不能夠連結伺服器,但是之前已經連結成功的客戶端正常通訊。
8.關閉 accept 返回的套接字意味著這個客戶端已經服務完畢
9.當客戶端的套接字呼叫 close 後,伺服器端會 recv 解堵塞,並且返回的長度為0,因此伺服器可以通過返回資料的長度來區別客戶端是否已經下線
五、檔案下載案例
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import socket # 1.建立和伺服器的連線 # 1.1 使用者輸入IP地址和埠 IP = input("伺服器IP:") port = int(input("伺服器埠:")) # 1.2 建立TCP套接字 tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 1.3 連線伺服器 tcp_socket.connect((IP, port)) # 2.向伺服器傳送需要下載檔案的名稱 # 2.1 輸入檔名稱 file_name = input('需要下載的檔名稱:') # 2.2 傳送 tcp_socket.send(file_name.encode()) # 3.一遍接收檔案資料 一遍寫入資料 # 3.1 開啟檔案用於儲存 接收到的資料 file = open("下載" + file_name, "wb") while True: # 3.2 接收資料 寫入檔案 file_data = tcp_socket.recv(4096) # 3.3 如果資料是'' 傳輸完成 關閉檔案 套接字 否則繼續3.2的步驟 if not file_data: print("檔案下載完成") file.close() tcp_socket.close() break file.write(file_data)客戶端
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import socket def main(): # 1.建立和客戶端的連線 # 1.1 建立套接字 tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 1.2 繫結埠 tcp_socket.bind(('192.168.33.68', 8888)) # 1.3 監聽 tcp_socket.listen(128) # 1.4 接收連線 while True: clent_socket, clent_addr = tcp_socket.accept() print("接收來自%s的連線" % str(clent_socket)) # 2.接收客戶端傳送的檔名 # 2.1 使用和客戶關聯的套接字物件 接收資料 file_name = clent_socket.recv(4096) if not file_name: print("客戶端已經斷開連結") clent_socket.close() continue # 2.2 檔名稱解碼成 str new_file_name = file_name.decode() # 3.根據檔名稱 讀取檔案資料 傳送給客戶端 # 3.1 開啟檔案 with open(new_file_name, "rb") as file: # 3.2讀取整個檔案的資料 data = file.read() # 如果檔案大 可能導致程式出現風險 # 3.3 向客戶端傳送資料 clent_socket.send(data) tcp_socket.close() if __name__ == '__main__': main()服務端
六、3次握手和4次揮手
3次握手
標誌位:
SYN: 表示連線請求 ACK: 表示確認 FIN: 表示關閉連線 seq:表示報文序號 ack: 表示確認序號
第一次握手: Client將標誌位SYN置為1,隨機產生一個值seq=J,並將該資料包傳送給Server,Client進入SYN_SENT狀態,等待Server確認。 第二次握手: Server收到資料包後由標誌位SYN=1知道Client請求建立連線,Server將標誌位SYN和ACK都置為1,ack (number )=J+1,隨機產生一個值seq=K,
並將該資料包傳送給 Client以確認連線請求,Server進入SYN_RCVD狀態。 第三次握手: Client收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=K+1,並將該資料包傳送給Server,Server檢查ack是否為K+1,ACK是否為1,
如果正確則連線建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸資料了。
4次揮手
第一次揮手:Client傳送一個FIN,用來關閉Client到Server的資料傳送。 第二次揮手:Server收到FIN後,傳送一個ACK給Client,確認序號為收到序號+1。 第三次揮手:Server傳送一個FIN,用來關閉Server到Client的資料傳送。 第四次揮手:Client收到FIN後,接著傳送一個ACK給Server,確認序號為收到序號+1。