Python網絡編程

簡介

使用套接字進行網絡編程。python

網絡編程

使用socket模塊的socket()函數,能夠建立套接字。react

socket模塊函數

要建立套接字,必須使用socket.socket()函數,語法以下:shell

socket(socket_family, socket_type, protocol=0)編程

其中,socket_familyAF_UNIXAF_INETsocket_typeSOCK_STREAMSOCK_DGRAMprotocol一般省略,默認爲0windows

導入模塊緩存

  • 建立TCP/IP套接字安全

    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)服務器

  • 建立UDP/IP套接字網絡

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

套接字對象方法

服務器套接字方法

名稱 描述
s.bind() 將地址(主機號、端口號對)綁定到套接字上
s.listen() 設置並啓動TCP監聽器
s.accept() 被動接受TCP客戶端鏈接,一直等待知道鏈接到達(阻塞)

客戶端套接字方法

名稱 描述
s.connect() 主動發起TCP服務器鏈接
s.connect_ex() connect()的擴展版本,此時會以錯誤碼的形式返回問題,而不是拋出一個異常

普通套接字方法

名稱 描述
s.recv() 接收TCP消息
s.recv_into() 接收TCP消息到指定的緩衝區
s.send() 發送TCP消息
s.sendall() 完整地發送TCP消息
s.recvfrom() 接受UDP消息
s.recvfrom_into() 接受UDP消息到指定的緩存區
s.sendto() 發送UDP消息
s.getpeername() 鏈接到套接字(TCP)的遠程地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回給定套接字選項的指
s.setsockopt() 設置給定套接字選項的值
s.shutdown() 關閉鏈接
s.close() 關閉套接字
s.detach() 在未關閉文件描述符的狀況下關閉套接字,返回文件描述符
s.ioctl() 控制套接字的模式(僅支持windows)

面向阻塞的套接字方法

名稱 描述
s.setblocking() 設置套接字的阻塞或非阻塞模式
s.settimeout() 設置阻塞套接字操做的超時時間
s.gettimeout() 獲取阻塞套接字操做的超時時間

面向文件的套接字方法

名稱 描述
s.fileno() 套接字的文件描述符
s.makefile() 建立與套接字關聯的文件對象

數據屬性

名稱 描述
s.family 套接字家族
s.type 套接字類型
s.proto 套接字協議

建立TCP服務器

TCP服務器的通常僞代碼。

ss = socket()                   # 建立服務器套接字
ss.bind()                       # 套接字與地址綁定
ss.listen()                     # 監聽鏈接
inf_loop:                       # 服務器無限循環
    cs = ss.accept()            # 接收客戶端鏈接
    comm_loop:                  # 通訊循環
        cs.recv() / cs.send()   # 對話(接受/發送)
    cs.close()                  # 關閉客戶端套接字
ss.close()                      # 關閉服務器套接字(可選)
  • 全部套接字都是經過使用socket.socket()函數來建立
  • 服務器須要佔用一個端口並等待客戶端的請求,因此它們必須綁定到一個本地地址
  • 由於TCP是一種面向鏈接的通訊系統,因此TCP服務器開始操做以前,必須安裝一些基礎設施
  • TCP服務器必須監聽(傳入) 的鏈接,一旦這個安裝過程完成後,服務器能夠開始它的無限循環
  • 調用accept()函數以後,就開啓了一個簡單的服務器,它會等待客戶端的鏈接,默認狀況下,accept()是阻塞的,這意味着執行將被暫停,直到一個鏈接到達
  • 一旦服務器接受一個鏈接,就會返回(經過accept())一個獨立的客戶端套接字,用來與即將到來的消息進行交換
  • 一旦建立了臨時套接字,通訊就能夠開始,經過使用這個新的套接字,客戶端與服務器就能夠開始參與發送和接受的對話中,直到鏈接終止
  • 當一方關閉鏈接或者向對方發送一個空字符串時,一般就會關閉鏈接
  • 在代碼中,一個客戶端鏈接關閉以後,服務器就會等待另外一個客戶端鏈接

下面是一個TCP服務器程序,它接受客戶端發送的數據字符串,並將其打上時間戳,並返回給客戶端。

# coding: utf-8

from socket import *
from time import ctime


HOST = ''       # HOST變量是空白,這是對bind()方法的標識,表示它能夠使用任何可用的地址
PORT = 21567    
BUFSIZ = 1024   # 緩衝區大小設置爲1KB
ADDR = (HOST, PORT) 

tcpSerSock = socket(AF_INET, SOCK_STREAM)   # 分配TCP服務器套接字
tcpSerSock.bind(ADDR)                       # 將套接字綁定到服務器地址
tcpSerSock.listen(5)    # 開啓TCP監聽器的調用,參數是在鏈接被轉接或拒絕以前,傳入鏈接請求的最大數

while True:
    print('waiting for connection...')
    tcpCliSock, addr = tcpSerSock.accept()  # 被動等待客戶端的鏈接
    print('...connected from:', addr)

    while True:
        data = tcpCliSock.recv(BUFSIZ)      # 等待客戶端發送的消息
        if not data:                        # 若是消息是空白的,意味着客戶端已經退出
            break                           # 跳出對話循環
        tcpCliSock.send(bytes('[%s] %s' % (ctime(), data), 'utf-8'))

    tcpCliSock.close()                      # 關閉當前客戶端鏈接

# tcpSerSock.close()

建立TCP客戶端

TCP客戶端的通常僞代碼。

cs = socket()               # 建立客戶端套接字
cs.connect()                # 嘗試鏈接服務器
comm_loop:                  # 通訊循環
    cs.send() / cs.recv()   # 對話(發送 / 接受)
cs.close()                  # 關閉客戶端套接字

Python代碼實現的TCP客戶端。

# coding: utf-8

from socket import *

HOST = '127.0.0.1'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)   # 分配TCP客戶端套接字
tcpCliSock.connect(ADDR)                    # 主動調用並鏈接到服務器

while True:
    data = input('> ')
    if not data:
        break
    tcpCliSock.send(bytes(data, 'utf-8'))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.decode('utf-8'))

tcpCliSock.close()

執行TCP服務器和客戶端

服務器端

bovenson@ThinkCentre:~/Git/notes/Python/Code/LearnPythonCode/network_programming$ python3 tsTserv.py 
waiting for connection...
...connected from: ('127.0.0.1', 35550)

客戶端

bovenson@ThinkCentre:~/Git/notes/Python/Code/LearnPythonCode/network_programming$ python3 tsTclnt.py 
> Hello
[Mon Jun  4 11:20:46 2018] b'Hello'
> World
[Mon Jun  4 11:20:48 2018] b'World'
>

建立UDP服務器

UDP服務器不須要TCP服務器那麼多的設置,由於它們不是面向鏈接的,除了等待傳入的鏈接以外,幾乎不須要作其餘工做。

ss = socket()                           # 建立服務器套接字
ss.bind()                               # 綁定服務器套接字
inf_loop():                             # 服務器無限循環
    cs = ss.recvfrom() / ss.sendto()    # 關閉(接受 / 發送)
ss.close()                              # 關閉服務器套接字

UDP服務器和TCP服務器之間的另外一個顯著差別是,覺得數據報套接字是無鏈接的,因此就沒有爲了成功通訊而使一個客戶端鏈接到一個獨立的套接字轉換操做。這些服務器僅僅接受消息並有可能回覆數據。

# coding: utf-8

from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR)

while True:
    print('waiting for message...')
    data, addr = udpSerSock.recvfrom(BUFSIZ)
    udpSerSock.sendto('[%s] %s' % (ctime(), data), addr)
    print('...received from and returned to:', addr)

# udpSerSock.close()

建立UDP客戶端

客戶端僞代碼以下。

ss = socket()                   # 建立客戶端套接字
comm_loop:                      # 通訊循環
    cs.sendto() / cs.recvfrom() # 對話(發送 / 接收)
cs.close()                      # 關閉客戶端套接字

Python實現的客戶端代碼。

# coding: utf-8

from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR)

while True:
    print('waiting for message...')
    data, addr = udpSerSock.recvfrom(BUFSIZ)
    udpSerSock.sendto(bytes('[%s] %s' % (ctime(), data), 'utf-8'), addr)
    print('...received from and returned to:', addr)

# udpSerSock.close()

執行UDP服務器和客戶端

客戶端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsUclnt.py
> Hello
b"[Mon Jun  4 12:42:07 2018] b'Hello'"
> World
b"[Mon Jun  4 12:42:08 2018] b'World'"
>

服務端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsUserv.py
waiting for message...
...received from and returned to: ('127.0.0.1', 59736)
waiting for message...
...received from and returned to: ('127.0.0.1', 59736)
waiting for message...

和TCP不一樣的是,UDP客戶端能夠先於UDP服務器運行。可是TCP服務器必須先於TCP客戶端運行。

socket模塊屬性

數據屬性

屬性名稱 描述
AF_UNIX,AF_INET,AF_INET6,AF_NETLINK,AF_TIPC Python中支持的套接字地址家族
SO_STREAM,SO_DGRAM 套接字類型(TCP=流,UDP=數據報)
has_ipv6 指示是否支持IPv6的布爾標記

異常

名稱 描述
error 套接字相關錯誤
herror 主機和地址相關錯誤
gaierror 地址相關錯誤
timeout 超時時間

函數

名稱 描述
socket() 以給定的地址家族、套接字類型和協議類型(可選)建立一個套接字對象
socketpair() 以給定的地址家族、套接字類型和協議類型(可選)建立一對套接字對象
create_connection() 常規函數,接受一個地址(主機號,端口號)對,返回套接字對象
fromfd() 以一個打開的文件描述符建立一個套接字對象
ssl() 經過套接字啓動一個安全套接字層鏈接;不執行證書驗證
getaddrinfo() 獲取一個五元組序列形式的地址信息
getnameinfo() 給定一個套接字地址,返回(主機號,端口號)二元組
getfqn() 返回完整的域名
gethostname() 返回當前主機名
gethostbyname() 將一個主機名映射到它的IP地址
gethostbyname_ex() gethostname()的擴展版本,它返回主機名、別名主機集合和IP地址列表
gethostbyaddr() 將一個IP地址映射到DNS信息:返回與gethostbyname_ex()相同的3元組
getprotobyname() 將一個協議名(如tcp)映射到一個數字
getservbyname()/getservbyport() 將一個服務名映射到一個端口號,或者反過來;對於任何一個函數來講,協議名都是可選的
ntohl()/ntohs() 未來自網絡的整數轉換爲主機字節順序
htonl()/htons() 未來自主機的整數轉換爲網絡字節順序
inet_aton()/inet_ntoa() 將IP地址八進制字符串轉換爲32位的包格式,或者反過來(僅用於IPv4地址)
inet_pton()/inet_ntop() 將IP地址字符串轉換成打包的二進制格式,或者反過來(同時適用於IPv4和IPv6地址)
getdefaulttimeout()/setdefaulttimeout() 以秒(浮點數)爲單位返回默認套接字超時時間;以秒(浮點數)爲單位設置默認套接字超時時間

SocketServer模塊

SocketServer是標準庫中的一個高級模塊,它的目標是簡化不少樣板代碼,它們是建立網絡客戶端和服務器所需必須的代碼。

描述
BaseServer 包含核心服務器功能和mix-in類的鉤子;僅用於推導,這樣不會建立這個類的實例;能夠用TCPServerUDPServer建立類的實例
TCPServer/UDPServer 基礎的網絡同步TCP/UDP服務器
UnixStreamServer/UnixDatagramServer 基於文件的基礎同步TCP/UDP服務器
ForkingMixIn/ThreadingMixIn 核心派出或線程功能;只用做mix-in類與一個服務器類配合實現一些異步性;不能直接實例化這個類
ForkingTCPServer/ForkingUDPServer ForkingMixInTCPServer/UDPServer的組合
ThreadingTCPServer/ThreadingUDPServer ThreadingMixInTCPServer/UDPServer的組合
BaseRequestHandler 包含處理服務請求的核心功能;僅僅用於推導,這樣沒法建立這個類的實例;能夠使用StreamRequestHandlerDatagramRequestHandler建立類的實例
StreamRequestHandler/DatagramRequestHandler 實現TCP/UDP服務器的服務處理器

事件包括消息的發送和接受。

類定義只包括一個用來接收客戶端消息的事件處理程序,全部其餘的功能都來自使用的SocketServer類。

建立SocketServer TCP服務器

# coding: utf-8

from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH)

from time import ctime

HOST = ''
PORT = 21567
ADDR = (HOST, PORT)


class MyRequestHandler(SRH):
    def handle(self):
        print('...connected from:', self.client_address)
        self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))

tcpServ = TCP(ADDR, MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()

MyRequestHandler做爲SocketServerStreamRequestHandler的一個子類,並重寫了它的handler()方法。當接收到一個來自客戶端的消息時,就會調用handler()方法。而StreamRequestHandler類將輸入和輸出套接字看做相似文件的對象,所以咱們將使用readline()來獲取客戶端消息,並利用write()將字符串發送回客戶端。

建立SocketServer TCP客戶端

# coding: utf-8

from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpCliSock = socket(AF_INET, SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpCliSock.send(bytes('%s\r\n' % data, 'utf-8'))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.strip())
    tcpCliSock.close()

執行TCP服務器和客戶端

服務端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTservSS.py
waiting for connection...
...connected from: ('127.0.0.1', 36118)
...connected from: ('127.0.0.1', 36120)
...connected from: ('127.0.0.1', 36122)

客戶端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTclntSS.py
> a
b"[Mon Jun  4 15:34:12 2018] b'a\\r\\n'"
> c
b"[Mon Jun  4 15:34:13 2018] b'c\\r\\n'"
>

Twisted框架

Twisted是一個完整的事件驅動的網絡框架,既能夠使用,也能開發完整的異步網絡應用程序和協議。

建立Twisted Reactor TCP服務器

# coding: utf-8

from twisted.internet import protocol, reactor
from time import ctime

PORT = 21567

class TSServProtocol(protocol.Protocol):
    def connectionMade(self):   # 客戶端鏈接到服務器時就會執行
        clnt = self.clnt = self.transport.getPeer().host
        print('...connected from:', clnt)

    def dataReceived(self, data):   # 當服務器收到客戶端經過網絡發送的一些數據時就會調用 dataReceived() 方法
        self.transport.write(bytes('[%s] %s' % (ctime(), data), 'utf-8'))

factory = protocol.Factory()
factory.protocol = TSServProtocol
print('waiting for connection...')
reactor.listenTCP(PORT, factory)
reactor.run()

建立Twisted Reactor TCP客戶端

# coding: utf-8

from twisted.internet import protocol, reactor

HOST = 'localhost'
PORT = 21567

class TSClntProtocol(protocol.Protocol):
    def sendData(self):
        data = input('> ')
        if data:
            print('...sending %s...' % data)
            self.transport.write(bytes(data, 'utf-8'))
        else:
            self.transport.loseConnection()

    def connectionMade(self):
        self.sendData()

    def dataReceived(self, data):
        print(data)
        self.sendData()


class TSClntFactory(protocol.ClientFactory):
    protocol = TSClntProtocol
    clientConnectionLost = clientConnectionFailed = lambda self, connector, reason: reactor.stop()

reactor.connectTCP(HOST, PORT, TSClntFactory())
reactor.run()

執行TCP服務器和客戶端

服務器

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTservTW.py
waiting for connection...
...connected from: 127.0.0.1

客戶端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTclntTW.py
> a
...sending a...
b"[Mon Jun  4 16:40:40 2018] b'a'"
> b
...sending b...
b"[Mon Jun  4 16:40:42 2018] b'b'"
>

參考

  • 《Python核心編程》
相關文章
相關標籤/搜索