Python進階之網路編程

来源:https://www.cnblogs.com/yifchan/archive/2019/05/14/python-1-22.html
-Advertisement-
Play Games

Python進階之網路編程,內容包括 網路通信,socket,udp,tcp。其中,網路通信 包括 使用網路的目的,ip地址,埠;socket 包括 socket的概念,創建socket;udp 包括 udp發送數據,udp接收數據,udp接發數據總結,埠綁定的問題,單工半雙工全雙工;tcp 包... ...


網路通信

使用網路的目的

把多方鏈接在一起,進行數據傳遞;
網路編程就是,讓不同電腦上的軟體進行數據傳遞,即進程間通信;

ip地址

ip地址概念和作用

IP地址是什麼:比如192.168.1.1 這樣的一些數字;
ip地址的作用:用來在電腦中 標識唯一一臺電腦,比如192.168.1.1;在本地區域網是唯一的。

網卡信息

查看網卡信息

Linux:ifconfig
windows:ipconfig

  • ensxx:用來與外部進行通信的網卡;
  • lo:環回網卡,用來進行本地通信的;

linux關閉/開啟網卡:sudo ifconfig ensxx down/up

ip和ip地址的分類

ip分為ipv4和ipv6

ip地址分為:

  • A類地址
  • B類地址
  • C類地址
  • D類地址--用於多播
  • E類地址--保留地址,因ipv6誕生,已無用
  • 私有ip

單播--一對一
多播--一對多
廣播--多對多

ip:標識電腦;
埠:標識電腦上的進程(正在運行的程式);
ip和埠一起使用,唯一標識主機中的應用程式,進行統一軟體的通信;

埠分類

知名埠

固定分配給特定進程的埠號,其他進程一般無法使用這個埠號;
小於1024的,大部分都是知名埠;
範圍從0~1023;

動態埠

不固定分配,動態分配,使用後釋放的埠號;
範圍1024~65535;

socket

socket的概念

socket是進程間通信的一種方式,能實現不同主機間的進程間通信,即socket是用來網路通信必備的東西;

創建socket

創建套接字:

import socket
soc = socket.socket(AddressFamily, Type)

函數socket.socket創建一個socket,該函數有兩個參數:
Address Family:可選 AF_INET(用於internet進程間通信)和AF_UNIX(用於同一臺機器進程間通信);
Type:套接字類型,可選 SOCK_STREAM(流式套接字,主用於TCP協議)/SOCK_DGRAM(數據報套接字,主用於UDP套接字);

創建tcp套接字

import socket

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...
soc.close()

創建udp套接字

import socket

soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
...
soc.close()

udp

udp使用socket發送數據

在同一區域網內發消息;
如果用虛擬機和windows,要用橋接模式,確保在同一區域網內;

import socket


def main():
    # 創建一個udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 使用套接字收發數據
    udp_socket.sendto(b"hahaha", ("193.168.77.1", 8080))
    # 關閉套接字
    udp_socket.close()


if __name__ == "__main__":
    main()

udp發送數據的幾種情況:

  1. 在固定數據的引號前加b,不能使用於用戶自定義數據;
  2. 用戶自定義數據,併進行發送,使用.encode("utf-8")進行encode編碼
  3. 用戶迴圈發送數據
  4. 用戶迴圈發送數據並可以退出

只貼出最後一種情況,即完整代碼

import socket


def main():
    # 創建一個udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while 1:
        # 從鍵盤獲取要發送的數據
        send_data = input("請輸入你要發送的數據:")
        if send_data == "exit":
            break
        # 使用套接字收發數據
        udp_socket.sendto(send_data.encode("utf-8"), ("193.168.77.1", 8080))

    # 關閉套接字
    udp_socket.close()


if __name__ == "__main__":
    main()

udp接收數據

接收到的數據是一個元組,元組第一部分是發送方發送的內容,元組第二部分是發送方的ip地址和埠號;

import socket


def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    localaddr = ('', 8080)
    udp_socket.bind(localaddr)  # 必須綁定自己電腦的ip和埠

    # 接收數據
    recv_data = udp_socket.recvfrom(1024)
    # recv_data這個變數存儲的是一個元組,例如 (b'hahaha', ('192.168.77.1', 8888))
    recv_msg = recv_data[0]
    send_addr = recv_data[1]
    # print("%s 發送了:%s" % (str(send_addr), recv_msg.decode("utf-8")))  # linux發送的數據用utf8解碼
    print("%s 發送了:%s" % (str(send_addr), recv_msg.decode("gbk")))  # windows發送的數據用gbk解碼

    udp_socket.close()


if __name__ == "__main__":
    main()

udp接發數據總結

發送數據的流程:

  1. 創建套接字
  2. 發送數據
  3. 關閉套接字

接收數據的流程:

  1. 創建套接字
  2. 綁定本地自己的信息,ip和埠
  3. 接收數據
  4. 關閉套接字

埠綁定的問題

  • 如果在你發送數據時,還沒有綁定埠,那麼操作系統就會隨機給你分配一個埠,迴圈發送時用的是同一個埠;
  • 也可以先綁定埠,再發送數據。

udp發送消息時自己綁定埠示例

import socket


def main():
    # 創建一個udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 綁定埠
    udp_socket.bind(('192.168.13.1', 8080))
    while 1:
        # 從鍵盤獲取要發送的數據
        send_data = input("請輸入你要發送的數據:")
        if send_data == "exit":
            break
        # 使用套接字收發數據
        udp_socket.sendto(send_data.encode("utf-8"), ("193.168.77.1", 8080))

    # 關閉套接字
    udp_socket.close()  # 按ctrl+c退出


if __name__ == "__main__":
    main()

但應註意,同一埠在同一時間不能被兩個不同的程式同時使用

單工,半雙工,全雙工

單工半雙工全雙工的理解

單工:
只能單向發送信息,別人接收,別人不能回覆消息,比如廣播;

半雙工:
兩個人都能發消息,但是在同一時間只能有一個人發消息,比如對講機;

全雙工
兩個人都能發消息,能同時發,比如打電話;

udp使用同一套接字收且發數據

"""socket套接字是全雙工"""
import socket


def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(('192.168.13.1', 8080))
    # 讓用戶輸入要發送的ip地址和埠
    dest_ip = input("請輸入你要發送數據的ip地址:")
    dest_port = int(input("請輸入你要發送數據的埠號:"))

    # 從鍵盤獲取要發送的數據
    send_data = input("請輸入你要發送的數據:")
    # 使用套接字收發數據
    udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
    # 套接字可以同時 收發數據;
    recv_data = udp_socket.recvfrom(1024)
    print(recv_data)

    # 關閉套接字
    udp_socket.close()  # 按ctrl+c退出


if __name__ == "__main__":
    main()

在這裡體現不出來socket是全雙工,因為現在解釋器只能按照流程,一步一步走下去,後面學習了進程線程協程就可以做到了。

tcp

tcp-可靠傳輸

tcp採取的機制

  1. 採用發送應答機制
  2. 超時重傳
  3. 錯誤校驗
  4. 流量控制和阻塞管理

tcp與udp的區別

  1. tcp更安全可靠,udp相對沒那麼安全可靠;
  2. 面向連接
  3. 有序數據傳輸
  4. 重發丟失的數據
  5. 捨棄重覆的數據包
  6. 無差錯的數據傳輸
  7. 阻塞/流量控制

tcp,udp應用場景

tcp應用場景:下載,發送消息
udp應用場景:電話,視頻直播等

tcp客戶端

tcp客戶端發送數據

import socket


def main():
    # 1.創建tcp的套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.鏈接伺服器
    tcp_socket.connect(('193.168.11.1', 8080))
    # 3.發送/接收消息
    send_data = input("請輸入你要發送的消息:")
    tcp_socket.send(send_data.encode("utf-8"))
    # 4.關閉套接字
    tcp_socket.close()


if __name__ == "__main__":
    main()

tcp伺服器

監聽套接字,專門用來監聽的;
accept會對應新創建的套接字,當監聽套接字收到一個請求後,將該請求分配給新套接字,由此監聽套接字可以繼續去監聽了,而新套接字則為該胡克段服務。

import socket


def main():
    # 創建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_service_socket.bind(('', 8080))
    # 讓預設的套接字由主動變為被動
    tcp_service_socket.listen(128)

    # 等待客戶端的鏈接
    new_client_socket, client_addr = tcp_service_socket.accept()
    print("鏈接的客戶端地址為:", client_addr)
    # 接收客戶端發送過來的請求
    recv_data = new_client_socket.recvfrom(1024)
    print(recv_data)
    # 給客戶端回送消息
    new_client_socket.send("hahahah".encode("utf-8"))

    new_client_socket.close()
    tcp_service_socket.close()


if __name__ == '__main__':
    main()

listen裡面的參數,表示同時只允許128個鏈接訪問。

QQ不綁定埠的運行原理-擴展

udp和tcp並用;
使用QQ,先登錄,登錄後告訴騰訊伺服器此QQ運行的埠,發消息時,通過騰訊伺服器轉發給另一個QQ;
不綁定埠也有一個好處,就是允許多開,即一個電腦上可以運行多個QQ;

recv和recvfrom的區別

recvfrom裡面不僅有發過來的數據,還有發過來數據的人的信息;
recv裡面就只有數據;

tcp客戶端服務端流程梳理

tcp伺服器流程梳理

  1. 創建伺服器套接字
  2. 綁定本地信息
  3. 讓預設的套接字由主動變為被動
  4. 等待客戶端的鏈接,堵塞
  5. 被客戶端鏈接後,創建一個新的客服套接字為客戶端服務;
  6. 接收客戶端發送的消息,堵塞
  7. 接收客戶端發送的消息後,給客戶端回消息
  8. 關閉客服套接字,關閉服務端套接字

tcp註意點

  1. tcp伺服器一般情況下都需要綁定,否則客戶端找不到這個伺服器。
  2. tcp客戶端一般不綁定,因為是主動鏈接伺服器,所以只要確定好伺服器的ip, port等信息就好,本地客戶端可以隨機。
  3. tcp伺服器通過listen可以將socket創建出來的主動套接字變為被動的,這是做tcp伺服器時必須要做的。
  4. 當客戶端需要鏈接伺服器時,就需要使用connect進行鏈接, udp是不需要鏈接的而是直接發送,但是tcp必須先鏈接,只有鏈接成功才能通信。

  5. 當一個tcp客戶端連接伺服器時,伺服器端會有1個新的套接字,這個套接字用來標記這個客戶端,單獨為這個客戶端服務。

  6. liston後的套接字是被動套接字,用來接收新的客戶端的鏈接請求的,而accept返回的新套接字是標記這個新客戶端的。

  7. 關閉isten後的套接字意味著被動套接字關閉了,會導致新的客戶端不能夠鏈接伺服器,但是之前已經鏈接成功的客戶端正常通信。

  8. 關閉accept返回的套接字意味著這個客戶端已經服務完畢。

9.當客戶端的套接字調用close後.伺服器端會recv解堵塞,並且返回的長度為0,因此伺服器可以通過 返回數據的長度來區別客戶端是否已經下線。

tcp應用案例

示例1-為一個用戶辦理一次業務:

"""可以理解為銀行一個客服為排隊的人員辦理業務"""

import socket


def main():
    # 1.創建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.綁定本地信息
    tcp_service_socket.bind(('', 8080))

    # 3.讓預設的套接字由主動變為被動
    tcp_service_socket.listen(128)
    while 1:
        # 4.等待客戶端的鏈接
        new_client_socket, client_addr = tcp_service_socket.accept()
        print("鏈接的客戶端地址為:", client_addr)
        # 接收客戶端發送過來的請求
        recv_data = new_client_socket.recvfrom(1024)
        print(recv_data)
        # 給客戶端回送消息
        new_client_socket.send("hahahah".encode("utf-8"))
        # 關閉套接字
        new_client_socket.close()

    tcp_service_socket.close()


if __name__ == '__main__':
    main()

示例2-為同一用戶服務多次並判斷一個用戶是否服務完畢:

"""可以理解為銀行一個客服為排隊的人員辦理業務"""

import socket


def main():
    # 1.創建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.綁定本地信息
    tcp_service_socket.bind(('', 8080))

    # 3.讓預設的套接字由主動變為被動
    tcp_service_socket.listen(128)
    while 1:
        # 4.等待客戶端的鏈接
        new_client_socket, client_addr = tcp_service_socket.accept()
        print("鏈接的客戶端地址為:", client_addr)
        # 迴圈目的:為同一個客戶服務多次
        while 1:
            # 接收客戶端發送過來的請求
            recv_data = new_client_socket.recvfrom(1024)
            print(recv_data)
            # 如果recv解堵塞,那麼有兩種方式
            # 1.客戶端發了數據過來
            # 2.客戶端調用了close
            if recv_data:
                # 給客戶端回送消息
                new_client_socket.send("hahahah".encode("utf-8"))
            else:
                break
        # 關閉套接字
        new_client_socket.close()

    tcp_service_socket.close()


if __name__ == '__main__':
    main()

示例3-tcp文件下載客戶端和服務端:

文件下載客戶端

import socket


def main():
    # 1.創建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.獲取伺服器的ip,port
    dest_ip = input("請輸入你要鏈接的伺服器ip:")
    dest_port = input("請輸入你要鏈接的埠:")
    # 3.鏈接伺服器
    tcp_socket.connect((dest_ip, dest_port))

    # 4.獲取下載的文件名字
    want_file = input("請輸入你要下載的文件:")
    # 5.將文件名字發送到伺服器
    tcp_socket.send(want_file.encode("utf-8"))

    # 6.接收要下載的文件
    file_data = tcp_socket.recv(1024)
    # 7.將接收文件的數據寫入一個文件中
    if file_data:
        with open("[復件]" + want_file, "wb") as f:
            f.write(file_data)

    # 8.關閉套接字
    tcp_socket.close()
    pass


if __name__ == '__main__':
    main()

文件下載服務端

import socket


def send_file2client(new_socket, client_addr):
    # 1.接受客戶端發送過來的 要下載的文件名
    want_file = new_socket.recv(1024).decode("utf-8")
    print("客戶端 %s 要接收的文件為:%s" % (str(client_addr), want_file))
    # 2.讀取文件數據
    file_data = None
    try:
        f = open(want_file, "rb")
        file_data = f.read()
        f.close()
    except Exception as e:
        print("你要下載的文件 %s 不存在" % want_file)

    # 3.發送文件的數據給客戶端
    if file_data:
        new_socket.send(file_data)


def main():
    # 1.創建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.綁定本地信息
    tcp_socket.bind(('', 8080))
    # 3.套接字被動接受 listen
    tcp_socket.listen(128)
    while 1:
        # 4.等待客戶端的鏈接 accept
        new_socket, client_addr = tcp_socket.accept()
        # 5.調用函數發送文件到客戶端
        send_file2client(new_socket, client_addr)
        # 7.關閉套接字
        new_socket.close()

    tcp_socket.close()


if __name__ == '__main__':
    main()

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在本次學習過程中,我們把封裝帶到一個全新的境界:把方法調用(method invocation)封裝起來 。沒錯,通過封裝方法調用,我們可以把運算塊包裝成形。 所以調用此運算的對象不需要關心事情是如何進行的,只要知道如何使用包裝成形的方法來完成它就可以。通過封裝方法調用,也可以做一些很聰明的事情,例 ...
  • 概述 基於串口的SD_card系統1, 掃描文件;2, 新建文件;3, 刪除文件;4, 寫入文件;5, 讀取文件。 整個文件系統的串口通信方式都是ASC通信方式。 文件系統分為簡單實用方式和專業使用方式,簡單使用方式只需要普通的串口調試助手就可以完成。專業使用方式需要專用的調試上位機。簡單模式/專業 ...
  • 背景 很多人心裡是想一直做技術的,但是擔心隨著年紀增加,會造成在技術方面的競爭力不足。加上迫於經驗人士的壓力,就在糾結要不要轉管理崗。不是什麼權威意見,僅僅談談個人看法。 個人觀點:做自己想做的事。 優劣勢 技術和管理兩個路線各有優劣勢。用SWOT分析來展現兩個維度的關係。 如果有朋友對到底是走哪個 ...
  • 第1題:閱讀下麵的代碼,默讀出A0,A1至An的最終值。 默讀代碼類的題目,相對來說是比較簡單的。重點去研究列表解析,之後你就可以輕鬆的回答這些問題嘍~ 第2題:如何提高python的運行效率? 1. 數據結構一定要選對 能用字典就不用列表:字典在索引查找和排序方面遠遠高於列表。 2. 多用pyth ...
  • 內容簡介 本書面向所有對機器學習與數據挖掘的實踐及競賽感興趣的讀者,從零開始,以Python編程語言為基礎,在不涉及大量數學模型與複雜編程知識的前提下,逐步帶領讀者熟悉並且掌握當下最流行的機器學習、數據挖掘與自然語言處理工具,如Scikitlearn、NLTK、Pandas、gensim、XGBo ...
  • Java9的日誌級別: ALL 最低級別,系統會輸出所有的日誌信息,會生成大量的·、冗餘的日誌 TRACE 輸出系統的各種跟蹤信息,會生成大量的·、冗餘的日誌 DEBUG 輸出調試信息,會生成較多的日誌 INFO 輸出系統提示用戶的信息 WARNING 輸出警告信息 ERROR 輸出錯誤信息 OFF ...
  • 筆記 7天學完Java基礎之0/7 1.常用命令提示符(cmd) 啟動: Win+R,輸入cmd​ :twisted_rightwards_arrows: cmd 切換盤符 盤符名稱+:(冒號為英文輸入法下的冒號) 進入指定文件夾 cd +文件夾名稱 註意只能夠進入文件夾,不能夠進入文件哦 查看當前 ...
  • 國際化是指應用程式運行時,可根據客戶端OS的國家/地區、語言的不同而顯示不同的界面,比如客戶端OS的語言環境為大陸的簡體中文,程式就顯示為簡體中文,客戶端OS的語言環境為美國——英語,程式就顯示美式英語。 OS的語言環境可在控制面板中手動設置。 國際化的英文單詞是Internationalizati ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...