前言
socket通常也稱作"套接字",用於描述IP地址和端口,是一個通信鏈的句柄,應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。【套接字,是一個對 TCP / IP協議進行封裝 的編程調用接口(API)】
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對於文件用【打開】【讀寫】【關閉】模式來操作。 socket就是該模式的一個實現,socket是一種特殊的文件,一些socket函數就是對其進行的操作(打開、讀/寫IO、關閉)。
socket和file的區別:
文件是都是在同一台計算機上,兩個進程之間傳輸數據。
那網絡通信呢又要說到tcp/ip協議和udp協議,socket里面已經封裝好了upd和tcp/ip協議,直接使用就可以了。
簡述tcp/ip協議:
簡單說下tcp/ip協議是干嘛的,網絡剛出來的時候,一片混亂,那要傳輸數據就得大家都遵守一個規則,大家都按照這個,然后就出現了tcp/ip協議。也許你聽過3次握手,4次斷開,說的就是tcp/ip連接的一個過程。加入a計算機要和b計算機通信,過程是這樣的:
a:在嗎,我可以連你嗎
b:在,你連吧
a:好的,我要給你發數據了
#這就是3次握手,這就建立好通道了,兩台計算機就可以進行通行了。
那么4次斷開是什么呢
a:我要和你斷開了
b:好的,你斷開吧
b:關閉通道
a:關閉通道
為什么關閉2次呢,因為兩端要互相傳數據,挖了兩條路,一條路用來b給a傳數據,另外一條是a給b傳數據【TCP屬於全雙工通信】;所以是2次關閉,各自關閉各自的通道。這兩條路呢,就有個名字叫全雙工,就是兩邊都可以互相發送數據;如果只有一端可以發送數據,那就叫單工。
而udp協議就比較簡單了,沒有那么復雜的斷開和連接,不需要3次握手,不需要確定客戶端、服務端是否能收到,tcp/ip是必須建立好連接之后,才能發數據;而udp是無連接的,知道ip和端口號直接就是發,它比tcp/ip快,但是不安全。
upd就像寫信一樣,有可能在路上就沒有了,對方沒有收到。而tcp/ip就像打電話一樣,必須得接通才能說話。
python代碼實現Socket通信原理
UDP協議實現
下面是udp server端的代碼:
import socket ''' 使用UDP協議時,不需要建立連接,只需要知道對方的IP地址和端口號,就可以直接發數據包。但是,能不能到達就不知道了。 雖然用UDP傳輸數據不可靠,但它的優點是和TCP比,速度快,對於不要求可靠到達的數據,就可以使用UDP協議。 我們來看看如何通過UDP協議傳輸數據。和TCP類似,使用UDP的通信雙方也分為客戶端和服務器。服務器首先需要綁定端口 綁定端口和TCP一樣,但是不需要調用listen()方法,而是直接接收來自任何客戶端的數據 ''' # ipv4 SOCK_DGRAM指定了這個Socket的類型是UDP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 綁定 客戶端口和地址: s.bind(('127.0.0.1', 9999))#綁定9999端口號 print('開始聊天了') while True: # 接收數據 自動阻塞 等待客戶端請求: data, addr = s.recvfrom(1024) #接收客戶端發過來的數據和ip地址 data = data.decode() print('客戶端的ip信息',addr) print('發過來的數據 %s'%data) msg = input('你的回復:') #這個是咱們返回的數據 s.sendto(msg.encode(), addr)#把數據發送給客戶端 # recvfrom()方法返回數據和客戶端的地址與端口,這樣,服務器收到數據后,直接調用sendto()就可以把數據用UDP發給客戶端。
下面是client端的代碼:
import socket ''' 客戶端使用UDP時,首先仍然創建基於UDP的Socket,然后不需要連接,直接通過sendto()給服務器發數據: ''' s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) data = input('data:') s.sendto(data.encode(), ('127.0.0.1', 9999))# # 發送數據: recv = s.recv(1024) #返回的數據 print(recv.decode()) # 接收數據: s.close()
先運行server端的代碼再運行client的向server端發送數據,server端再返回數據,以上就是通過UDP協議原理做了一個簡單的聊天的小程序。
TCP協議實現
下面是tcp/ip協議的代碼,server端代碼:
import socket # SOCK_STREAM指定了這個socket的類型是TCP sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #建立一個tcp/ip scoket sock.bind(('127.0.0.1',9999)) #綁定端口號 sock.listen(128)#監聽,同時能連多少個客戶端 while True: print('開始等待下一個客戶端過來。。。') client,addr = sock.accept() #接收到客戶端的socket,和地址 print('接收到 client數據',addr) while True: # data = client.recv(1024)#獲取到客戶端的數據 data = data.decode() if not data or data=='bye': #如果沒有發送過來數據就代表客戶端close了,或者發過來bye代表連接要斷開 print('服務結束',addr) client.close()#斷開連接,為下一個服務 break else:#如果他還在發送的話 print('發過來的', data) msg = input('回復:') client.send(msg.encode()) # 數據 sock.close()
下面是客戶端連接服務端的代碼:
import socket s = socket.socket() s.connect(('127.0.0.1',9999)) #連接服務端【由於基於TCP協議的socket,所以需要連接服務器】 while True:# data = input('data:') s.send(data.encode())#發送數據 recv = s.recv(1024).decode() print(recv) if data=='close': break s.close()
大家可能會想,學這個有啥用呢,其實這些web框架底層就是這么實現的,比如說django、flask這些,會了socket,我們也可以自己開發一個web框架。當然現在只能一次給一個客戶端服務,用了多線程或者多進程就可以為多個客戶端來服務了。
多個客戶端連接服務端實現
下面用多線程,每次有客戶端連過來就啟動一個線程來服務,這樣就可以為多個客戶端服務了,用threading模塊啟動一個線程,來一個請求就啟動一個線程為他服務,代碼如下:
import socket,threading class SocketServer: def __init__(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立一個tcp/ip scoket sock.bind(('127.0.0.1', 9999)) # 綁定端口號 sock.listen(128) # 監聽 self.sock = sock def start_server(self): while True: print('開始等待個客戶端過來') client,addr = self.sock.accept() print('客戶【%s】過來了',addr) t = threading.Thread(target=self.client_recv,args=(client,addr)) t.start() def client_recv(self,client,addr): while True: data = client.recv(1024) # 獲取到客戶端的數據 data = data.decode() if not data or data == 'bye': # 如果沒有發送過來數據就代表客戶端close了,或者發過來bye代表連接要斷開 print('服務結束', addr) client.close() # 斷開連接,為下一個服務 break else: # 如果他還在發送的話 print('發過來的', data) msg = '統一回復,人不在' client.send(msg.encode()) # 數據 if __name__ == '__main__': t = SocketServer() t.start_server()