1. 程式人生 > >python高階程式設計(二)--網路程式設計

python高階程式設計(二)--網路程式設計

1. 認識網路程式設計

  • 網路程式設計從大的方面來講就是對資訊的傳送到接收,中間傳輸為物理線路的作用。小的方面是指編寫執行在多個裝置(計算機)的程式,這些裝置都通過網路連線起來。

2. IP地址

一個IP地址就是一個32位無符號整數,IP地址通常是以一個稱為點分十進位制表示法來表示。每個位元組由它的十進位制值來表示,並且用句號(英文中的句號)和其它位元組間分開。

  • A類:地址範圍1.0.0.1 - 126.255.255.254 —-有126個網路
  • B類:地址範圍128.1.0.1 - 191.255.255.254 —-有16384個網路
  • C類:地址範圍192.0.1.1-233.255.255.254 —-有2097152個網路
  • D類:用於多點廣播
  • 私有IP:10.0.0.0-10.255.255.255 , 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255(注意:IP地址127.0.0.1-127.255.255.255用於迴路測試)

3. OSI模型與IP/TCP協議模型

  • OSI模型
    開放式系統互聯通訊參考模型(Open System Interconnection Reference Model)簡稱OSI模型,一種概念模型,由國際標準化組織(ISO)提出,一個試圖使各種計算機在世界範圍內互連為網路的標準框架。

ISO模型

功能
7.應用層 application layer 網路程序到應用程式。針對特定應用規定各層協議、時序、表示等,進行封裝 。在端系統中用軟體來實現,如HTTP,HTTPS,FTP,SSH,SMTP,POP3等
6.表示層 presentation layer 資料表示形式,加密和解密,把機器相關的資料轉換成獨立與機器的資料。規定資料的格式化表示,資料格式的轉換等
5.會話層 session layer 主機間通訊,管理應用程式之間的會話。規定通訊時序;資料交換的定界、同步,建立檢查點等。
4.傳輸層 transport layer 在網路的各個節點之間可靠地分發資料包。所有傳輸遺留問題;複用;流量;可靠。
3.網路層 network layer 在網路的各個節點之間進行地址分配、路由和(不一定可靠的)分發報文。路由(IP定址);擁塞控制。
2.資料鏈路層 data link layer 一個可靠的點對點資料直鏈。檢錯與糾錯(CRC碼);多路訪問;定址。
1.物理層 physical layer 一個(不一定可靠的)點對點資料直鏈。定義機械特性;電氣特性;功能特性;過程特性
  • TCP/IP協議
    網際網路協議族(Internet Protocol Suits,縮寫IPS)是一個網路通訊模型,以及一整個網路傳輸協議家族,為網際網路的基礎通訊架構。它常被通稱為TCP/IP協議族(TCP/IP Protocol Suite,或TCP/IP Protocols,)簡稱TCP/IP。因為該協議家族的兩個核心協議:TCP(傳輸控制協議)和IP(網際協議)

TCP/IP協議
這裡寫圖片描述

協議
4.應用層 application layer HTTP、FTP、DNS
3.傳輸層 transport layer TCP、UDP、RIP、SCTP
2.網路互聯層 internet layer IP
1.網路介面層 link layer 乙太網、Wi-Fi、MPLS

4. Socket(套接字)

4.1 Python提供了兩個級別訪問的網路服務

  • 低階的網路服務支援基本的Socket,它提供了標準的BSD Sockets API,可以訪問底層作業系統Socket介面的全部方法。
  • 高級別的網路服務模組SocketServer , 它提供可伺服器中心類,可以簡化伺服器的開發。

4.2 什麼是Socket?

  • Socket 是對TCP/IP協議族的一種封裝,是應用層與TCP/IP協議族通訊的中間軟體抽象層。從設計模式的角度看來,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓socket去組織資料,以符合指定的協議。
  • Socket 還可以認為是一種網路間不同計算機上的程序通訊的一種方式,利用三元組(IP地址,協議,埠)就可以唯一標識網路中的程序,網路中的程序通訊可以利用這個標識與其它程序進行互動。
  • Socket起源於Unix,Unix/Linux 基本哲學之一就是“一切皆檔案”,都可以用”開啟(open)–>讀寫(write/read)–>關閉(close)”模式來進行操作。因此Socket也被處理為一種特殊的檔案。

4.3 建立socket

  • 在python中使用socket模組的函式socket就可以完成:
import socket
socket.socket(AddressFamily,Type)

說明:函式socket.socket建立一個socket,該函式帶有兩個引數
Address Family:可以選擇AF_INET(用於Internet程序間通訊)或者AF_UNIX(用於同一臺機器程序間通訊),實際工作中常用AF_INET
Type:套接字型別,可以是SOCK_STREAM(流式套接字,主要用於TCP協議)或者SOCK_DGRAM(資料報套接字,主要用於UDP協議)

  • 建立一個TCP socket(tcp套接字)
import socket
# 建立tcp的套接字
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# ...這裡是使用套接字的功能

# 不用的時候,關閉套接字
  • 建立一個UDP socket(UDP套接字)
import socket

# 建立udp的套接字
s= socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

# ...這裡是使用套接字的功能(省略...)

# 不用的時候,關閉套接字

s.close()

4.4 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.recv() 接收TCP資料,資料以字串形式返回,bufsize指定要接收的最大資料量。flag提供有關訊息的其他資訊,通常可以忽略。
s.send() 傳送TCP 資料,將string中的資料傳送到連線的套接字。返回值是要傳送的位元組數量,該數量可能小於string的位元組大小。
s.sendall() 完整發送TCP資料,完整發送TCP資料。將string中的資料傳送到連線的套接字,但在返回之前會嘗試傳送所有的資料。成功返回None,失敗則丟擲異常。
s.recvfrom() 接收UDP資料,與recv()類似,但返回值是(data,address)。其中data是包含接收資料的字串,address是傳送資料的套接字地址。
s.sendto() 傳送UDP資料,將資料傳送到套接字,address是形式為(ipaddr,port)的元組,指定遠端地址。返回值是傳送的位元組數。
s.close() 關閉套接字
s.getpeername() 返回連線套接字的遠端地址。返回值通常是元組(ipaddr,port).
s.getsockname() 返回套接字自己的地址。通常是一個元組(ipaddr,port)
s.settimeout(timeout) 設定套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛建立套接字時設定,因為它們可能用於連線的操作(如connet())
s.gettimeout() 返回當前超時期的值,單位是秒,如果沒有設定超時期,則返回None.
s.fileno() 返回套接字的檔案描述符
s.setblocking(flag) 如果flag為0,則將套接字設為非阻塞模式,否則將套接字設為阻塞模式(預設值)。非阻塞模式下,如果呼叫recv()沒有發現任何資料,或send()呼叫無法立即傳送資料,那麼將引起socket.error異常。
s.makefile() 建立一個與該套接字相關連的檔案

5. 網路-UDP

  • 建立一個udp客戶端程式的流程:
    1. 建立客戶端套接字
    2. 傳送/接收資料
    3. 關閉套接字

這裡寫圖片描述

程式碼如下:

from socket import *

# 1. 建立udp 套接字
udp_socket = socket(AF_INET,SOCK_DGRAM)

# 2. 準備接收方的地址
#  192.168.1.100 表示目的IP地址 8080表示目的埠
dest_addr = ("192,.168.1.103" , 8080) # 注意是元組,ip是字串,埠是數字

# 3.從鍵盤獲得資料
send_data = input("請輸入要傳送的資料:")

# 4.傳送資料到指定的電腦上的指定程式中
udp_socket.sendto(send_data.encode("utf-8"), dest_addr)

# 5.關閉套接字
udp_socket.close()
  • udp 網路程式-傳送、接收資料
from socket import *

# 1. 建立udp 套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)

# 2. 準備接收方的地址
# 192.168.1.100 表示 目的地址  8080表示目的埠
dest_addr = ("192,168.1.100",8080)

# 3. 從鍵盤獲取資料
send_data = input("請輸入要傳送的資料:")

# 4.傳送資料到指定的電腦上
udp_socket.sendto(send_data.encode("utf-8"), dest_addr)

# 5.等待接收對方傳送的資料
recv_data = udp_socket.recvfrom(1024) # 2014 表示本次接收的最大位元組數

# 6.顯示對方傳送的資料
# 接收到的資料recv_data 是一個元組
# 第1個元素是對方傳送的資料
# 第2個元素是對方的IP和埠
print(recv_data[0].decode("gbk"))
print(recv_data[1])

# 7.關閉套接字
udp_socker.close()
  • python3–程式設計轉換
str  ----> bytes:encode 編碼
# 說明 字串通過編碼成為位元組碼

bytes----> str:decode  解碼
# 說明位元組碼通過解碼成為字串

# 其中decode()與encode()方法可以接受引數,其宣告分別為:
bytes.decode(encoding="utf-8",errors="strict")
str.encode(encoding="utf-8",errors="strict")# errors是指錯誤的處理方案。
  • udp繫結資訊

# 繫結本地的相關資訊,如果一個網路程式不繫結,則系統會隨機分配
local_addr = ("",7788# ip地址和埠號,ip一般不用寫,表示本機的任何一個IP
udp_socker.bind(loacl_addr)
  • 總結:

    • 一個udp網路程式,可以不繫結,此時作業系統會隨機進行分配一個埠,如果重新執行此程式埠可能會發生變化。
    • 一個udp網路程式,也可以繫結資訊(ip地址,埠號),如果繫結成功,那麼作業系統用這個埠號來進行區別收到的網路資料是否是此程序。

6、網路-TCP

  • 定義:TCP協議,傳輸控制協議是一種面向連線、可靠、基於位元組流的傳輸層通訊協議。
  • 步驟:TCP通訊需要經過建立連線、資料傳送、終止連線三個步驟
  • TCP特點:

    • 面向連線:雙方必須先建立連線才能進行資料的傳輸,雙方都必須為該連線分配必要的系統核心資源,以管理連線的狀態和連線上的傳輸。
    • TCP採用傳送應答機制:傳送的每個報文段都必須得到接收方的應答才認為這個TCP報文段傳輸成功。
    • 超時重傳:在定時時間內沒有收到應答就重新發送這個報文段。
    • 錯誤校驗:tcp用一個校驗和函式來檢驗資料是否有錯誤,在傳送和接收時都要計算校驗和。
    • 流量控制和阻塞管理:流量控制用來避免主機發送得過快而使接收方來不及完全收下。
  • TCP與UDP的不同點

    • 面向連線(確定有建立三方交握,連線已建立才作傳輸)
    • 有序資料傳輸
    • 重發丟失的資料包
    • 捨棄重複的資料包
    • 無差錯的資料傳輸
    • 阻塞/流量控制
  • TCP模型
    這裡寫圖片描述

  • TCP客戶端構建流程

    • 建立socket
    • 輸入目的資訊
    • 連結伺服器
    • 接收對方資料
    • 關閉套接字

程式碼:

from socket import *

# 建立socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)

# 目的資訊
sever_ip = input("請輸入伺服器IP:")
sever_port = int (input("請輸入伺服器port:"))

# 連結伺服器
tcp_client_socket.connect((server_ip, server_port)) # 元組

# 提示使用者輸入資料
send_data = input("請輸入要傳送的資料:")
tcp_client_socket.send(send_data.encode("gbk"))

# 接收對方傳送過來的資料,最大接收1024個位元組
recvData = tcp_client_socket.recv(1024)
print("接收到的資料為:", recvData.decode("gbk"))

# 關閉套接字

tcp_client_socket.close()

執行結果:
請輸入伺服器ip:10.10.0.47
請輸入伺服器port:8080
請輸入要傳送的資料:你好啊
接收到的資料為: 我很好,你呢

  • TCP伺服器
    • socket建立一個套接字
    • bind 繫結ip和port
    • listen 使套接字變為可以被動連結
    • accept等待客戶端的連結
    • recv/send接收發送資料

程式碼:

from socket import *

# 建立socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)

# 本地資訊
address = ('', 7788)

# 繫結
tcp_server_socket.bind(address)

# 使用socket建立的套接字預設的屬性是主動的,使用listen將其變為被動的,這樣就可以接收別人的連結了
tcp_server_socket.listen(128)

# 如果有新的客戶端來連結伺服器,那麼就產生一個新的套接字專門為這個客戶端服務
# client_socket用來為這個客戶端服務
# tcp_server_socket就可以省下來專門等待其他新客戶端的連結
client_socket, clientAddr = tcp_server_socket.accept()

# 接收對方傳送過來的資料
recv_data = client_socket.recv(1024)  # 接收1024個位元組
print('接收到的資料為:', recv_data.decode('gbk'))

# 傳送一些資料到客戶端
client_socket.send("thank you !".encode('gbk'))

# 關閉為這個客戶端服務的套接字,只要關閉了,就意味著為不能再為這個客戶端服務了,如果還需要服務,只能再次重新連線
client_socket.close()
  • tcp注意點
    • tcp伺服器一般情況下都需要繫結,否則客戶端找不到這個伺服器
    • tcp客戶端一般不繫結,因為是主動連結伺服器,所以只要確定好伺服器的ip、port等資訊就好,本地客戶端可以隨機
    • tcp伺服器中通過listen可以將socket創建出來的主動套接字變為被動的,這是做tcp伺服器時必須要做的
    • 當客戶端需要連結伺服器時,就需要使用connect進行連結,udp是不需要連結的而是直接傳送,但是tcp必須先連結,只有連結成功才能通訊
    • 當一個tcp客戶端連線伺服器時,伺服器端會有1個新的套接字,這個套接字用來標記這個客戶端,單獨為這個客戶端服務
    • listen後的套接字是被動套接字,用來接收新的客戶端的連結請求的,而accept返回的新套接字是標記這個新客戶端的
    • 關閉listen後的套接字意味著被動套接字關閉了,會導致新的客戶端不能夠連結伺服器,但是之前已經連結成功的客戶端正常通訊。
    • 關閉accept返回的套接字意味著這個客戶端已經服務完畢
    • 當客戶端的套接字呼叫close後,伺服器端會recv解堵塞,並且返回的長度為0,因此伺服器可以通過返回資料的長度來區別客戶端是否已經下線