👏作者簡介:大家好,我是愛敲代碼的小王,CSDN博客博主,Python小白
📕系列專欄:python入門到實戰、Python爬蟲開發、Python辦公自動化、Python數據分析、Python前后端開發
📧如果文章知識點有錯誤的地方,請指正!和大家一起學習,一起進步👀
🔥如果感覺博主的文章還不錯的話,請👍三連支持👍一下博主哦
🍂博主正在努力完成2023計劃中:以夢為馬,揚帆起航,2023追夢人
🔥🔥🔥 python入門到實戰專欄:從入門到實戰?
🔥🔥🔥 Python爬蟲開發專欄:從入門到實戰
🔥🔥🔥?Python辦公自動化專欄:從入門到實戰
🔥🔥🔥 Python數據分析專欄:從入門到實戰
🔥🔥🔥 Python前后端開發專欄:從入門到實戰
目錄
TCP建立連接的三次握手
?TCP斷開連接的四次揮手
?數據包與處理流程
?數據包處理流程
?套接字編程實戰
?UDP編程介紹
?UDP編程的實現
持續通信
TCP編程介紹
?TCP編程的實現
TCP雙向持續通信
?結合多線程實現TCP雙向傳送(自由聊天)
TCP建立連接的三次握手
?TCP是面向連接的協議,也就是說,在收發數據前,必須和對方建立可靠的連接。 一個TCP連接必須要經過三次“對話”才能建立起來,其中的過程非常復雜, 只簡單的描述下這三次對話的簡單過 程:
1)主機A向主機B發出連接請求:“我想給你發數據,可以嗎?”,這是第一次對話;
2)主機B向主機A發送同意連接和要求同步 (同步就是兩臺主機一個在發送,一個在接收,協調工作)的數據包 :“可以,你什么時候 發?”,這是第二次對話;
3)主機A再發出一個數據包確認主機B的要求同步:“我現在就發, 你接著吧!”, 這是第三次握手。 三次“對話”的目的是使數據包的發送和接收同步, 經過三次“對話” 之后,主機A才向主機B正式發送數據。
?
?
?1、第一步,客戶端發送一個包含SYN即同步(Synchronize)標志的TCP報文,SYN同步報文會指明客戶端使用的端口以及TCP連接的初始序號。
2、第二步,服務器在收到客戶端的SYN報文后,將返回一個SYN+ACK的報文,表示客戶端的請求被接受,同時TCP序號被加一,ACK即確認(Acknowledgement)
3、第三步,客戶端也返回一個確認報文ACK給服務器端,同樣TCP序列號被加一,到此一個TCP連接完成。然后才開始通信的第二步:數據處理。
這就是所說的TCP的三次握手(Three-way Handshake)。
?為什么TCP協議有三次握手,而UDP協議沒有?
??? 因為三次握手的目的是在client端和server端建立可靠的連接。保證雙方發送的數據對方都能接受到,這也是TCP協議的被稱為可靠的數據傳輸協議的原因。而UDP就不一樣,UDP不提供可靠的傳輸模式,發送端并不需要得到接收端的狀態,因此 UDP協議就用不著使用三次握手。
?TCP斷開連接的四次揮手
?TCP建立連接要進行3次握手,而斷開連接要進行4次:
第一次: 當主機A完成數據傳輸后,將控制位FIN置1,提出停止TCP連接的請求 ;
第二次: 主機B收到FIN后對其作出響應,確認這一方向上的TCP連接將關閉,將ACK置1;
第三次: 由B 端再提出反方向的關閉請求,將FIN置1 ;
第四次: 主機A對主機B的請求進行確認,將ACK置1,雙方向的關閉結束.。
?由TCP的三次握手和四次斷開可以看出,TCP使用面向連接的通信方式, 大大提高了數據通信的可靠性,使發送數據端和接收端在數據正式傳輸前就有了交互, 為數據正式傳輸打下了可靠的基礎。
?數據包與處理流程
什么是數據包
通信傳輸中的數據單位,一般也稱“數據包”。在數據包中包括: 包、幀、數據包、段、消息。
網絡中傳輸的數據包由兩部分組成:一部分是協議所要用到的首部,另一部分是上一層傳過來的數據。首部的結構由協議的具體規范詳細定義。在數據包的首部,明確標明了協議應該如何讀取數 據。反過來說,看到首部,也就能夠了解該協議必要的信息以及所要處理的數據。包首部就像協議的臉。
?
?數據包處理流程
?套接字編程實戰
socket編程介紹
TCP協議和UDP協議是傳輸層的兩種協議。Socket是傳輸層供給應用層的編程接口,所以Socket編程就分為TCP編程和UDP編程兩類。
?Socket編程封裝了常見的TCP、UDP操作,可以實現非常方便的網絡編程。
?socket()函數介紹
在Python語言標準庫中,通過使用socket模塊提供的socket對象, 可以在計算機網絡中建立可以互相通信的服務器與客戶端。在服務器端需要建立一個socket對象,并等待客戶端的連接。客戶端使用socket對象與服務器端進行連接,一旦連接成功,客戶端和服務器端就可以進行通信了。
???上圖中,我們可以看出socket通訊中,發送和接收數據,都是通過操作系統控制網卡來進行。因此,我們在使用之后,必須關閉socket。
?在Python 中,通常用一個Socket表示“打開了一個網絡連接”,語法格式如下:
socket.socket([family[, type[, proto]]])
family : 套接字家族可以使 AF_UNIX 或者 AF_INET ;
AF 表示ADDRESS FAMILY 地址族
AF_INET(又稱 PF_INET)是 IPv4 網絡協議的套接字類型;而 AF_UNIX 則是 Unix 系統本地通信
?type : 套接字類型可以根據是面向連接的還是非連接分為 SOCK_STREAM 或 SOCK_DGRAM ; protocol : 一般不填,默認為0。
Socket主要分為面向連接的Socket和無連接的Socket。 無連接Socket的主要協議是用戶數據報協議,也就是常說的UDP, UDP Socket的名字是 SOCK_DGRAM 。創建套接字UDP/IP套接字,可以 調用 socket.socket() 。示例代碼如下: udpSocket=socket.socket (AF_INET,SOCK_DGRAM)
?socket對象的內置函數和屬性
在Python語言中socket對象中,提供如表所示的內置函數。
?
?UDP編程介紹
UDP協議時,不需要建立連接,只需要知道對方的IP地址和端口 號,就可以直接發數據包。但是,能不能到達就不知道了。雖然用 UDP傳輸數據不可靠,但它的優點是和TCP比,速度快,對于不要求可靠到達的數據,就可以使用UDP協議。
創建Socket時, SOCK_DGRAM 指定了這個Socket的類型是UDP。綁定端口和TCP一樣,但是不需要調用 listen() 方法,而是直接接收來自任何客戶端的數據。 recvfrom() 方法返回數據和客戶端的地址與端口,這樣,服務器收到數據后,直接調用 sendto() 就可以把數據用UDP發給客戶端。
?UDP編程的實現
【示例】UDP接收數據
#coding=utf-8
from socket import *
s = socket(AF_INET, SOCK_DGRAM) #創建套接字
#綁定接收信息端口
s.bind(('127.0.0.1', 8888)) #綁定一個端口,ip地址和端?號
print("等待接收數據!")
redata = s.recvfrom(1024) #1024表示本次接收的最?字節數
print(redata)
print(f'收到遠程信息:{redata[0].decode("gbk")}, from {redata[1]}')
s.close()
【示例】UDP發送數據
from socket import *
s = socket(AF_INET, SOCK_DGRAM) #創建套接字
addr = ('127.0.0.1', 8888) #準備接收方地址
data = input("請輸入:")
#發送數據時,python3需要將字符串轉成byte
s.sendto(data.encode('gbk'),addr) #默認的網絡助手使用的編碼是gbk
s.close()
持續通信
【示例】UDP接收數據
#coding=utf-8
from socket import *
s = socket(AF_INET,SOCK_DGRAM) #創建UDP類型的套接字
s.bind(("127.0.0.1",8888)) #綁定端口,ip可以不寫
print("等待接收數據!")
while True:recv_data = s.recvfrom(1024) #1024表示本次接收的最大字節數recv_content = recv_data[0].decode('gbk')print(f"收到遠程信息:{recv_content},from {recv_data[1]}")if recv_content == "88":print("結束聊天!")break
s.close()
【示例】UDP發送數據
#coding=utf-8
from socket import *
s = socket(AF_INET,SOCK_DGRAM) #創建UDP類型的套接字
addr = ("127.0.0.1",8888)
while True:data = input("請輸入:")s.sendto(data.encode("gbk"),addr)if data == "88":print("結束聊天!")break
s.close()
結合多線程實現UDP雙向自由通信
UDP 不同于 TCP,不存在請求連接和受理過程,因此在某種意義上無法明確區分服務器端和客戶端,只是因為其提供服務而稱為服務器端。
?如下服務端、客戶端代碼幾乎一模一樣,注意接收和發送端口對應,即可。
?【示例】UDP實現多線程服務端
#coding=utf-8
from socket import *
from threading import Thread
udp_socket=socket(AF_INET,SOCK_DGRAM)
#綁定接收信息端口
udp_socket.bind(('127.0.0.1',8989))
#不停接收
def recv_data():while True:redata = udp_socket.recvfrom(1024)print(f'收到信息: {redata[0].decode("gbk")}, from {redata[1]}')
#不停發送
def send_data():while True:data=input('輸入信息:')addr=('127.0.0.1',8080)udp_socket.sendto(data.encode('gbk'),addr)
if __name__=='__main__':# 創建兩個線程t1=Thread(target=send_data)t2=Thread(target=recv_data)t2.start()t1.start()t1.join()t2.join()
【示例】UDP實現多線程客戶端
#coding=utf-8
from socket import *
from threading import Thread
udp_socket=socket(AF_INET,SOCK_DGRAM)
#綁定接收信息端口
udp_socket.bind(('127.0.0.1',8080))
#不停接收
def recv_data():while True:redata = udp_socket.recvfrom(1024)print(f'收到信息: {redata[0].decode("gbk")}, from {redata[1]}')
#不停發送
def send_data():while True:data=input('輸入信息:')addr=('127.0.0.1',8989)udp_socket.sendto(data.encode('gbk'),addr)
if __name__=='__main__':# 創建兩個線程t1=Thread(target=send_data)t2=Thread(target=recv_data)t2.start()t1.start()t1.join()t2.join()
TCP編程介紹
面向連接的Socket使用的協議是TCP協議。TCP的Socket名稱是 SOCK_STREAM 。創建套接字TCP套接字,可以調用 socket.socket() 。示例代碼如下:
tcpSocket=socket.socket(AF_INET,SOCK_STREAM)
?TCP編程的實現
在Python語言中創建Socket服務端程序,需要使用socket模塊中的 socket類。創建Socket服務器程序的步驟如下:
(1) 創建Socket對象。
(2) 綁定端口號。
(3) 監聽端口號。
(4) 等待客戶端Socket的連接。
(5) 讀取客戶端發送過來的數據。
(6) 向客戶端發送數據。
(7) 關閉客戶端Socket連接。
(8) 關閉服務端Socket連接。
?【示例】TCP服務器端接收數據
#coding=utf-8
from socket import *
server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(("", 8899))
server_socket.listen(5)
client_socket, client_info =
server_socket.accept()
#clientSocket 表示這個新的客戶端
#clientInfo 表示這個新的客戶端的ip以及port
recv_data = client_socket.recv(1024)
print(f"收到信息:{recv_data.decode('gbk')},來自:{client_info}")
client_socket.close()
server_socket.close()
【示例】TCP客戶端發送數據到服務端
#coding=utf-8
from socket import *
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 8899))
#注意:
# 1. tcp客戶端已經鏈接好了服務器,所以在以后的數據發
送中,不需要填寫對方的ip和port----->打電話
# 2. udp在發送數據的時候,因為沒有之前的鏈接,所以需
要在每次的發送中,都要填寫接收方的ip和port----->寫信
client_socket.send("haha".encode("gbk"))
client_socket.close()
TCP雙向持續通信
【示例】TCP:雙向通信Socket之服務器端
#coding=utf-8
'''
雙向通信Socket之服務器端讀取客戶端發送的數據,將內容輸出到控制臺將控制臺輸入的信息發送給客戶器端
'''
#導入socket模塊
from socket import *
#創建Socket對象
tcp_server_socket=socket(AF_INET,SOCK_STREAM)
#綁定端口
tcp_server_socket.bind(('', 8888))
#監聽客戶端的連接
tcp_server_socket.listen()
print("服務端已經啟動,等待客戶端連接!")
#接收客戶端連接
tcp_client_socket,
host=tcp_server_socket.accept()
print("一個客戶端建立連接成功!")
while True:#讀取客戶端的消息re_data=tcp_client_socket.recv(1024).decode('gbk')#將消息輸出到控制臺print('客戶端說:',re_data)if re_data=='end':break#獲取控制臺信息msg=input('>')tcp_client_socket.send(msg.encode('gbk'))
tcp_client_socket.close()
tcp_server_socket.close()
【示例】TCP:雙向通信Socket之客戶端
#coding=utf-8
'''
雙向通信Socket之客戶端將控制臺輸入的信息發送給服務器端讀取服務器端的數據,將內容輸出到控制臺
'''
#導入socket模塊
from socket import *
#創建客戶端Socket對象
tcp_client_socket=socket(AF_INET,SOCK_STREAM)
#連接服務器端
tcp_client_socket.connect(('127.0.0.1',8888))
while True:msg=input('>')#向服務器發送數據tcp_client_socket.send(msg.encode('gbk'))if msg=='end':break#接收服務器端數據re_data=tcp_client_socket.recv(1024)print('服務器端說:',re_data.decode('gbk'))
tcp_client_socket.close()
首先運行示例啟動服務器端程序,然后運行示例客戶端程序。執行結果如下圖所示:
?
?結合多線程實現TCP雙向傳送(自由聊天)
【示例】TCP服務端結合多線程實現自由收發信息
#coding=utf-8
'''
雙向通信Socket之服務器端讀取客戶端發送的數據,將內容輸出到控制臺將控制臺輸入的信息發送給客戶器端
'''
#導入socket模塊
from socket import *
from threading import Thread
def recv_data():while True:# 讀取客戶端的消息re_data =
tcp_client_socket.recv(1024).decode('gbk')# 將消息輸出到控制臺print(f'客戶端說:{re_data}')if re_data == 'end':break
def send_data():while True:# 獲取控制臺信息msg = input('>')tcp_client_socket.send(msg.encode('gbk'))
if __name__ == '__main__':# 創建Socket對象tcp_server_socket = socket(AF_INET,SOCK_STREAM)# 綁定端口tcp_server_socket.bind(('', 8888))# 監聽客戶端的連接tcp_server_socket.listen()print("服務端已經啟動,等待客戶端連接!")# 接收客戶端連接tcp_client_socket, host = tcp_server_socket.accept()print("一個客戶端建立連接成功!")t1 = Thread(target=recv_data)t2 = Thread(target=send_data)t1.start()t2.start()t1.join()t2.join()tcp_client_socket.close()tcp_server_socket.close()
【示例】TCP客戶端結合多線程實現自由收發信息
#coding=utf-8
'''
雙向通信Socket之客戶端將控制臺輸入的信息發送給服務器端讀取服務器端的數據,將內容輸出到控制臺
'''
#導入socket模塊
from socket import *
from threading import Thread
def recv_data():while True:# 接收服務器端數據re_data = tcp_client_socket.recv(1024)print(f'\n服務器端說: {re_data.decode("gbk")}')
def send_data():while True:msg = input('>')# 向服務器發送數據tcp_client_socket.send(msg.encode('gbk'))if msg == 'end':break
if __name__ == '__main__':# 創建客戶端Socket對象tcp_client_socket = socket(AF_INET,SOCK_STREAM)# 連接服務器端tcp_client_socket.connect(('127.0.0.1',8888))t1 = Thread(target=recv_data)t2 = Thread(target=send_data)t1.start()t2.start()t1.join()t2.join()tcp_client_socket.close()
關于http協議和服務器的授課順序說明
由于大家還沒有學習網頁內容,考慮到更好的和實戰結合。 所以,在講服務器編程時,詳細展開。
?