python高階程式設計(二)--網路程式設計
阿新 • • 發佈:2019-02-01
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客戶端程式的流程:
- 建立客戶端套接字
- 傳送/接收資料
- 關閉套接字
程式碼如下:
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,因此伺服器可以通過返回資料的長度來區別客戶端是否已經下線