python網絡編程

簡介

網絡編程就是在程序中實現網絡中兩臺計算機的通信。而用Python進行網絡編程,就是在Python程序本身這個進程內,連接別的服務器進程的通信端口進行通信。

初識Socket

Socket(又稱套接字)起源於Unix,是應用層與TCP/IP協議族通信的中間軟件抽象層。複雜的TCP/IP協議族隱藏在了Socket接口內部,用戶只需要簡單地使用Socket接口來進行網絡編程。應用程序通常是通過Socket向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間可以通訊。

創建Socket
在Python中,我們用 socket()函數來創建套接字,語法格式如下:
-> socket(family,type[,protocol])
參數解釋:

family:socket家族 描述
socket.AF_UNIX 只能夠用於單一的Unix系統進程間通信
socket.AF_INET 指定使用IPv4協議進行服務器間網絡通信
socket.AF_INET6 指定使用IPv6協議進行服務器間網絡通信
type:socket類型 描述
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_SEQPACKET 可靠的連續數據包服務
protocol:socket協議 描述
一般不填且默認爲0 系統會根據地址格式和套接字類別,自動選擇一個合適的協議。

Socket常用方法

服務器端方法 描述
socket.bind(address) 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。
socket.listen(backlog) 開始監聽TCP傳入連接。backlog指定在拒絕連接之前,操作系統可以掛起的最大連接數量。該值至少爲1,大部分應用程序設爲5就可以了。
socket.accept() 被動接受TCP客戶端連接並返回(sock,address),其中sock是新的套接字對象,可以用來接收和發送數據,address是連接客戶端的地址。
客戶端方法 描述
socket.connect(address) 主動初始化TCP服務器連接,一般address的格式爲元組(hostname,port),如果連接出錯,返回socket.error錯誤。
socket.connect_ex(address) connect()的擴展版本,出錯時返回出錯碼,而不是拋出異常。
公共方法 描述
socket.recv(bufsize[,flag]) 接受TCP套接字的數據。數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其他信息,通常可以忽略。
socket.send(string[,flag]) 發送TCP數據。將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。
socket.sendall(string[,flag]) 完整發送TCP數據。將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。
socket.recvfrom(bufsize[.flag]) 接受UDP套接字的數據。與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
socket.sendto(string[,flag],address) 發送UDP數據。將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。
socket.close() 關閉套接字。

TCP編程

大多數網絡通信連接都是可靠的TCP連接。創建TCP連接時,主動發起連接的叫客戶端,被動響應連接的叫服務器;連接成功後,通信雙方都能以流的形式發送數據。

在Python中用TCP協議進行Socket編程十分簡單,對於客戶端,要主動連接服務器的IP和指定端口,對於服務器,要首先監聽指定端口,然後,對每一個新的連接,創建一個線程或進程來處理。通常,服務器程序會無限運行下去。要注意的是,一個端口不能同被兩個Socket綁定。

下圖展示了TCP服務端和客戶端各自Socket創建以及它們之間的交互過程:
這裏寫圖片描述

接下來是一個簡單的示例程序(建議自己敲下來跑一遍),服務器端接收客戶端的連接請求,把客戶端發過來的字符串加上Hello再發回去。

服務器端代碼:

#!/usr/bin/python
# 文件名:tcp_server.py

import socket
import threading
import time

# 實現連接成功後的交互,參數sock爲套接字對象,addr爲客戶端地址
def tcplink(sock, addr):
    # 輸出連接成功的提示:
    print('Accept new connection from %s:%s...' % addr)
    # 發送TCP數據:
    sock.send(b'Hello, What\'s your name?') 
    while True:
        # 接收小於 1024 字節的數據
        data = sock.recv(1024) 
        time.sleep(1)
        if not data or data.decode('utf-8') == 'disconnect':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    # 關閉Socket,一次完整的網絡通信就此結束
    sock.close()
    print('Connection from %s:%s closed.' % addr)

# 創建一個基於IPv4和TCP協議的socket:    
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定地址(host,port)到套接字:
s.bind(('127.0.0.1', 9999))
# 開始監聽,最大連接數設爲5:
s.listen(5)

print('TCP Server is running...')    
print('Waiting for connection...')    
while True:
    # 被動接受TCP客戶端連接,(阻塞式)等待連接的到來:
    sock, addr = s.accept()
    # 創建新線程來處理TCP連接:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

客戶端代碼:

#!/usr/bin/python
# 文件名:tcp_client.py

import socket

# 創建一個基於IPv4和TCP協議的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接,指定主機和端口:
s.connect(('127.0.0.1', 9999))
# 接收歡迎消息:
print(s.recv(1024).decode('utf-8'))

# 持續與服務器交互:
while True:
    # 獲取用戶輸入:
    msg = input('Your input:')
    if not msg or msg == 'quit':
        break
    # 發送數據:
    s.send(msg.encode('utf-8'))
    # 輸出服務器返回的消息
    print('From server:',s.recv(1024).decode('utf-8'))

# 發送斷開連接的指令    
s.send(b'disconnect')
# 套接字關閉
s.close()

打開三個命令行窗口,一個運行服務器端程序,另外兩個運行客戶端程序,效果如下圖:
這裏寫圖片描述

UDP編程

相對TCP,UDP則是面向無連接的協議。使用UDP協議時,不需要建立連接,只需要知道對方的IP地址和端口號,就可以直接發數據包。但是,數據包能否到達是無法確定的。

雖然用UDP傳輸數據不可靠,但它的優點是和TCP比,速度快,對於不要求可靠到達的數據,就可以使用UDP協議。

接下來我們使用UDP協議來實現與上面示例的程序。

服務器端代碼:

#!/usr/bin/python
# 文件名:udp_server.py

import socket

# 創建一個基於IPv4和UDP協議的socket:    
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定地址(host,port)到套接字:
s.bind(('127.0.0.1', 9999))

print('UDP Server is running...')
print('Waiting for message...')  
while True:
    # 接收數據,recvfrom()方法返回數據和客戶端的地址與端口:
    data, addr = s.recvfrom(1024)
    print('Received from %s:%s.' % addr)
    s.sendto(b'Hello, %s!' % data, addr)

客戶端代碼:

#!/usr/bin/python
# 文件名:udp_client.py

import socket

# 創建一個基於IPv4和UDP協議的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 持續與服務器交互:
while True:
    # 獲取用戶輸入
    msg = input('Your input:')
    if not msg or msg == 'quit':
        break
    # 無需連接,直接發送數據到服務器Socket綁定的地址:
    s.sendto(msg.encode('utf-8'), ('127.0.0.1', 9999))
    # 輸出服務器返回的消息:
    print('From server:',s.recv(1024).decode('utf-8'))

# 套接字關閉
s.close()

打開三個命令行窗口,一個運行服務器端程序,另外兩個運行客戶端程序,效果如下圖:
這裏寫圖片描述


參考文章:
- python socket編程
- 菜鳥教程-python3網絡編程
- 廖雪峯-python3教程

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