1. 程式人生 > >Python Socket網路程式設計(一)初識Socket和Socket初步使用

Python Socket網路程式設計(一)初識Socket和Socket初步使用

目錄

前言

       本系列部落格是筆者學習Python Socket的過程筆記,目的在於記錄。其中的解釋都為自己的見解,僅供參考,如有錯誤,還望指出。本篇部落格是對Python Socket的初步瞭解和使用,大佛請移駕。

網路程式設計

  • 實質

       網路程式設計本質就是實現兩個裝置之間的資料交換(通訊),通常這個裝置指的是計算機,實際上任何能連線網路的硬體裝置都能實現通訊。也就是說我們的任何能連線網路的裝置都是可通訊的,比如我有一個 LED顯示屏,我現在需要伺服器控制這個 LED螢幕顯示一段文字,一些輪播圖片或者一個視訊,並且控制裝置什麼時間播放什麼內容,播放多長時間等命令。那麼LED和我的伺服器之間就需要通訊,這個時候就不是計算機與計算機之間通訊了。

       在網路程式設計中,發起連線的一方被稱作客戶端(Client),等待連線的一方被稱作伺服器(Server)。服務端一般都需要一直啟動,等待客戶端來連線。連線一旦建立,雙方就可以互相傳送資料了。

  • IP地址和埠

       網路通訊和生活中的打電話類似,我要給某個人打電話我就得知道他的電話號碼,在網路中也是如此,我要和伺服器建立通訊就需要知道伺服器的在網路中的位置。計算機在網路中的位置由IP地址(具體概念檢視IP百度百科

)來區分標識。建議再看看私有IP和共有IP的區別。

       一臺計算機上(裝置)可以執行多個程式(多個程序),為了區分這些程式就設計了埠(Port)的概念。裝置最多有216=65536個埠,一個埠可以對應一個唯一程式,無論是服務端還是客戶端,每個程式都對應一個或多個埠。其中0-1024之間的大多埠已經被作業系統佔用,我們的程式一般就使用之後的埠號(僅僅針對計算機裝置)。也就是說通過IP和埠號就可以實現兩個裝置的某個程式之間進行通訊了。

  • 資料傳輸協議

       對於建立了連線的兩個裝置以何種方式進行資料的傳輸呢,傳輸資料的方式無論是有線傳輸還是無線傳輸,一般就兩種傳輸協議方式:

       1. TCP(Transfer Control Protocol)

       傳輸控制協議方式,該傳輸方式是一種穩定可靠的傳送方式,類似於顯示中的打電話。只需要建立一次連線,就可以多次傳輸資料。就像電話只需要撥一次號,就可以實現一直通話一樣,如果你說的話不清楚,對方會要求你重複,保證傳輸的資料可靠。使用該種方式的優點是穩定可靠,缺點是建立連線和維持連線的代價高,傳輸速度不快。

       2. UDP(User Datagram Protocol)

       使用者資料報協議方式,該傳輸方式不建立穩定的連線,類似於發簡訊息。每次傳送資料都直接傳送。傳送多條簡訊,就需要多次輸入對方的號碼。該傳輸方式不可靠,資料有可能收不到,系統只保證盡力傳送。使用該種方式的優點是開銷小,傳輸速度快,缺點是資料有可能會丟失。

  • 協議

       協議(Protocol)在網路裡是一個重要的概念,平時所說的協議實際就是指網路協議,網路協議就是裝置實現通訊的規定,雙方必須遵守這個規定才能獲取到正確的通訊資訊。比如在建立連線的時候應該何種方式連線,怎麼互相判別,傳輸的資料格式(也就是要按照一定的格式來發送和接收資料)是什麼。只有遵守這個規定,裝置之間才能相互通訊交流。它的三要素是:語法、語義、時序。
       為了使資料在網路上從源到達目的,網路通訊的參與方必須遵循相同的規則,這套規則稱為協議(protocol),它最終體現為在網路上傳輸的資料包的格式。

Socket

  • 概念

       Socket即是套接字,Python將低級別的網路服務封裝成了一個模組,通過socket就可以將網路中的兩個裝置的某一程序(應用程式)以TCP或者UDP協議方式建立連線,在建立連線過後就可以實現裝置程序與裝置程序之間的通訊了。

  • 套接字

       我們匯入socket(使用import socket)模組,使用s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)來建立返回一個基於IPv4地址流式TCP傳輸協議的套接字s,我們需要建立套接字最主要考慮前兩個引數,第一個是IP地址簇,第二個是傳輸協議型別,下面列了常用的幾個可選值,想要了解全部請前往Python3 socket

  1. family:IP地址簇引數
可選值 描述
socket.AF_INET IPv4(預設)
socket.AF_INET6 IPv6
socket.AF_UNIX 用於Unix系統程序間通訊
socket.AF_…
  1. type:型別引數
可選值 描述
socket.SOCK_STREAM 流式socket,TCP(預設)
socket.SOCK_DGRAM 資料報式socket,UDP
socket.SOCK_…
  • socket物件方法
函式 型別 描述
s.bind() 服務端用 繫結地址和埠到套接字,引數address為元組形式的(host, port)
s.listen() 服務端用 開始監聽繫結的地址埠程式(程序),引數backlog指定在拒絕連線之前,可以掛起的最大連線數量。
s.accept() 服務端用 等待客戶端的連線(阻塞式)。
s.connect() 客戶端用 主動去連線伺服器,引數address為元組形式的(host, port),如果連接出錯,返回socket.error錯誤。
s.connect_ex() 客戶端用 主動去連線伺服器,引數address為元組形式的(host, port),出錯時返回出錯碼,而不是丟擲異常。
s.recv() 公共使用 接收資料,返回位元組型字串資料,引數bufsize指定接收資料的最大長度。
s.send() 公共使用 傳送資料,返回傳送成功的位元組數,引數data是要傳送的位元組型字串資料。
s.sendall() 公共使用 完整發送資料。內部迴圈呼叫send直至資料傳送完整,引數data是要傳送的位元組型字串資料。
s.recvfrom() 公共使用 接收資料,返回值是(data, address)。data是包含接收資料的字串,address是傳送資料的套接字地址。
s.sendto() 公共使用 傳送資料,將資料傳送到套接字,address形式為(host, port)的元組,指定遠端地址。返回值是傳送位元組數。
s.close() 公共使用 關閉套接字
s.getpeername() 公共使用 返回連線套接字的遠端地址。返回值通常是元組(host, port)。
s.getsockname() 公共使用 返回套接字自己的地址。通常是一個元組(host, port)。
s.setsockopt() 公共使用 設定給定套接字選項的值。
s.getsockopt() 公共使用 返回套接字選項的值。
s.settimeout() 公共使用 設定套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。
s.gettimeout() 公共使用 返回當前超時期的值,單位是秒,如果沒有設定超時期,則返回None。
s.fileno() 公共使用 接收資料,返回位元組型字串資料,引數bufsize指定接收資料的最大長度。
s.setblocking() 公共使用 flag為0非阻塞,否則為阻塞模式(預設)。非阻塞模式調recv()或send()沒有資料,將引起socket.error異常。
s.makefile() 公共使用 建立一個與該套接字相關連的檔案。

初步使用

  • 功能

       由於要實現的是兩個程序間的通訊(同一個裝置),那麼需要兩個程式,我們分別叫客戶端程式和服務端程式。
       在這個初級使用例項中,實現的是一臺計算機中兩個程序之間的通訊(一個程序為等待客戶端連線的服務端程式,一個程序為主動去連線服務端的客戶端程式)。也就是說客戶端和服務端程式都執行在本地的一臺計算機上,服務端建立socket,監聽本機IP的6688埠等待客戶端來連線,一旦有一個客戶端來連線了就列印連線資訊,然後等待連線的客戶端傳送資料,接收到客戶端傳來的資料後,就反饋給客戶端收到資訊了;客戶端程式先建立socket套接字,然後去連線本機的IP和6688埠程序,連線成功後等待控制檯輸入資料,接受到輸入的資料後傳送到服務端,然後結束連線。

  • 原始碼

       先新建一個server.py檔案,這個檔案裡寫服務端功能程式,如下:

import socket


# 建立一個socket套接字,該套接字還沒有建立連線
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 繫結監聽埠
server.bind(('localhost', 6688))
# 開始監聽,並設定最大連線數
server.listen(5)
# 獲取未建立連線的服務端的IP和埠資訊
print(server.getsockname())
# 下面註釋掉的是獲取未建立連線的服務端套接字的遠端IP和埠資訊,執行下面語句會報錯,原因就是現在還沒有遠端客戶端程式連線
# print(server.getpeername())

print(u'waiting for connect...')
# 等待連線,一旦有客戶端連線後,返回一個建立了連線後的套接字和連線的客戶端的IP和埠元組
connect, (host, port) = server.accept()
# 現在建立連線就可以獲取這兩個資訊了,注意server和connect套接字的區別,一個是未建立連線的套接字,一個是已經和客戶端建立了連線的套接字
peer_name = connect.getpeername()
sock_name = connect.getsockname()
print(u'the client %s:%s has connected.' % (host, port))
print('The peer name is %s and sock name is %s' % (peer_name, sock_name))

# 接受客戶端的資料
data = connect.recv(1024)
# 傳送資料給客戶端告訴他接收到了
connect.sendall(b'your words has received.')
print(b'the client say:' + data)

# 結束socket
server.close()

       然後建立一個client.py檔案,實現客戶端程式,內容如下:

import socket


# 建立一個socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 主動去連線本機IP和埠號為6688的程序,localhost等效於127.0.0.1,也就是去連線本機埠為6688的程序
client.connect(('localhost', 6688))

# 接受控制檯的輸入
data = input()
# 對資料進行編碼格式轉換,不然報錯
data = data.encode('utf-8')
# 傳送資料
client.sendall(data)
# 接收服務端的反饋資料
rec_data = client.recv(1024)
print(b'form server receive:' + rec_data)

client.close()
  • 執行結果

在這裡插入圖片描述

       其中左邊為服務端執行結果,右邊為客戶端執行結果,右邊的hello是手動輸入然後回車。

結語

       到此初步的使用已經差不多了,不過上述程式碼只能是一對一的通訊,一對多不是本篇部落格的內容,因為客戶端一般都不會只有一個,所以這個一對一不能滿足我們的常用需求,後面的部落格將會介紹多執行緒實現多客戶端連線伺服器,與伺服器通訊。在下一篇部落格會講解和實現區域網內兩臺裝置,廣域網之間,區域網與廣域網之間裝置的通訊。

下一篇:Python Socket網路程式設計(二)區域網內和區域網與廣域網的持續通訊