python進階之socket詳解

pythontab發表於2016-09-20

Socket的英文原義是“孔”或“插座”。作為BSD UNIX的程式通訊機制,通常也稱作"套接字",用於描述IP地址和埠,是一個通訊鏈的控制程式碼,可以用來實現不同虛擬機器或不同計算機之間的通訊。

網路上的兩個程式透過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。

建立網路通訊連線至少要一對埠號(socket)。socket本質是程式設計介面(API),對TCP/IP的封裝,TCP/IP也要提供可供程式設計師做網路開發所用的介面,這就是Socket程式設計介面;HTTP是轎車,提供了封裝或者顯示資料的具體形式;Socket是發動機,提供了網路通訊的能力。

下面來說一下python的socket。

1.socket模組

要使用socket.socket()函式來建立套接字。其語法如下:

socket.socket(socket_family,socket_type,protocol=0)


socket_family可以是如下引數:


  socket.AF_INET IPv4(預設)

  socket.AF_INET6 IPv6


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


socket_type可以是如下引數:


  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 可靠的連續資料包服務


protocol引數:


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


2.套接字物件內建方法


伺服器端套接字函式


s.bind()   繫結地址(ip地址,埠)到套接字,引數必須是元組的格式例如:s.bind(('127.0.0.1',8009))


s.listen(5)  開始監聽,5為最大掛起的連線數


s.accept()  被動接受客戶端連線,阻塞,等待連線


客戶端套接字函式


s.connect()  連線伺服器端,引數必須是元組格式例如:s.connect(('127,0.0.1',8009))


公共用途的套接字函式


s.recv(1024)  接收TCP資料,1024為一次資料接收的大小


s.send(bytes)  傳送TCP資料,python3傳送資料的格式必須為bytes格式


s.sendall()  完整傳送資料,內部迴圈呼叫send


s.close()  關閉套接字


例項1.簡單實現socket程式

server端

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import socket
import time
IP_PORT = ('127.0.0.1',8009)
BUF_SIZE = 1024
 
tcp_server = socket.socket()
tcp_server.bind(IP_PORT)
tcp_server.listen(5)
 
while True:
    print("waiting for connection...")
    conn,addr = tcp_server.accept()
    print("...connected from:",addr)
    while True:
        data = tcp_server.recv(BUF_SIZE)
        if not data:break
        tcp_server.send('[%s] %s'%(time.ctime(),data))
 
tcp_server.close()

以上程式碼解釋:


1~4行


第一行是Unix的啟動資訊行,隨後匯入time模組和socket模組


5~10行


IP_PORT為全域性變數宣告瞭IP地址和埠,表示bind()函式繫結在此地址上,把緩衝區的大小設定為1K,listen()函式表示最多允許多少個連線同時進來,後來的就會被拒絕掉


11~到最後一行


在進入伺服器的迴圈後,被動等待連線的到來。當有連線時,進入對話迴圈,等待客戶端傳送資料。如果訊息為空,表示客戶端已經退出,就跳出迴圈等待下一個連線到來。得到客戶端訊息後,在訊息前面加一個時間戳然後返回。最後一行不會執行,因為迴圈不會退出所以服務端也不會執行close()。只是提醒不要忘記呼叫close()函式。


client端

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import socket
 
HOST = '127.0.0.1'
PORT = 8009
BUF_SIZE = 1024
ADDR = (HOST,PORT)
 
client = socket.socket()
client.connect(ADDR)
 
while True:
    data = input(">>> ")
    if not data:break
    client.send(bytes(data,encoding='utf-8'))
    recv_data = client.recv(BUF_SIZE)
    if not recv_data:break
    print(recv_data.decode())
     
client.close()

5~11行

HOST和PORT變數表示伺服器的IP地址與埠號。由於演示是在同一臺伺服器所以IP地址都是127.0.0.1,如果執行在其他伺服器上要做相應的修改。埠號要與伺服器端完全相同否則無法通訊。緩衝區大小還是1K。


客戶端套接字在10行建立然後就去連線伺服器端


13~21行


客戶端也無限迴圈,客戶端的迴圈在以下兩個條件的任意一個發生後就退出:1.使用者輸入為空的情況或者伺服器端響應的訊息為空。否則客戶端會把使用者輸入的字串傳送給伺服器進行處理,然後接收顯示伺服器返回來的帶有時間戳的字串。


執行客戶端程式與服務端程式


以下是客戶端的輸入與輸出

[root@pythontab]# python client.py 
>>> hello python
[Thu Sep 15 22:29:12 2016] b'hello python'


以下是服務端輸出

[root@pythontab]# python server.py 
waiting for connection...
...connected from: ('127.0.0.1', 55378)

3.socketserver模組

socketserver是標準庫中的一個高階別的模組。用於簡化實現網路客戶端與伺服器所需要的大量樣板程式碼。模組中已經實現了一些可以使用的類。


例項1:使用socketserver實現與上面socket()例項一樣的功能


服務端程式程式碼

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import socketserver
import time
 
HOST = '127.0.0.1'
PORT = 8009
ADDR = (HOST,PORT)
BUF_SIZE = 1024
 
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            print("...connected from:",self.client_address)
            data = self.request.recv(BUF_SIZE)
            if not data:break
            self.request.send(bytes("%s %s"%(time.ctime(),data)))
 
server = socketserver.ThreadingTCPServer(ADDR,Myserver)
print("waiting for connection...")
server.serve_forever()


11~17行


主要的工作在這裡。從socketserver的BaseRequestHandler類中派生出一個子類,並重寫handle()函式。


在有客戶端發進來的訊息的時候,handle()函式就會被呼叫。


19~21行


程式碼的最後一部分用給定的IP地址和埠加上自定義處理請求的類(Myserver)。然後進入等待客戶端請求與處理客戶端請求的無限迴圈中。


客戶端程式程式碼

import socket
HOST = '127.0.0.1'
PORT = 8009
ADDR = (HOST,PORT)
BUF_SIZE = 1024
 
client = socket.socket()
client.connect(ADDR)
 
while True:
    data = input(">>> ")
    if not data:continue
    client.send(bytes(data,encoding='utf-8'))
    recv_data = client.recv(BUF_SIZE)
    if not recv_data:break
    print(recv_data.decode())
 
client.close()

執行服務端和客戶端程式碼  


下面是客戶端輸出

[root@pythontab]# python socketclient.py 
>>> hello python
Thu Sep 15 23:53:31 2016 b'hello python'
>>> hello pythontab
Thu Sep 15 23:53:49 2016 b'hello pythontab'


下面是服務端輸出

[root@pythontab]# python socketserver.py 
waiting for connection...
...connected from: ('127.0.0.1', 55385)
...connected from: ('127.0.0.1', 55385)
...connected from: ('127.0.0.1', 55385)
...connected from: ('127.0.0.1', 55385)


相關文章