Python 網路通訊協議 tcp udp區別
網路通訊的整個流程
在這一節就給大家講解,有些同學對網路是既熟悉又陌生,熟悉是因為我們都知道,我們安裝一個路由器,拉一個網線,或者用無限路由器,連上網線或者連上wifi就能夠上網購物、看片片、吃雞了,但是這一系列的神操作到底是怎麼讓我們上網了呢?讓我們起底揭祕!由於網路的內容非常的多,本篇部落格主要是學socket網路程式設計,所以我把網路這方面的內容放到了我另外一篇部落格上,這個部落格很簡單,不是什麼深入研究類的部落格,沒有學過網路的或者說對網路不太熟悉的同學可以去看看,地址是網路通訊的整個流程,有網路基礎的同學,可以直接往下面學習,如果你自認上學時是個學渣,也可以過去大致溜一眼~~~將來你面向的是開發,所有網路這一塊對你來講就是大致知道就可以了,但是以後想在技術上有深造,那麼就需要你深入的研究一下網路了,內容非常多,學海無涯~~
別忘了埠+IP能夠確定一臺電腦上的某一個應用程式~~
那麼我們通過下面的程式碼簡單看一下socket到底是個什麼樣子,大概怎麼使用:下面的程式就是一個應用程式,和qq啊、微信啊是一樣的,都叫做應用程式。
import socket #建立一個socket物件 server = socket.socket() #相當於建立了一部電話 ip_port = ('192.168.111.1',8001) #建立一個電話卡 server.bind(ip_port) #插上電話卡 server.listen(5) #監聽著電話,我能監聽5個,接到一個電話之後,後面還能有四個人給我打電話,但是後面這四個人都要排隊等著,等著我第一個電話掛掉,再來第6個的時候,第六個人的手機會報錯View Codeprint('11111') #等著別人給我打電話,打來電話的時候,我就拿到了和對方的這個連線通道conn和對方的電話號碼addr conn,addr = server.accept() #阻塞住,一直等到有人連線我,連線之後得到一個元祖,裡面是連線通道conn和對方的地址(ip+埠) print('22222') print(conn) print('>>>>>>>>>',addr) while True: from_client_data = conn.recv(1024) #服務端必須通過兩者之間的連線通道來收訊息 from_client_data = from_client_data.decode('utf-8') print(from_client_data) if from_client_data == 'bye': break server_input = input('明威說>>>>:') conn.send(server_input.encode('utf-8')) if server_input == 'bye': break conn.close() #掛電話 server.close() #關手機 test_server.py
listen(3),這個3的意思是我連線著一個,後面還可以有三個排隊的,也就是支援4個人的服務,但是後面三個要排隊。
import socket #建立一個socket物件 server = socket.socket() #相當於建立了一部電話 ip_port = ('192.168.111.1',8001) #建立一個電話卡 server.bind(ip_port) #插上電話卡 server.listen(5) #監聽著電話,我能監聽5個,接到一個電話之後,後面還能有四個人給我打電話,但是後面這四個人都要排隊等著,等著我第一個電話掛掉,再來第6個的時候,第六個人的手機會報錯 print('11111') #等著別人給我打電話,打來電話的時候,我就拿到了和對方的這個連線通道conn和對方的電話號碼addr conn,addr = server.accept() #阻塞住,一直等到有人連線我,連線之後得到一個元祖,裡面是連線通道conn和對方的地址(ip+埠) print('22222') print(conn) print('>>>>>>>>>',addr) while True: from_client_data = conn.recv(1024) #服務端必須通過兩者之間的連線通道來收訊息 from_client_data = from_client_data.decode('utf-8') print(from_client_data) if from_client_data == 'bye': break server_input = input('明威說>>>>:') conn.send(server_input.encode('utf-8')) if server_input == 'bye': break conn.close() #掛電話 server.close() #關手機 test_server.pyView Code
注意:先執行server,然後再執行client,然後你會發現client這個檔案再輸出臺的地方讓你輸入內容,你輸入一個內容然後回車,你會發現server那邊的控制檯就輸出了以client傳送的內容
osi七層模型
網際網路的核心就是由一堆協議組成,協議就是標準,標準就是大家都認可的,所有人都按照這個來,這樣大家都能夠互相瞭解,互相深入了~~~比如全世界人通訊的標準是英語
五層通訊流程:
基於TCP和UDP兩個協議下socket的通訊流程
1.TCP和UDP對比
TCP(Transmission Control Protocol)可靠的、面向連線的協議(eg:打電話)、傳輸效率低全雙工通訊(傳送快取&接收快取)、面向位元組流。使用TCP的應用:Web瀏覽器;檔案傳輸程式。
UDP(User Datagram Protocol)不可靠的、無連線的服務,傳輸效率高(傳送前時延小),一對一、一對多、多對一、多對多、面向報文(資料包),盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視訊流;IP語音(VoIP)。
直接看圖對比其中差異
繼續往下看
TCP和UDP下socket差異對比圖:
上面的圖只是讓大家感受一下TCP和UDP協議下,socket工作流程的不同,兩者之間的差異是tcp需要連線,udp不需要,有些同學是不是有些迷糊,老師,這裡面的bind、listen啥的都是什麼東西啊,我感覺人生是迷茫的!calm down!下面我們就分開兩者,細細學習!
2.TCP協議下的socket
來吧!先上圖!
基於TCP的socket通訊流程圖片:
雖然上圖將通訊流程中的大致描述了一下socket各個方法的作用,但是還是要總結一下通訊流程(下面一段內容)
先從伺服器端說起。伺服器端先初始化Socket,然後與埠繫結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待客戶端連線。在這時如果有個客戶端初始化一個Socket,然後連線伺服器(connect),如果連線成功,這時客戶端與伺服器端的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料傳送給客戶端,客戶端讀取資料,最後關閉連線,一次互動結束
上程式碼感受一下,需要建立兩個檔案,檔名稱隨便起,為了方便看,我的兩個檔名稱為tcp_server.py(服務端)和tcp_client.py(客戶端),將下面的server端的程式碼拷貝到tcp_server.py檔案中,將下面client端的程式碼拷貝到tcp_client.py的檔案中,然後先執行tcp_server.py檔案中的程式碼,再執行tcp_client.py檔案中的程式碼,然後在pycharm下面的輸出視窗看一下效果。
server端程式碼示例(如果比喻成打電話)
import socket sk = socket.socket() sk.bind(('127.0.0.1',8898)) #把地址繫結到套接字 sk.listen() #監聽連結 conn,addr = sk.accept() #接受客戶端連結 ret = conn.recv(1024) #接收客戶端資訊 print(ret) #列印客戶端資訊 conn.send(b'hi') #向客戶端傳送資訊 conn.close() #關閉客戶端套接字 sk.close() #關閉伺服器套接字(可選) tcp_server.pyView Code
client端程式碼示例
import socket sk = socket.socket() # 建立客戶套接字 sk.connect(('127.0.0.1',8898)) # 嘗試連線伺服器 sk.send(b'hello!') ret = sk.recv(1024) # 對話(傳送/接收) print(ret) sk.close() # 關閉客戶套接字 tcp_client.pyView Code
在看UDP協議下的socket之前,我們還需要加一些內容來講:看程式碼
server端
import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() # sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) sk.bind(('127.0.0.1',8090)) sk.listen() conn,addr = sk.accept() #在這阻塞,等待客戶端過來連線 while True: ret = conn.recv(1024) #接收訊息 在這還是要阻塞,等待收訊息 ret = ret.decode('utf-8') #位元組型別轉換為字串中文 print(ret) if ret == 'bye': #如果接到的訊息為bye,退出 break msg = input('服務端>>') #服務端發訊息 conn.send(msg.encode('utf-8')) if msg == 'bye': break conn.close() sk.close() 只能與第一個客戶端通訊server端程式碼View Code
client端
import socket sk = socket.socket() sk.connect(('127.0.0.1',8090)) #連線服務端 while True: msg = input('客戶端>>>') #input阻塞,等待輸入內容 sk.send(msg.encode('utf-8')) if msg == 'bye': break ret = sk.recv(1024) ret = ret.decode('utf-8') print(ret) if ret == 'bye': break sk.close() 只能與第一個客戶端通訊client端程式碼View Code
你會發現,第一個連線的客戶端可以和服務端收發訊息,但是第二個連線的客戶端發訊息服務端是收不到的
原因解釋: tcp屬於長連線,長連線就是一直佔用著這個連結,這個連線的埠被佔用了,第二個客戶端過來連線的時候,他是可以連線的,但是處於一個佔線的狀態,就只能等著去跟服務端建立連線,除非一個客戶端斷開了(優雅的斷開可以,如果是強制斷開就會報錯,因為服務端的程式還在第一個迴圈裡面),然後就可以進行和服務端的通訊了。什麼是優雅的斷開呢?看程式碼。 server端程式碼:import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() # sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #允許地址重用,這個東西都說能解決問題,我非常不建議大家這麼做,容易出問題 sk.bind(('127.0.0.1',8090)) sk.listen() # 第二步演示,再加一層while迴圈 while True: #下面的程式碼全部縮排進去,也就是迴圈建立連線,但是不管怎麼聊,只能和一個聊,也就是另外一個優雅的斷了之後才能和另外一個聊 #它不能同時和好多人聊,還是長連線的原因,一直佔用著這個埠的連線,udp是可以的,然後我們學習udp conn,addr = sk.accept() #在這阻塞,等待客戶端過來連線 while True: ret = conn.recv(1024) #接收訊息 在這還是要阻塞,等待收訊息 ret = ret.decode('utf-8') #位元組型別轉換為字串中文 print(ret) if ret == 'bye': #如果接到的訊息為bye,退出 break msg = input('服務端>>') #服務端發訊息 conn.send(msg.encode('utf-8')) if msg == 'bye': break conn.close() 優雅的斷開一個client端之後另一個client端就可以通訊的程式碼View Code
client端程式碼
import socket sk = socket.socket() sk.connect(('127.0.0.1',8090)) #連線服務端 while True: msg = input('客戶端>>>') #input阻塞,等待輸入內容 sk.send(msg.encode('utf-8')) if msg == 'bye': break ret = sk.recv(1024) ret = ret.decode('utf-8') print(ret) if ret == 'bye': break # sk.close() client端程式碼View Code
3.UDP協議下的socket
老樣子!先上圖!
基於UDP的socket通訊流程:
總結一下UDP下的socket通訊流程
先從伺服器端說起。伺服器端先初始化Socket,然後與埠繫結(bind),recvform接收訊息,這個訊息有兩項,訊息內容和對方客戶端的地址,然後回覆訊息時也要帶著你收到的這個客戶端的地址,傳送回去,最後關閉連線,一次互動結束