1. 程式人生 > >Python網絡編程

Python網絡編程

客戶端、服務器、套接字、sockerserver

  • 客戶端/服務器架構

所謂服務器就是一系列硬件或軟件,為一個或多個客戶端(服務的用戶)提供所需要的‘服務‘。它存在的唯一目的就是等待客戶端的請求,並響應它們(提供服務),然後等待更多的請求。另一方面,客戶端因特定的請求而聯系服務器,並發送必要的數據,然後等待服務器的回應,最後完成請求或給出故障信息。服務器無限地運行下去,並不斷地處理請求;而客戶端會進行一次性請求,然後接收該服務,最後結束他們之間的事務。

目前最常見的客戶端/服務器架構如圖所示,其中描繪了多個客戶端通過互聯網從一臺服務器上檢索信息。

技術分享

套接字(socket)是計算機網絡數據結構,它體現了‘通信端點‘的概念。在沒有任何類型的通信開始之前,網絡應用程序必須創建套接字。可以將它們比作電話插孔,沒有它將無法通信。

socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對於文件用打開、讀寫、關閉模式來操作。socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)。套接字通常有兩種類型:基於文件的和面向網絡的。

第一種類型是基於文件的AF_UNIX,其主要用於同一計算機中的兩個進程間通信;第二種是面向網絡的AF_INET,主要用於互聯網上計算機之間的通信。

通常網絡套接字分為面向連接的套接字與無連接的套接字。面向連接的通信提供序列化的、可靠的和不重復的數據交付,而沒有記錄邊界。實現這種連接類型的主要協議是傳輸控制協議(TCP)。為了創建TCP套接字,必須使用SOCK_STREAM作為套接字類型。

技術分享

與傳輸控制協議形成對比的是數據報類型的套接字,它是一種無連接的套接字。這意味著,在通信開始之前並不需要建立連接。此時,在數據傳輸過程並無法保證它的順序性、可靠性或者重復性。然而,數據報保存了記錄邊界,這意味著消息是以整體發送的,而並非分成多個片段。實現這種連接類型的主要協議是用戶數據報(UDP)。為了創建UDP套接字,必須使用SOCK_DGRAM作為套接字類型。

技術分享

  • Python中的網絡編程

s=socket(family,type, proto=0)

參數一:地址簇

socket.AF_INET IPv4(默認)
socket.AF_INET6 IPv6

socket.AF_UNIX 只能夠用於單一的Unix系統進程間通信

參數二:連接類型

socket.SOCK_STREAM 流式socket , for TCP (默認)
socket.SOCK_DGRAM  數據報式socket , for UDP

socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以 ;其次,SOCK_RAW也可以處理特殊的IPv4報文;利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
socket.SOCK_RDM一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。
socket.SOCK_SEQPACKET 可靠的連續數據包服務

參數三:協議

0 (默認)與特定的地址家族相關的協議,如果是0,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

服務器套接字方法

s.bind(address)將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組 (host,port)的形式表示地址。
s.listen(backlog)開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。
s.accept()接受連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。接收TCP客戶的連接(阻塞式)等待連接的到來

客戶端套接字方法

s.connect(address)

連接到address處的套接字。address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。

s.connect_ex(address)同上,只不過會有返回值,連接成功時返回0,連接失敗時候返回編碼,而不是拋出一個異常。

普通的套接字方法

s.recv(bufsize[,flag])接收套接字的數據。數據以字符串形式返回,bufsize指定最多可以接收的數量。flag提供有關消息的其他信息,通常可以忽略。
s.recv_into()
接收TCP消息到指定的緩存區。
sk.recvfrom(bufsize[.flag])與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
s.send(string[,flag])將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容全部發送 。
s.sendall(string[,flag])

將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。內部通過遞歸調用

send,將所有內容發送出去。

s.sendto(string[,flag],address)將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。
s.close()關閉套接字
s.getpeername()返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)
s.getsockname()返回套接字自己的地址。通常是一個元組(ipaddr,port)

面向阻塞的套接字方法

s.setblocking(bool)是否阻塞(默認True),如果設置False,那麽accept和recv時一旦無數據,則報錯。
s.settimeout(timeout)

設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為

None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用於連接的操作(如 client 連接最多等待5s)

s.gettimeout()獲取阻塞套接字操作的超時時間

面向文件的套接字方法

s.fileno()套接字的文件描述符
s.makefile()
創建套接字關聯的文件對象
  • 創建TCP服務器與客戶端

服務器端:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
‘‘‘
這個腳本創建TCP服務器,它接收來自客戶端的消息並打印在終端,返回當前時間戳
‘‘‘
from socket import *
from time import ctime

HOST = ‘‘
PORT = 12345
BUFSIZ = 1024
ADDR = (HOST,PORT)

tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)

while True:
    print(‘waiting for connection...‘)
    tcpCliSock,addr=tcpSerSock.accept()
    print(‘...connected from:‘,addr)

    while True:
        data=tcpCliSock.recv(BUFSIZ)
        print(str(data,‘utf-8‘))
        if not data:
            break
        tcpCliSock.send(bytes(ctime(),‘utf-8‘))
    tcpCliSock.close()
tcpSerSock.close()

客戶端:

# !/usr/bin/env python
# -*- coding:utf-8 -*-

from socket import *

HOST = ‘127.0.0.1‘
PORT = 12345
BUFSIZ = 1024
ADDR = (HOST,PORT)

tcpCliSock = socket(AF_INET,SOCK_STREAM)
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(str(data,‘utf-8‘))
tcpCliSock.close()

運行結果:

服務器端:

waiting for connection...
...connected from: (‘127.0.0.1‘, 62735)
123
1234
3453
45
567

客戶端:

> 123
Sat May  6 21:31:06 2017
> 1234
Sat May  6 21:32:44 2017
> 3453
Sat May  6 21:32:46 2017
> 45
Sat May  6 21:32:47 2017
> 567
Sat May  6 21:32:48 2017
>
  • 創建UDP客戶端與服務器

服務器端:

socket *
time ctime
HOST = PORT = BUFSIZ = ADDR = (HOST,PORT)

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

:
    ()
    data,addr = udpSerSock.recvfrom(BUFSIZ)
    ((data,))
    udpSerSock.sendto((ctime(),),addr)
    (,addr)

udpSerSock.close()

客戶端:

socket *

HOST = PORT = BUFSIZ = ADDR = (HOST,PORT)

udpCliSock = socket(AF_INET,SOCK_DGRAM)

:
    data=()
    data:
        udpCliSock.sendto((data,),ADDR)
    data,ADDR=udpCliSock.recvfrom(BUFSIZ)
    data:
        ((data,))

udpCliSock.close()

輸出結果:

服務器端:

waiting for message... 
123
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message... 
12334
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message... 
123
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message... 
14
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message... 
234
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message... 
5
...received form and return to: (‘127.0.0.1‘, 58765)
waiting for message...

客戶端:

>123
Sun May  7 11:59:46 2017
>12334
Sun May  7 12:02:32 2017
>123
1Sun May  7 12:02:33 2017
>4
2Sun May  7 12:02:34 2017
>34
Sun May  7 12:02:34 2017
>5
Sun May  7 12:02:35 2017
>
  • SocketServer模塊

SocketServer是標準庫中的一個高級模塊,它的目的是簡化很多樣板代碼,它們是創建網絡客戶端和服務器必須的代碼。這個模塊中有各種各樣的類,這些類幫助程序員隱藏具體的實現細節,除此之外以面相對象的方式處理事務有助於組織數據,以及邏輯性地將功能放在正確的地方。在python3中SocketServer改名為socketserver。

各個類之間的繼承關系:

| BaseServer |
      |
      v
| TCPServer |------->| UnixStreamServer |
      |
      v
| UDPServer |------->| UnixDatagramServer |


描述

BaseServer

包含核心服務器功能和mix-in類的鉤子,僅用於推導,這樣不會創建這個類的實例:可以用TCPServer或UDPServer創建類的實例
TCPServer/UDPServer基於的網絡同步TCP/UDP服務器

UnixStreamServer /

UnixDatagramServer

基於文件的基礎同步TCP/UDP服務器


上面四個服務類都是同步處理請求的,即一個請求沒處理完不能處理下一個請求。當要想支持異步模型,可以利用多繼承讓server類繼承ForkingMixIn或ThreadingMixIn類。

描述

ForkingMixIn/

ThreadingMixIn

核心派出或線程功能:只作用mix-in類域一個服務器類配合實現一些異步性;不能直接實例化。

ForkingTCPServer/

ForkingUDPServer

ForkingMixIn和TCPServer/UDPServer的組合,利用多進程實現異步
ThreadingTCPServer/

ThreadingUDPServer

ThreadingMixIn和TCPServer/UDPServer的組合,利用多線程實現異步

在SocketServer模塊中,服務器處理請求主要通過BaseRequestHandler類來實現,在這個類中有多個處理函數可以使用。

setup()

在handle()方法之前調用執行請求的初始化動作,默認不執行任何操作。

handle()

此函數必須完成對服務器所有的請求工作,有些實例屬性對其可獲得,

請求通過self.request函數,

客戶端地址通過self.client_address函數,

服務器實例通過self.server函數,

self.request對於流服務self.request是套接字對象,對於數據報文self.request是一系列字符串和套接字。

finish()

在handle()函數後調用此方法,執行請求後的清理操作,默認不執行任何操作,如果setup()出現異常,此函數不會被調用。

服務器端實例

socketserver (TCPServer TCP, StreamRequestHandler SRH)
time ctime

HOST = PORT = ADDR = (HOST,PORT)


MyRequestHandler(SRH):
    setup():
        ()

    handle():
        conn=.request
        (,.client_address)
        ((conn.recv(),))
        conn.send((ctime(),))

    finish():
        ()
        ()

tcpServ = TCP(ADDR,MyRequestHandler)
()
tcpServ.serve_forever()

客戶端實例:


socket *

HOST = PORT = BUFSIZ = ADDR = (HOST,PORT)

:
    tcpCliSock = socket(AF_INET,SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    data=()
    data:
        tcpCliSock.send((data,))
    data=tcpCliSock.recv(BUFSIZ)
    data:
        ((data,))
    tcpCliSock.close()

運行結果:

服務器端:

waiting for connection...
處理請求前!!!
...connected from: (‘127.0.0.1‘, 62042)
1223
處理請求後!!!
----------------------
處理請求前!!!
...connected from: (‘127.0.0.1‘, 62051)
123
處理請求後!!!
----------------------
處理請求前!!!
...connected from: (‘127.0.0.1‘, 62052)
5435
處理請求後!!!
----------------------
處理請求前!!!
...connected from: (‘127.0.0.1‘, 62053)
574
處理請求後!!!
----------------------
處理請求前!!!
...connected from: (‘127.0.0.1‘, 62054)
68
處理請求後!!!
----------------------

客戶端:

>1223
Sun May  7 13:01:45 2017
>123
Sun May  7 13:01:46 2017
>5435
Sun May  7 13:01:47 2017
>574
Sun May  7 13:01:48 2017
>68
Sun May  7 13:01:49 2017
>


本文出自 “隨風而飄” 博客,請務必保留此出處http://yinsuifeng.blog.51cto.com/10173491/1922818

Python網絡編程