python socket編程

什麼是 Socket?

Socket是網絡編程的一個抽象概念。通常我們用一個Socket表示“打開了一個網絡鏈接”,而打開一個Socket需要知道目標計算機的IP地址和端口號,再指定協議類型即可。

Socket又稱"套接字",應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間可以通訊。

網絡中的 Socket 和 Socket API 是用來跨網絡的消息傳送的,它提供了 進程間通信(IPC) 的一種形式。Socket 應用最常見的類型就是 客戶端/服務器 應用,服務器用來等待客戶端的鏈接。

Socket API 概覽

Python 提供了兩個基本的 socket 模塊:

  • Socket 它提供了標準的BSD Socket API。
  • SocketServer 它提供了服務器重心,可以簡化網絡服務器的開發。

Socket 類型

套接字格式:socket(family, type[,protocal]) 使用給定的套接族,套接字類型,協議編號(默認爲0)來創建套接字

socket 類型 描述
socket.AF_UNIX 用於同一臺機器上的進程通信(既本機通信)
socket.AF_INET 用於服務器與服務器之間的網絡通信
socket.AF_INET6 基於IPV6方式的服務器與服務器之間的網絡通信
socket.SOCK_STREAM 基於TCP的流式socket通信
socket.SOCK_DGRAM 基於UDP的數據報式socket通信
socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次SOCK_RAW也可以處理特殊的IPV4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭
socket.SOCK_SEQPACKET 可靠的連續數據包服務

Socket 函數

  • TCP發送數據時,已建立好TCP鏈接,所以不需要指定地址,而UDP是面向無連接的,每次發送都需要指定發送給誰。
  • 服務器與客戶端不能直接發送列表,元素,字典等帶有數據類型的格式,發送的內容必須是字符串數據。
服務器端 Socket 函數
Socket 函數 描述
s.bind(address) 將套接字綁定到地址,在AF_INET下,以tuple(host, port)的方式傳入,如s.bind((host, port))
s.listen(backlog) 開始監聽TCP傳入連接,backlog指定在拒絕鏈接前,操作系統可以掛起的最大連接數,該值最少爲1,大部分應用程序設爲5就夠用了
s.accept() 接受TCP鏈接並返回(conn, address),其中conn是新的套接字對象,可以用來接收和發送數據,address是鏈接客戶端的地址。
客戶端 Socket 函數
Socket 函數 描述
s.connect(address) 鏈接到address處的套接字,一般address的格式爲tuple(host, port),如果鏈接出錯,則返回socket.error錯誤
s.connect_ex(address) 功能與s.connect(address)相同,但成功返回0,失敗返回errno的值
公共 Socket 函數
Socket 函數 描述
s.recv(bufsize[, flag]) 接受TCP套接字的數據,數據以字符串形式返回,buffsize指定要接受的最大數據量,flag提供有關消息的其他信息,通常可以忽略
s.send(string[, flag]) 發送TCP數據,將字符串中的數據發送到鏈接的套接字,返回值是要發送的字節數量,該數量可能小於string的字節大小
s.sendall(string[, flag]) 完整發送TCP數據,將字符串中的數據發送到鏈接的套接字,但在返回之前嘗試發送所有數據。成功返回None,失敗則拋出異常
s.recvfrom(bufsize[, flag]) 接受UDP套接字的數據u,與recv()類似,但返回值是tuple(data, address)。其中data是包含接受數據的字符串,address是發送數據的套接字地址
s.sendto(string[, flag], address) 發送UDP數據,將數據發送到套接字,address形式爲tuple(ipaddr, port),指定遠程地址發送,返回值是發送的字節數
s.close() 關閉套接字
s.getpeername() 返回套接字的遠程地址,返回值通常是一個tuple(ipaddr, port)
s.getsockname() 返回套接字自己的地址,返回值通常是一個tuple(ipaddr, port)
s.setsockopt(level, optname, value) 設置給定套接字選項的值
s.getsockopt(level, optname[, buflen]) 返回套接字選項的值
s.settimeout(timeout) 設置套接字操作的超時時間,timeout是一個浮點數,單位是秒,值爲None則表示永遠不會超時。一般超時期應在剛創建套接字時設置,因爲他們可能用於連接的操作,如s.connect()
s.gettimeout() 返回當前超時值,單位是秒,如果沒有設置超時則返回None
s.fileno() 返回套接字的文件描述
s.setblocking(flag) 如果flag爲0,則將套接字設置爲非阻塞模式,否則將套接字設置爲阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那麼將引起socket.error異常。
s.makefile() 創建一個與該套接字相關的文件

TCP Sockets

爲什麼應該使用 TCP 協議?

  • 可靠的:網絡傳輸中丟失的數據包會被檢測到並重新發送
  • 有序傳送:數據按發送者寫入的順序被讀取

相反,使用 socket.SOCK_DGRAM 創建的 用戶數據報協議(UDP) Socket 是 不可靠 的,而且數據的讀取寫發送可以是 無序的
下面的示意圖中,我們將看到 Socket API 的調用順序和 TCP 的數據流:

python socket編程

左邊表示服務器,右邊則是客戶端

左上方開始,注意服務器創建「監聽」Socket 的 API 調用:

  • socket()
  • bind()
  • listen()
  • accept()

「監聽」Socket 做的事情就像它的名字一樣。它會監聽客戶端的連接,當一個客戶端連接進來的時候,服務器將調用 accept() 來「接受」或者「完成」此連接

客戶端調用 connect() 方法來建立與服務器的鏈接,並開始三次握手。握手很重要是因爲它保證了網絡的通信的雙方可以到達,也就是說客戶端可以正常連接到服務器,反之亦然

上圖中間部分往返部分表示客戶端和服務器的數據交換過程,調用了 send() 和 recv()方法

下面部分,客戶端和服務器調用 close() 方法來關閉各自的 socket

Socket 編程思想

TCP 服務器
1、創建套接字,綁定套接字到本地IP與端口

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind()

2、開始監聽鏈接

s.listen()

3、進入循環,不斷接受客戶端的鏈接請求

While True:
    s.accept()

4、接收客戶端傳來的數據,並且發送給對方發送數據

s.recv()
s.sendall()

5、傳輸完畢後,關閉套接字

s.close()

TCP 客戶端
1、創建套接字並鏈接至遠端地址

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect()

2、鏈接後發送數據和接收數據

s.sendall()
s.recv()

3、傳輸完畢後,關閉套接字

Socket 編程實踐之服務器端代碼

import socket

HOST = '192.168.1.100'
PORT = 8001

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)

print 'Server start at: %s:%s' %(HOST, PORT)
print 'wait for connection...'

while True:
    conn, addr = s.accept()
    print 'Connected by ', addr

    while True:
        data = conn.recv(1024)
        print data

        conn.send("server received you message.")

# conn.close()

Socket 編程實踐之客戶端代碼

import socket
HOST = '192.168.1.100'
PORT = 8001

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

while True:
    cmd = raw_input("Please input msg:")
    s.send(cmd)
    data = s.recv(1024)
    print data

    #s.close()

參考
https://gist.github.com/kevinkindom/108ffd675cb9253f8f71
https://keelii.com/2018/09/24/socket-programming-in-python/

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