1. 程式人生 > >python中的socket、TCP/IP協議、UDP協議

python中的socket、TCP/IP協議、UDP協議

本文的大概內容從一片技術文章中而來,再加上自己的想法。

socket通常也稱作"套接字",用於描述IP地址和埠,是一個通訊鏈的控制代碼,應用程式通常通過"套接字"向網路發出請求或者應答網路請求。

socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆檔案”,對於檔案用【開啟】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket是一種特殊的檔案,一些socket函式就是對其進行的操作(開啟、讀/寫IO、關閉)。

socket和file的區別:

檔案是都是在同一臺計算機上,兩個程序之間傳輸資料。

socket可以實現在不同的計算機之間傳輸資料,也就是網路傳輸資料。比如說qq、開啟一個網頁,這些都是socket來實現通訊的。

那網路通訊呢又要說到tcp/ip協議和udp協議,socket裡面已經封裝好了upd和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傳資料,所以是2次關閉,各自關閉各自的通道。這兩條路呢,就有個次叫全雙工,就是兩邊都可以互相傳送資料,如果只有一端可以傳送資料,那就叫單工。

下面這個圖,就可以看到建立連線的過程和傳輸資料的過程,以及斷開的過程。

而udp協議就比較簡單了,沒有那麼複雜的斷開和連線,不需要3次握手,不需要確定客戶端、服務端是否能收到,tcp/ip是必須建立好連線之後,才能發資料,而udp是無連線的,知道ip和埠號直接就是發,它比tcp/ip快,但是不安全。

upd就像寫信一樣,有可能在路上就沒有了,對方沒有收到。而tcp/ip就像打電話一樣,必須得接通才能說話。

下面是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端再返回資料,做一個簡單的聊天的小程式,結果如下

下面是tcp/ip協議的程式碼,server端程式碼:

import socket
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))  #連線服務端
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()