新聞中心
在如今的互聯(lián)網(wǎng)當中,Socket 協(xié)議是最重要的基礎之一。本文涵蓋了在 Python 中處理 Socket 編程的所有領域。

為什么使用 Sockets
Sockets 是組成當今網(wǎng)絡的各種通信協(xié)議,這些協(xié)議使得在兩個不同的程序或設備之間傳輸信息成為可能。例如,當我們打開瀏覽器時,我們作為客戶機就會創(chuàng)建與服務器的連接以傳輸信息。
在深入研究這個通信原理之前,讓我們先弄清楚 Sockets 到底是什么。
什么是 Sockets
一般來說,Socket 是為發(fā)送和接收數(shù)據(jù)而構建的內部應用協(xié)議。單個網(wǎng)絡將有兩個 Sockets,每個 Sockets 用于通信設備或程序,這些 Sockets 是IP地址和端口的組合。根據(jù)使用的端口號,單個設備可以有“n”個 Sockets,不同的端口可用于不同類型的協(xié)議。
下圖展示了一些常見端口號和相關協(xié)議的信息:
|
協(xié)議 |
端口號 |
Python 庫 |
應用 |
|
HTTP |
80 |
httplib,urllib,requests |
網(wǎng)頁,網(wǎng)站 |
|
FTP |
20 |
ftplib |
文件傳輸 |
|
NNTP |
119 |
nttplib |
新聞傳輸 |
|
SMTP |
25 |
smtplib |
發(fā)送郵件 |
|
Telnet |
23 |
telnetlib |
命令行 |
|
POP3 |
110 |
poplib |
接收郵件 |
|
Gopher |
70 |
gopherlib |
文檔傳輸 |
現(xiàn)在我們已經(jīng)了解了 Sockets 的概念,現(xiàn)在讓我們來看看 Python 的 Socket 模塊
如何在 Python 中實現(xiàn) Socket 編程
要在 Python 中實現(xiàn) Socket 編程,需要導入 socket 模塊。
該模塊的一些重要方法如下:
|
方法 |
描述 |
|
socket.socket() |
用于創(chuàng)建 socket(服務器端和客戶端都需要創(chuàng)建) |
|
socket.accept() |
用于接受連接。它返回一對值(conn,address),其中conn是用于發(fā)送或接收數(shù)據(jù)的新 socket 對象,address是連接另一端的 socket 地址 |
|
socket.bind() |
用于綁定到指定為參數(shù)的地址 |
|
socket.close() |
用于關閉 socket |
|
socket.connect() |
用于連接到指定為參數(shù)的遠程地址 |
|
socket.listen() |
使服務器能夠接受連接 |
現(xiàn)在我們已經(jīng)了解了 socket 模塊的重要性,接下來讓我們看看如何在 Python 中建服務器和客戶機。
什么是服務器
服務器或者是一個程序、一臺計算機,或者是一臺專門用于管理網(wǎng)絡資源的設備。服務器可以位于同一設備或計算機上,也可以本地連接到其他設備和計算機,甚至可以遠程連接。有各種類型的服務器,如數(shù)據(jù)庫服務器、網(wǎng)絡服務器、打印服務器等。
服務器通常使用socket.socket(),socket.bind(),socket.listen()等來建立連接并綁定到客戶端,現(xiàn)在讓我們編寫一個程序來創(chuàng)建服務器。
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(),1234))
#port number can be anything between 0-65535(we usually specify non-previleged ports which are > 1023)
s.listen(5)
while True:
clt,adr=s.accept()
print(f"Connection to {adr}established")
#f string is literal string prefixed with f which
#contains python expressions inside braces
clt.send(bytes("Socket Programming in Python","utf-8 ")) #to send info to clientsocket
創(chuàng)建 socket 的第一個必要條件是導入相關模塊。之后是使用socket.socket()方法創(chuàng)建服務器端 socket。
AF_INET 是指來自 Internet 的地址,它需要一對(主機、端口),其中主機可以是某個特定網(wǎng)站的 URL 或其地址,端口號為整數(shù)。SOCK_STREAM 用于創(chuàng)建 TCP 協(xié)議。
bind()?方法接受兩個參數(shù)作為元組(主機、端口)。這里需要注意的是最好使用4位的端口號,因為較低的端口號通常被占用或者是系統(tǒng)預留的。listen()方法允許服務器接受連接,5是同時接受的多個連接的隊列。此處可以指定的最小值為0,如果未指定參數(shù),則采用默認的合適參數(shù)。
while?循環(huán)允許永遠接受連接,clt和adr?是客戶端對象和地址,print?語句只是打印出客戶端 socket 的地址和端口號,最后,clt.send用于以字節(jié)為單位發(fā)送數(shù)據(jù)。
現(xiàn)在我們的服務器已經(jīng)設置好了,讓我們繼續(xù)向客戶機前進。
什么是客戶端
客戶端是從服務器接收信息或服務的計算機或軟件。在客戶端-服務器模型中,客戶端從服務器請求服務。最好的例子是 Google Chrome、Firefox 等 Web 瀏覽器,這些 Web 瀏覽器根據(jù)用戶的指示請求 Web 服務器提供所需的網(wǎng)頁和服務。其他示例包括在線游戲、在線聊天等。
現(xiàn)在,讓我們看看如何用 Python 編程語言編寫客戶端程序:
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 2346))
msg=s.recv(1024)
print(msg.decode("utf-8"))
首先依然是導入 socket 模塊,然后像創(chuàng)建服務器時那樣創(chuàng)建套接字。接著要在客戶端服務器之間創(chuàng)建連接,需要通過指定(host,port)使用 connect()方法。
注意:當客戶端和服務器位于同一臺計算機上時,需要使用gethostname。(LAN–localip/WAN–publicip)
在這里,客戶端希望從服務器接收一些信息,為此,我們需要使用recv()?方法,信息存儲在另一個變量msg中。需要注意的是正在傳遞的信息將以字節(jié)為單位,在上述程序的客戶端中,一次傳輸最多可以接收1024字節(jié)(緩沖區(qū)大?。8鶕?jù)傳輸?shù)男畔⒘?,可以將其指定為任意?shù)量。
最后,再解碼并打印正在傳輸?shù)南ⅰ?/p>
現(xiàn)在我們已經(jīng)了解了如何創(chuàng)建客戶端-服務器程序,接下來讓我們看看它們需要如何執(zhí)行。
客戶端服務器交互
要執(zhí)行這些程序,需要打開命令程序,進入創(chuàng)建客戶端和服務器程序的文件夾,然后鍵入:
py server.py #這里,server.py 是服務器的文件名
不出意外服務器開始運行
要執(zhí)行客戶端,需要打開另一個cmd窗口,然后鍵入:
py client.py
下面讓我們將緩沖區(qū)大小減少到7,來看看相同的程序會怎么樣
如圖所示,傳輸7個字節(jié)后,連接終止。
其實這是一個問題,因為我們尚未收到完整的信息,但是連接卻提前關閉了,下面讓我們來解決這個問題。
多重通信
為了在客戶端收到完整信息之前繼續(xù)連接,我們可以使用while循環(huán)
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 2346))
while True:
msg=s.recv(7)
print(msg.decode("utf-8"))
如此修改之后,每次傳輸將以7個字節(jié)接收完整消息。
但這又引來了另一個問題,連接永遠不會終止,你永遠不知道什么時候會終止。此外,如果我們實際上不知道客戶端將從服務器接收到的消息或信息有多大,該怎么辦。在這種情況下,我們需要繼續(xù)完善代碼
complete_info=''
while True:
msg = s.recv(7)
if len(msg)<=0:
break
complete_info += msg.decode("utf-8")
print(complete_info)
在服務器端,使用close()方法,如下所示:
clt.close()
輸出如下:
程序會檢查信息的大小,并將其打印到一次兩個字節(jié)的緩沖區(qū)中,然后在完成連接后關閉連接。
傳輸 Python 對象
目前為止我們僅僅掌握了傳遞字符串的方法,但是,Python 中的 Socket 編程也允許我們傳輸 Python 對象。這些對象可以是集合、元組、字典等。要實現(xiàn)這一點,需要用到 Python 的 pickle 模塊。
Python pickle模塊
當我們實際序列化或反序列化 Python 中的對象時,就會使用到 Python pickle 模塊。讓我們看一個小例子
import pickle
mylist=[1,2,'abc']
mymsg = pickle.dumps(mylist)
print(mymsg)
Output:
b’x80x03]qx00(Kx01Kx02Xx03x00x00x00abcqx01e.’
在上面的程序中,mylist?是使用pickle模塊的dumps()?函數(shù)序列化的。還要注意,輸出以b開頭,表示它已轉換為字節(jié)。在 socket 編程中,可以實現(xiàn)此模塊以在客戶端和服務器之間傳輸 python 對象。
如何使用 pickle 模塊傳輸 Python 對象
當我們將 pickle 與 socket 一起使用時,完全可以通過網(wǎng)絡傳輸任何內容。
先來看看服務端代碼
Server-Side:
import socket
import pickle
a=10
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 2133)) #binding tuple
s.listen(5)
while True:
clt , adr = s.accept()
print(f"Connection to {adr}established")
m={1:"Client", 2:"Server"}
mymsg = pickle.dumps(m) #the msg we want to print later
mymsg = {len(mymsg):{a}}"utf-8") + mymsg
clt.send(mymsg)
這里,m?是一個字典,它基本上是一個需要從服務器發(fā)送到客戶端的 Python 對象。這是通過首先使用dumps()序列化對象,然后將其轉換為字節(jié)來完成的。
現(xiàn)在,讓我們記下客戶端:
Client-Side:
import socket
import pickle
a=10
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 2133))
while True:
complete_info = b''
rec_msg = True
while True:
mymsg = s.recv(10)
if rec_msg:
print(f"The length of message = {mymsg[:a]}")
x = int (mymsg[:a ] )
rec_msg = False
complete_info += mymsg
if len(complete_info)-a == x:
print("Recieved the complete info")
print(complete_info[a:])
m = pickle.loads(complete_info[a:])
print(m)
rec_msg = True
complete_info = b''
print(complete_info)
第一個while循環(huán)將幫助我們跟蹤完整的消息(complete_info)以及正在使用緩沖區(qū)接收的消息(rec_msg)。
然后,在接收消息時,我們所做的就是打印每一位消息,并將其放在大小為10的緩沖區(qū)中接收。此大小可以是任何大小,具體取決于個人選擇。
然后如果收到的消息等于完整消息,我們只會將消息打印為收到的完整信息,然后使用loads()反序列化消息。
輸出如下:
? ?
名稱欄目:Python中的Socket編程,全掌握!
網(wǎng)站URL:http://www.5511xx.com/article/codcogi.html


咨詢
建站咨詢
