Python之——網路程式設計筆記

b10l07發表於2018-08-10

1. 套接字

套接字是計算機網路資料結構,它體現了上節中所描述的“通訊端點”的概念。在任何
型別的通訊開始之前,網路應用程式必須建立套接字。可以將它們比作電話插孔,沒有它將
無法進行通訊。
有兩種型別的套接字:基於檔案的和麵向網路的。
UNIX 套接字是我們所講的套接字的第一個家族,並且擁有一個“家族名字”。(這塊不是本文重點,不細說)
而基於網路的套接字,有自己的家族名字 AF_INET。
Python 只支援 AF_UNIX、 AF_NETLINK、 AF_TIPC 和 AF_INET 家族。在網路程式設計中,將會用到 AF_INET。

  • 面向連線
    實現這種連線型別的主要協議是傳輸控制協議(更為人熟知的是它的縮寫 TCP)。為 了
    建立 TCP 套接字,必須使用 SOCK_STREAM 作為套接字型別。

  • 面向無連線
    實現這種連線型別的主要協議是使用者資料包協議(更為人熟知的是其縮寫 UDP)。為 了
    建立 UDP 套接字,必須使用 SOCK_DGRAM 作為套接字型別。

2. socket()模組函式

要建立套接字,必須使用 socket.socket()函式,它一般的語法如下。

socket(socket_family, socket_type, protocol=0)

為了建立 TCP/IP 套接字,可以用下面的方式呼叫 socket.socket()。

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

同樣,為了建立 UDP/IP 套接字,需要執行以下語句。

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

3. 建立TCP伺服器

虛擬碼如下:

# -*- coding: utf-8 -*
from socket import *

ss = socket() # 建立伺服器套接字
ss.bind() # 套接字與地址繫結
ss.listen() # 監聽連線
inf_loop: # 伺服器無限迴圈
    cs = ss.accept() # 接受客戶端連線
    comm_loop: # 通訊迴圈
        cs.recv()/cs.send() # 對話(接收/傳送)
    cs.close() # 關閉客戶端套接字
ss.close() # 關閉伺服器套接字#(可選)

在程式中,如果想要完成一個tcp伺服器的功能,需要的流程如下:

  1. socket建立一個套接字
  2. bind繫結ip和port
  3. listen使套接字變為可以被動連結
  4. accept等待客戶端的連結
  5. recv/send接收傳送資料

3.1 一個很簡單的tcp伺服器如下:

#coding=utf-8
from socket import *

# 建立socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)

# 繫結本地資訊
address = ('', 7787)
tcpSerSocket.bind(address)

# 使用socket建立的套接字預設的屬性是主動的,使用listen將其變為被動的,這樣就可以接收別人的連結了
tcpSerSocket.listen(5)

# 如果有新的客戶端來連結伺服器,那麼就產生一個新的套接字專門為這個客戶端伺服器
# newSocket用來為這個客戶端服務
# tcpSerSocket就可以省下來專門等待其他新客戶端的連結
newSocket, clientAddr = tcpSerSocket.accept()

# 接收對方傳送過來的資料,最大接收1024個位元組
recvData = newSocket.recv(1024)
print('接收到的資料為:',recvData)

# 傳送一些資料到客戶端
newSocket.send("thank you !".encode())

# 關閉為這個客戶端服務的套接字,只要關閉了,就意味著為不能再為這個客戶端服務了,如果還需要服務,只能再次重新連線
newSocket.close()

# 關閉監聽套接字,只要這個套接字關閉了,就意味著整個程式不能再接收任何新的客戶端的連線
tcpSerSocket.close()

tcp的客戶端要比伺服器端簡單很多,如果說伺服器端是需要自己買手機、查手機卡、設定鈴聲、等待別人打電話流程的話,那麼客戶端就只需要找一個電話亭,拿起電話撥打即可,流程要少很多。

3.2 一個很簡單的客戶端如下:

#coding=utf-8
from socket import *

# 建立socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)

# 連結伺服器
serAddr = ('10.88.2.192', 7787)
tcpClientSocket.connect(serAddr)

# 提示使用者輸入資料
sendData = input("請輸入要傳送的資料:")

tcpClientSocket.send(sendData.encode())

# 接收對方傳送過來的資料,最大接收1024個位元組
recvData = tcpClientSocket.recv(1024)
print('接收到的資料為:',recvData)

# 關閉套接字
tcpClientSocket.close()

注:以上服務端、客戶端的程式碼已經在python3下除錯通過,除錯的過程中,也碰到了一些問題,記錄如下:

  1. 出錯NameError: name 'raw_input' is not defined
    原因出在raw_input ,python3.0版本後用input替換了raw_input 。所以是自己看的教程太老,基於python2.x,而自己用的是3.x,所以使用input就好了。
  2. 除錯時出錯TypeError: a bytes-like object is required, not 'str'
    這個也是版本的問題,python3.5和Python2.7在套接字返回值解碼上有區別。
    參考文章:https://blog.csdn.net/yexiaohhjk/article/details/68066843
    (未完待續,待整理)

相關文章