『Python』socket網絡編程

Python3網絡編程html

'''不管是str2bytes或者是bytes2str其編碼方式都是utf-8

str(   ,encoding='utf-8')

bytes(   ,encoding='utf-8')

而在使用.encode('utf-8')時,雖然type類型是byte,但經常報錯'''

 

Socket又稱"套接字",應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間能夠通信。python

socket起源於UNIX,在Unix一切皆文件哲學的思想下,socket是一種"打開—讀/寫—關閉"模式的實現,服務器和客戶端各自維護一個"文件",在創建鏈接打開後,能夠向本身文件寫入內容供對方讀取或者讀取對方內容,通信結束時關閉文件。socket的英文原義是「插槽」或「插座」,就像咱們家裏座機同樣,若是沒有網線的那個插口,電話是沒法通訊的。Socket是實現TCP,UDP協議的接口,便於使用TCP,UDP。
 
# 流程描述:
#
# 1 服務器根據地址類型(ipv4,ipv6)、socket類型、協議建立socket
#
# 2 服務器爲socket綁定ip地址和端口號
#
# 3 服務器socket監聽端口號請求,隨時準備接收客戶端發來的鏈接,這時候服務器的socket並無被打開
#
# 4 客戶端建立socket
#
# 5 客戶端打開socket,根據服務器ip地址和端口號試圖鏈接服務器socket
#
# 6 服務器socket接收到客戶端socket請求,被動打開,開始接收客戶端請求,直到客戶端返回鏈接信息。這時候socket進入阻塞狀態,
#  所謂阻塞即accept()方法一直等到客戶端返回鏈接信息後才返回,開始接收下一個客戶端鏈接請求
#
# 7 客戶端鏈接成功,向服務器發送鏈接狀態信息
#
# 8 服務器accept方法返回,鏈接成功
#
# 9 客戶端向socket寫入信息(或服務端向socket寫入信息)
#
# 10 服務器讀取信息(客戶端讀取信息)
#
# 11 客戶端關閉
#
# 12 服務器端關閉
 

 

Socket 對象(內建)方法

服務器端

s.bind()   
# 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。s.listen()   
# 開始TCP監聽。backlog指定在拒絕鏈接以前,操做系統能夠掛起的最大鏈接數量。該值至少爲1,大部分應用程序設爲5就能夠了。s.accept()   
# 被動接受TCP客戶端鏈接,(阻塞式)等待鏈接的到來

客戶端

s.connect()   
# 主動初始化TCP服務器鏈接,。通常address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。s.connect_ex()   
# connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的函數

s.recv()   
# 接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其餘信息,一般能夠忽略。s.send()   
# 發送TCP數據,將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。s.sendall()   
# 完整發送TCP數據,完整發送TCP數據。將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。s.close()
# 關閉套接字
 
s.recvform()   
# 接收UDP數據,與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。s.sendto()   
# 發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。s.getpeername()   
# 返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。s.getsockname()   
# 返回套接字本身的地址。一般是一個元組(ipaddr,port)s.setsockopt(level,optname,value)   
# 設置給定套接字選項的值。s.getsockopt(level,optname[.buflen])     
# 返回套接字選項的值。s.settimeout(timeout)   
# 設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如connect())s.gettimeout()   
# 返回當前超時期的值,單位是秒,若是沒有設置超時期,則返回None。s.fileno()   
# 返回套接字的文件描述符。s.setblocking(flag)   
# 若是flag爲0,則將套接字設爲非阻塞模式,不然將套接字設爲阻塞模式(默認值)。非阻塞模式下,若是調用recv()沒有發現任何數據,或send()調用沒法當即發送數據,那麼將引發socket.error異常。s.makefile()   
# 建立一個與該套接字相關連的文件

簡單實例

服務端

咱們使用 socket 模塊的  socket 函數來建立一個 socket 對象。socket 對象能夠經過調用其餘函數來設置一個 socket 服務。

如今咱們能夠經過調用 bind(hostname, port) 函數來指定服務的 port(端口)編程

接着,咱們調用 socket 對象的 accept 方法。該方法等待客戶端的鏈接,並返回 connection 對象,表示已鏈接到客戶端。服務器

完整代碼以下:
import socket  # 導入socket模塊
sk = socket.socket() # 建立socket對象 sk.bind(("127.0.0.1", 8888)) # 綁定端口,「127.0.0.1」表明本機地址,8888爲設置連接的端口地址 sk.listen(5) # 設置監聽,最多可有5個客戶端進行排隊 conn, addr = sk.accept() # 阻塞狀態,被動等待客戶端的鏈接print(conn) # conn能夠理解客戶端的socket對象 # <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9005), raddr=('127.0.0.1', 36694)>print(addr) # addr爲客戶端的端口地址 # ('127.0.0.1', 40966) accept_data = conn.recv(1024) # conn.recv()接收客戶端的內容,接收到的是bytes類型數據, accept_data2 = str(accept_data, encoding="utf8") # str(data,encoding="utf8")用「utf8」進行解碼print("".join(("接收內容:", accept_data2, " 客戶端口:", str(addr[1])))) send_data = input("輸入發送內容:") conn.sendall(bytes(send_data, encoding="utf8")) # 發送內容必須爲bytes類型數據,bytes(data, encoding="utf8")用「utf8」格式進行編碼 conn.close()

 

客戶端

接下來咱們寫一個簡單的客戶端實例鏈接到以上建立的服務。端口號爲 8888。

socket.connect(hosname, port ) 方法打開一個 TCP 鏈接到主機爲 「127.0.0.1」 端口爲 port 的服務商。鏈接後咱們就能夠從服務端後期數據,記住,操做完成後須要關閉鏈接。網絡

完整代碼以下:
import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8888))  # 主動初始化與服務器端的鏈接
send_data = input("輸入發送內容:")
sk.sendall(bytes(send_data, encoding="utf8"))
accept_data = sk.recv(1024)
print(str(accept_data, encoding="utf8"))
sk.close()

服務端

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 9008))
sk.listen(5)
while True:
    conn, addr = sk.accept()
    while True:
        accept_data = str(conn.recv(1024),
                          encoding="utf8")
        print("".join(["接收內容:", accept_data, "    客戶端口:", str(addr[1])]))
        if accept_data == "byebye":  # 若是接收到「byebye」則跳出循環結束和第一個客戶端的通信,開始與下一個客戶端進行通信
            break
        send_data = input("輸入發送內p容:")
        conn.sendall(bytes(send_data, encoding="utf8"))
    conn.close()  # 跳出循環時結束通信

客戶端

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 9008))  # 主動初始化與服務器端的鏈接while True:
    send_data = input("輸入發送內容:")
    sk.sendall(bytes(send_data, encoding="utf8"))
    if send_data == "byebye":
        break
    accept_data = str(sk.recv(1024), encoding="utf8")
    print("".join(("接收內容:", accept_data)))
sk.close()
以上簡單實例中,客戶端必須排隊與服務端進行通信,只有當前一個客戶斷與服務端通信完畢後才能與服務斷進行通信,那麼有沒有方法能讓多個客戶端同時與服務端進行通信呢?那麼就要用到socketserver模塊了:
 

簡單併發實例

服務端

import socketserver  # 導入socketserver模塊

class MyServer(socketserver.BaseRequestHandler):  # 建立一個類,繼承自socketserver模塊下的BaseRequestHandler類
    def handle(self):  # 要想實現併發效果必須重寫父類中的handler方法,在此方法中實現服務端的邏輯代碼(不用再寫鏈接準備,包括bind()、listen()、accept()方法)
        while 1:
            conn = self.request
            addr = self.client_address
            # 上面兩行代碼,等於 conn,addr = socket.accept(),只不過在socketserver模塊中已經替咱們包裝好了,還替咱們包裝了包括bind()、listen()、accept()方法
            while 1:
                accept_data = str(conn.recv(1024), encoding="utf8")
                print(accept_data)
                if accept_data == "byebye":
                    break
                send_data = bytes(input(">>>>>"), encoding="utf8")
                conn.sendall(send_data)
            conn.close()


if __name__ == '__main__':
    sever = socketserver.ThreadingTCPServer(("127.0.0.1", 8888),
                                            MyServer)  # 傳入 端口地址 和 咱們新建的繼承自socketserver模塊下的BaseRequestHandler類  實例化對象
    sever.serve_forever()  # 經過調用對象的serve_forever()方法來激活服務端

客戶端

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8888))  # 主動初始化與服務器端的鏈接while True:
    send_data = input("輸入發送內容:")
    sk.sendall(bytes(send_data, encoding="utf8"))
    if send_data == "byebye":
        break
    accept_data = str(sk.recv(1024), encoding="utf8")
    print("".join(("接收內容:", accept_data)))
sk.close()
相關文章
相關標籤/搜索