1. 程式人生 > >Python網路程式設計之socket模組基礎例項!

Python網路程式設計之socket模組基礎例項!

Python網路程式設計之socket模組基礎例項!

 

TCP是流協議,而UDP是資料報協議。換句話說,TCP在客戶機和伺服器之間建立持續的開放連線,在該連線的生命期內,位元組可以通過該連線寫出(並且保證順序正確)。然而,通過 TCP 寫出的位元組沒有內建的結構,所以需要高層協議在被傳輸的位元組流內部分隔資料記錄和欄位。

UDP是資料報協議,不需要在客戶機和伺服器之間建立連線,它只是在地址之間傳輸報文。UDP的一個很好特性在於它的包是自分隔的(self-delimiting),也就是一個數據報都準確地指出它的開始和結束位置。然而,UDP的一個可能的缺點在於,它不保證包將會按順序到達,甚至根本就不保證。不過,UDP有一個很大的有點就是效率高。

TCP與UDP直接的對比就好似手機通訊和郵寄信件通訊。TCP猶如手機通訊機制,當呼叫者通過手機撥打接受者手機,只有接受者按下接聽鍵,兩方才算建立起了連線,且只要沒人結束通話,連線就一直是存活的,也只有在連線存活情況下兩者才能通話。UDP就如有郵局系統,只有有人來寄信,郵局就回幫他寄,但是不會去管收件人是否存在、也不管收件人什麼時候能收到信,如果寄件人陸續寄出多封信,收件人收的的信先後順序是混亂的,如果有的信沒有送達,那這封信就此消失在歷史的塵埃中。

Python網路程式設計之socket模組基礎例項!

 

網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。每一個socket都用一個半相關描述{協議、本地地址、本地埠}來表示;一個完整的套接字則用一個相關描述{協議、本地地址、本地埠、遠端地址、遠端埠}來表示。socket也有一個類似於開啟檔案的函式呼叫,該函式返回一個整型的socket描述符,隨後的連線建立、資料傳輸等操作都是通過socket描述符來實現的。以TCP協議中socket建立連線的“三次握手”為例,其過程如下:

Python網路程式設計之socket模組基礎例項!

 

如圖所示,當客戶端通過socket呼叫connect時,觸發了連線請求,向伺服器傳送了SYN J包,這時connect進入阻塞狀態;伺服器監聽到連線請求,即收到SYN J包,呼叫socket的accept函式接收請求向客戶端傳送SYN K ,ACK J+1,這時accept進入阻塞狀態;客戶端收到伺服器的SYN K ,ACK J+1之後,這時connect返回,並對SYN K進行確認;伺服器收到ACK K+1時,accept返回,至此三次握手完畢,連線建立。

兩個socket通過“網路”互動資料進行資料互動時只負責兩件事:建立連線,傳遞資料;同時socket在收發資料時遵循的原則:有發就有收,收發必相等!

4、socket基本使用

1)socket函式

功能:使用給定的地址族、套接字型別、協議編號(預設為0)來建立套接字。

格式:socket.socket([family[, type[, proto]]])

引數:

family : AF_INET (預設ipv4),AF_INET6(ipv6) , AF_UNIX(Unix系統程序間通訊).

type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) .

protocol : 一般為0或者預設

備註:如果socket建立失敗會丟擲一個socket.error異常

2)伺服器端函式

a)bind函式

格式:s.bind(address)

功能:將地址address繫結到套接字, 地址以元組(host,port)的形式表示。

引數:

address為元組(host,port)

host: ip地址, 為一個字串

post: 自定義主機號, 為整型

b)listen函式

格式:s.listen(backlog)

功能:使伺服器的這個埠和IP處於監聽狀態,等待網路中某一客戶機的連線請求。如果客戶端有連線請求,埠就會接受這個連線。

引數:backlog : 作業系統可以掛起的最大連線數量。該值至少為1,大部分應用程式設為5就可以了

c)accept函式

格式:s.accept()

功能:接受遠端計算機的連線請求,建立起與客戶機之間的通訊連線。伺服器處於監聽狀態時,如果某時刻獲得客戶機的連線請求,此時並不是立即處理這個請求,而是將這個請求放在等待佇列中,當系統空閒時再處理客戶機的連線請求。

返回值:返回一個數組(conn,address),其中conn是新的套接字物件,可以用來接收和傳送資料。address是連線客戶端的地址

3)客戶端函式

a)connect函式

格式:s.connect(address)

功能:用來請求連線遠端伺服器

引數:address為遠端伺服器地址, 格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤

b)connect_ex函式

格式:s.connect_ex(address)

備註:connect()函式的擴充套件版本,出錯時返回出錯碼,而不是丟擲異常

4)通用函式

a)recv函式

格式:s.recv(bufsize[,flag])

功能:接收遠端主機傳來的資料

引數:

bufsize : 指定要接收的資料大小

flag : 提供有關訊息的其他資訊,通常可以忽略

返回值:返回值為資料以字串形式</code>

b)send函式

格式:s.send(string[,flag])

功能:傳送資料給指定的遠端主機

引數:

string : 要傳送的字串資料

flag : 提供有關訊息的其他資訊,通常可以忽略

返回值:返回值是要傳送的位元組數量,該數量可能小於string的位元組大小。

c)sendall函式

格式:s.sendall(string[,flag])

功能:內部呼叫了send函式,完整發送TCP資料。將string中的資料傳送到連線的套接字,但在返回之前會嘗試傳送所有資料。

引數:同send函式

返回值 : 成功返回None,失敗則丟擲異常。

d)close函式

格式:s.close()

功能:關閉套接字

e)recvfrom函式

格式:s.recvfrom(bufsize[.flag])

功能:與recv()類似,區別是返回值不同

返回值:返回一個數組(data,address),其中data是包含接收資料的字串,address是傳送資料的套接字地址。

f)sendto函式

格式:s.sendto(string[,flag],address)

功能:將資料傳送到套接字

引數:

string : 要傳送的字串資料

flag : 提供有關訊息的其他資訊,通常可以忽略

address是形式為(ipaddr,port)的元組,指定遠端地址

返回值:返回值是要傳送的位元組數量

備註:該函式主要用於UDP協議。

g)settimeout函式

格式:s.settimeout(timeout)

功能:設定套接字操作的超時期

引數:timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛建立套接字時設定,因為它們可能用於連線的操作(如 client 連線最多等待5s )

h)getpeername函式

格式:s.getpeername()

功能:獲取連線套接字的遠端地址

返回值:返回值通常是元組(ipaddr,port)。

i)getsockname函式

格式:s.getsockname()

功能:獲取套接字自己的地址

返回值:通常是一個元組(ipaddr,port)

socket中常用的函式就上面這些了。先用上面這些函式嘗試TCP協議下的socket通訊。

伺服器端程式碼如下:

import socket

sk = socket.socket()

sk.bind(('127.0.0.1' ,8088))

sk.listen(5)

print('正在等待Linux公社客戶端連線……')

conn , addr = sk.accept()

print('Linux公社客戶端已連線到伺服器……')

mes_from_client = conn.recv(1024).decode('utf-8')

print(mes_from_client)

mes_to_server = '你好,Linux公社www.linuxidc.com客戶端,已收到您的資訊!'.encode('utf-8')#傳送的資料必須是byte型別

conn.send(mes_to_server)

conn.close()

sk.close()

客戶端程式碼:

import socket

sk = socket.socket()

sk.connect(('127.0.0.1',8088))

mes_to_server = '你好,Linux公社www.linuxidc.com伺服器!'.encode('utf-8')#傳送的資料必須是byte型別

sk.send(mes_to_server)

mes_from_server = sk.recv(1024).decode('utf-8')

print(mes_from_server)

sk.close()

注意:上述兩程式碼塊必須放在兩不同的檔案中,且必須先執行伺服器程式碼,然後在開啟客戶端。開啟伺服器後,首先輸出“正在等待客戶端連線……”,然後程序會阻塞在accept函式中,下面的程式碼不會被執行,知道有客戶端連線過來。開啟客戶端後,伺服器端會先收到客戶端發來的資訊,然後客戶端也會受到伺服器發來的資訊。

Python網路程式設計之socket模組基礎例項!

 

上面的例子中,伺服器和客戶端都是收發了一條資訊後socket關閉,如果要保持連線進行長時間通訊呢?那麼,我們可以把收發函式放入一個“while True”迴圈中:

伺服器端程式碼:

import socket

BUF_SIZE = 1024 #設定緩衝區大小

server_addr = ('127.0.0.1', 8089) #IP和埠構成表示地址

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #生成一個新的socket物件

server.bind(server_addr) #繫結地址

print("socket與地址繫結完成……")

server.listen(5) #監聽, 最大監聽數為5

print("socket監聽開始……")

client, client_addr = server.accept() #接收TCP連線, 並返回新的套接字和地址, 阻塞函式

print("報告:有客戶端請求連線,正在連線……")

print('客戶端地址為:{}'.format( client_addr))

while True :

mes_from_client = client.recv(BUF_SIZE) #從客戶端接收資料

mes = mes_from_client.decode('utf-8')

print('客戶端說:{}'.format(mes))

mes = input('回覆客戶端的資訊>')

mes_to_client = mes.encode('utf-8')

client.sendall(mes_to_client) #傳送資料到客戶端

server.close()

客戶端程式碼:

import socket

BUF_SIZE = 1024 #設定緩衝區的大小

server_addr = ('127.0.0.1', 8089) #IP和埠構成的伺服器地址

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #返回新的socket物件

client.connect(server_addr) #連線伺服器

while True:

mes = input("傳送給伺服器的資訊> ")

mes_to_server = mes.encode('utf-8')

client.sendall(mes_to_server) #傳送資料到伺服器

mes_from_server = client.recv(BUF_SIZE) #從伺服器端接收資料

mes = mes_from_server.decode('utf-8')

print(mes)

client.close()

Python網路程式設計之socket模組基礎例項!

 

Python網路程式設計之socket模組基礎例項!

 

執行上述程式碼後,客戶端和伺服器可以長時間維持通訊。不過,使用socket時一定要注意,有發才有收,收發必相等,否則,就回出現異常。如果需要求換其他客戶端與當前伺服器進行通訊,必須先斷開當前客戶端的連線。

再來嘗試UDP協議下socket通訊:

伺服器端程式碼:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1' , 8090))

print('等待客戶端發來訊息……')

msg , addr = sk.recvfrom(1024) # 此處會阻塞

print(msg.decode('utf-8'))

mes_to_server = '你好,Linux公社www.linuxidc.com客戶端,已收到你的資訊!'.encode('utf-8')#傳送的資料必須是byte型別

sk.sendto(mes_to_server,addr)

sk.close()

客戶端程式碼:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ('127.0.0.1' , 8090)

mes_to_server = '你好,Linux公社www.linuxidc.com伺服器!'.encode('utf-8')#傳送的資料必須是byte型別

sk.sendto(mes_to_server , ip_port)

ret , addr = sk.recvfrom(1024)

print(ret.decode('utf-8'))

sk.close()

Python網路程式設計之socket模組基礎例項!

 

如果需要不停收發訊息,程式碼更改如下:

伺服器端程式碼:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1' , 8090))

print('等待Linux公社linuxidc.com客戶端發來訊息……')

while True:

msg , addr = sk.recvfrom(1024) # 此處會阻塞

print('Linux公社收到{}發來的資訊,內容是:{}'.format(addr , msg.decode('utf-8')))

mes_to_server = input('>>>').encode('utf-8')#傳送的資料必須是byte型別

sk.sendto(mes_to_server,addr)

sk.close()

客戶端程式碼:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ('127.0.0.1' , 8090)

while True:

mes_to_server = input('>>>').encode('utf-8')#傳送的資料必須是byte型別

sk.sendto(mes_to_server , ip_port)

ret , addr = sk.recvfrom(1024)

print(ret.decode('utf-8'))

sk.close()

Python網路程式設計之socket模組基礎例項!

進群:960410445  即可獲取數十套PDF!

使用socket進行UDP協議下通訊時,可以多個客戶端與伺服器通訊,也就是說,上面客戶端程式碼你可以另開一個程序與伺服器通訊,且不需要關閉當前客戶端,這是TCP協議與UDP協議下socket通訊的一個不同之處。

5、總結

本篇初步總結了Python網路程式設計中socket模組的使用,事實上只是大致總結了基本函式的用法