python3網路編程之socket,python3網路編程

來源:互聯網
上載者:User

python3網路編程之socket,python3網路編程
文章內容:

  • socket介紹
  • socket參數介紹
  • 流程描述
  • socket對象內建方法
  • 基本socket執行個體
  • 通過socket實現簡單ssh並實現接收大資料
socket介紹

socket又稱”通訊端“,應用程式通常通過”通訊端“向網路發出請求或者應答網路請求,使主機間或者一台電腦上的進程可以通訊。

socket起源於Unix,在Unix一切皆檔案的哲學的思想下,socket是一種"開啟—讀/寫—關閉"模式的實現,伺服器和用戶端各自維護一個"檔案",在建立串連開啟後,可以向自己檔案寫入內容供對方讀取或者讀取對方內容,通訊結束時關閉檔案。socket的英文原義是“插槽”或“插座”,就像我們家裡有線電話一樣,如果沒有網線的那個插口,電話是無法通訊的。Socket是實現TCP,UDP協議的介面,便於使用TCP,UDP。

socket參數介紹

sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
參數一:地址簇:
    1. socket.AF_INET   IPv4(預設)
    2. socket.AF_INET6  IPv6
    3. socket.AF_UNIX   只能夠用於單一的Unix系統處理序間通訊

參數二:Socket類型:
    1. 流式socket(SOCK_STREAM)用於TCP通訊(預設)
        流式通訊端提供可靠的,連線導向的通訊流;它使用TCP協議,從而保證了資料轉送的正確性和順序性
    2. 資料報socket(SOCK_DGRAM)用於UDP通訊
        資料通訊端定義了一種不需連線的服務,資料通過相互獨立的報文進行傳輸,是無序的,並且不保證是 可靠的,無差錯的、它使用資料報協議UDP
    3. 原始socket(SOCK_RAW)用於新的網路通訊協定實現的測試等原始通訊端,普通的通訊端無法處理ICMP、IGMP等網路報文,而SOCK_RAW可以, 其次,SOCK_RAW也可以處理特殊的IPv4報文;
        此外,利用原始通訊端,可以通過IP_HDRINCL通訊端選項由使用者構造IP頭

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

流程描述
1.伺服器根據地址類型(Ipv4,Ipv6)、socket類型、協議建立socket2.伺服器為socket綁定IP地址和連接埠號碼3.伺服器socket監聽連接埠號碼請求,隨時準備接收用戶端發來的串連,這時候伺服器的socket並沒有被開啟4.客服端建立socket5.客服端開啟socket,根據伺服器IP地址和連接埠號碼試圖串連伺服器socket6.伺服器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執行個體

伺服器端(SocketServer.py):

import socket# 建立socketserver = socket.socket()# 為socket綁定IP和連接埠號碼server.bind(('localhost', 9999))# 監聽設定連接埠等待用戶端的請求server.listen()print('Waiting for client connect.')# 接受並建立與用戶端的串連,程式在此處開始阻塞,只到有用戶端串連進來...conn, addr = server.accept()print("New connect:", addr)data = conn.recv(1024)print("Accept news:", data)server.close()

用戶端(SocketClinet.py):

import socket# 建立socketclient = socket.socket()# 根據伺服器IP地址和連接埠號碼試圖串連伺服器socketclient.connect(('localhost', 9999))client.send(b'123')client.close()

以上代碼只是實現了伺服器端和用戶端一次互動,那要怎麼實現多次互動呢?

伺服器端支援多次互動(SocketServer.py):

import socket# 建立socketserver = socket.socket()# 為socket綁定IP和連接埠號碼server.bind(('localhost', 9999))# 監聽設定連接埠等待用戶端的請求server.listen()print('Waiting for client connect.')# 接受並建立與用戶端的串連,程式在此處開始阻塞,只到有用戶端串連進來...conn, addr = server.accept()print("New connect:", addr)while True:    data = conn.recv(1024)    # 判斷伺服器接到的資料是否為空白(避免用戶端一斷開,伺服器端進入死迴圈)    if not data:        print("Client disconnect.")        break    print("Accept news:", data)    conn.send(data.upper())server.close()

客服端支援多次互動(SocketClient.py):

import socket# 建立socketclient = socket.socket()# 根據伺服器IP地址和連接埠號碼試圖串連伺服器socketclient.connect(('localhost', 9999))while True:    msg = input(">>>:").strip()    if len(msg) == 0:continue    client.send(msg.encode('utf-8'))    data = client.recv(1024)    print("from server:",data)client.close()

以上代碼雖然實現了伺服器端和用戶端的多次互動,但是用戶端一旦斷開了,伺服器端也會跟著立刻斷開,因為伺服器只有一個while 迴圈,用戶端一斷開,服務端收不到資料 ,就會直接break跳出迴圈,然後程式就退出了,這顯然不是我們想要的結果 ,我們想要的是,用戶端如果斷開了,我們這個服務端還可以為下一個用戶端服務。

Socket實現多串連處理:

import socket# 建立socketserver = socket.socket()# 為socket綁定IP和連接埠號碼server.bind(('localhost', 9999))# 監聽設定連接埠等待用戶端的請求server.listen()while True:    print('Waiting for client connect.')    # 接受並建立與用戶端的串連,程式在此處開始阻塞,只到有用戶端串連進來...    conn, addr = server.accept()    print("New connect:", addr)    while True:        data = conn.recv(1024)        # 判斷伺服器接到的資料是否為空白(避免用戶端一斷開,伺服器端進入死迴圈)        if not data:            print("Client disconnect.")            break        print("Accept news:", data)        conn.send(data.upper())server.close()
View Code

PS:此時伺服器端依然只能同時為一個客戶服務,其客戶來了,得排隊(串連掛起)。

通過socket實現簡單ssh並實現接收大資料

其實在接收大資料的時候會引入一個重要的概念”粘包“,即伺服器端你調用send兩次,當你send調用時,資料其實並沒有立刻被發送給用戶端,而是放到了系統的socket發送緩衝區裡,等緩衝區滿了,或者資料等待逾時了,資料才會被send到用戶端,這樣就把好幾次的小資料拼成一個大資料,統一發送到用戶端了,這麼做的目地是為了提高io利用效率,一次性發送總比連發好幾次效率高嘛。 但也帶來一個問題,就是“粘包”,即2次或多次的資料粘在了一起統一發送了。

我們在這裡必須要想辦法把粘包分開, 因為不分開,你就沒辦法取出來伺服器端返回的命令執行結果的大小呀。so ,那怎麼分開呢?首先你是沒辦法讓緩衝區強制重新整理把資料發給用戶端的。 你能做的,只有一個。就是,讓緩衝區逾時,逾時了,系統就不會等緩衝區滿了,會直接把資料發走,因為不能一個勁的等後面的資料呀,等太久,會造成資料延遲了,那可是極不好的。so如果讓緩衝區逾時呢?

解決方案如下:

伺服器端每發送一個資料給用戶端,就立刻等待用戶端進行回應,即調用 conn.recv(1024), 由於recv在接收不到資料時是阻塞的,這樣就會造成,伺服器端接收不到用戶端的響應,就不會執行後面的conn.sendall(命令結果)的指令,收到用戶端響應後,再發送命令結果時,緩衝區就已經被清空了,因為上一次的資料已經被強制發到用戶端了。

伺服器端:

import socket,os,timeserver = socket.socket()server.bind(('localhost', 9999))server.listen()while True:    print('Waiting for client connect.')    conn, addr = server.accept()    print("new conn:", addr)    while True:        print("等待新指令")        data = conn.recv(1024)        if not data:            print("用戶端已斷開")            break        print("執行命令:", data)        cmd_res = os.popen(data.decode()).read() # 接受字串,執行結果也是字串        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = "cmd has no output..."        conn.send( str(len(cmd_res.encode())).encode("utf-8") )  # 先發大小給用戶端        # 為了防止粘包        # time.sleep(0.5)        client_ack = conn.recv(1024) # wait client  to confirm        print("ack from client:", client_ack)        conn.send(cmd_res.encode("utf-8"))        print("send done")server.close()

用戶端:

import socketclient = socket.socket()client.connect(('localhost', 9999))while True:    cmd = input(">>>:").strip()    if len(cmd) == 0:        continue    client.send(cmd.encode("utf-8"))    cmd_res_size = client.recv(1024) # 接受命令結果長度    print("命令結果大小:", cmd_res_size)    client.send("準備好接受了,loser可以發了".encode("utf-8"))    received_size = 0    received_data = b''    while received_size < int(cmd_res_size.decode()):        data = client.recv(1024)        received_size += len(data) # 每次收到的有可能小於1024,所以必須用len判斷        # print(data.decode())        received_data += data    else:        print("cmd res receive done...", received_size)        print(received_data.decode())client.close()

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.