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網絡編程(二)局域網內和局域網與廣域網的持續通信

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章