Python第十三章-網絡編程

網絡編程

1、網絡編程基礎

python 的網絡編程模塊主要支持兩種Internet協議: TCP 和 UDP.html

1.1通訊協議

通訊協議也叫網絡傳輸協議或簡稱爲傳送協議(Communications Protocol),是指計算機通訊或網絡設備的共同語言。python

如今最普及的計算機通訊爲網絡通訊,因此「傳送協議」通常都指計算機通訊的傳送協議,如:TCP/IP、NetBEUI、HTTP、FTP等。web

然而,傳送協議也存在於計算機的其餘形式通訊,例如:面向對象編程裏面對象之間的通訊;操做系統內不一樣程序之間的消息,都須要有一個傳送協議,以確保傳信雙方可以溝通無間。編程


1.2TCP/IP協議

在Internet中TCP/IP協議是使用最爲普遍的通信協議(互聯網上的一種事實的標準)。TCP/IP是英文Transmission Control Protocol/Internet Protocol的縮寫,意思是「傳輸控制協議/網際協議」數組

TCP/IP 協議是一個工業標準協議套件,專爲跨廣域網(WAN)的大型互聯網絡而設計。瀏覽器

TCP/IP 網絡體系結構模型就是遵循TCP/IP 協議進行通訊的一種分層體系,現今,Internet和Intranet所使用的協議通常都爲TCP/IP 協議。緩存

在瞭解該協議以前,咱們必須掌握基於該協議的體系結構層次,而TCP/IP體系結構分爲四層。安全

第 1 層 網絡接口層
包括用於協做IP數據在已有網絡介質上傳輸的協議,提供TCP/IP協議的數據結構和實際物理硬件之間的接口。好比地址解析協議(Address Resolution Protocol, ARP )等。服務器

第 2 層 網絡層
對應於OSI模型的網絡層,主要包含了IP、RIP等相關協議,負責數據的打包、尋址及路由。還包括網間控制報文協議(ICMP)來提供網絡診斷信息。網絡

第 3 層 傳輸層
對應於OSI的傳輸層,提供了兩種端到端的通訊服務,分別是TCP和UDP協議。

第 4 層 應用層
對應於OSI的應用層、表達層和會話層,提供了網絡與應用之間的對話接口。包含了各類網絡應用層協議,好比Http、FTP等應用協議。


附錄:OSI 七層參考模型


1.3 IP 地址和端口號

1.3.1 IP 地址

互聯網協議地址(英語:Internet Protocol Address,又譯爲網際協議地址),縮寫爲IP地址(英語:IP Address)

IP 地址是分配給網絡上使用網際協議(英語:Internet Protocol, IP)的設備的數字標籤。常見的IP地址分爲IPv4與IPv6兩大類。


IPV4地址

IP地址由32位二進制數組成,爲便於使用,常以XXX.XXX.XXX.XXX形式表現,每組XXX表明小於或等於255的10進制數。例如維基媒體的一個IP地址是208.80.152.2。

地址可分爲A、B、C、D、E五大類,其中E類屬於特殊保留地址。
IP地址是惟一的。目前IP技術可能使用的IP地址最多可有4,294,967,296個(即232)。驟看可能以爲很難會用盡,但因爲早期編碼和分配上的問題,使不少區域的編碼實際上被空出或不能使用。加上互聯網的普及,使大部分家庭都至少有一部電腦,連同公司的電腦,以及鏈接網絡的各類設備都消耗了大量IPv4地址資源。

隨着互聯網的快速成長,IPv4的42億個地址的分配最終於2011年2月3日用盡[1][2]。相應的科研組織已研究出128位的IPv6,其IP地址數量最高可達3.402823669 × 1038個,屆時每一個人家居中的每件電器,每件對象,甚至地球上每一粒沙子均可以擁有本身的IP地址。

在A類、B類、C類IP地址中,若是主機號是全1,那麼這個地址爲直接廣播地址,它是用來使路由器將一個分組以廣播形式發送給特定網絡上的全部主機。32位全爲1的IP地址「255.255.255.255」爲受限廣播地址("limited broadcast" destination address),用來將一個分組以廣播方式發送給本網絡中的全部主機,路由器則阻擋該分組經過,將其廣播功能限制在本網內部。


IPV6地址

IPv6地址爲128位長但一般寫做8組每組四個十六進制數的形式。例如:
2001:0db8:85a3:08d3:1319:8a2e:0370:7344
是一個合法的IPv6地址。

IPv4地址能夠很容易的轉化爲IPv6格式。舉例來講,若是IPv4的一個地址爲135.75.43.52(十六進制爲0x874B2B34),它能夠被轉化爲0000:0000:0000:0000:0000:0000:874B:2B34或者::874B:2B34。同時,還可使用混合符號(IPv4-compatible address),則地址能夠爲::135.75.43.52。


1.3.2端口

在網絡技術中,端口(Port)包括邏輯端口和物理端口兩種類型。

物理端口指的是物理存在的端口,如ADSL Modem、集線器、交換機、路由器上用 於鏈接其餘網絡設備的接口,如RJ-45端口、SC端口等等。

邏輯端口是指邏輯意義上用於區分服務的端口,如TCP/IP協議中的服務端口,端口號的範圍從0到65535,好比用於瀏覽網頁服務的80端口,用於FTP服務的21端口等。因爲物理端口和邏輯端口數量較多,爲了對端口進行區分,將每一個端口進行了編號,這就是端口號。

咱們主要研究的是邏輯端口號.咱們平時所說的端口號也是指的邏輯端口號

端口是一個軟件結構,被客戶程序或服務程序用來發送和接收數據,一臺服務器有 256*256個端口。 端口號範圍: 0 - 65535

0-1023是公認端口號,即已經公認定義或爲將要公認定義的軟件保留的

1024-65535是並無公共定義的端口號,用戶能夠本身定義這些端口的做用。

端口與協議有關:TCP和UDP的端口互不相干

2、TCP編程

什麼是 TCP 協議

TCP(Transmission Control Protocol 傳輸控制協議)是一種面向鏈接(鏈接導向)的、可靠的、基於IP的傳輸層協議。

彌補了IP協議的不足,屬於一種較高級的協議,它實現了數據包的有力捆綁,經過排序和重傳來確保數據傳輸的可靠(即數據的準確傳輸以及完整性)。

排序能夠保證數據的讀取是按照正確的格式進行,重傳則保證了數據可以準確傳送到目的地!

使用 TCP 協議通訊是, 首先建立 TCP 鏈接, 主動發起鏈接的叫客戶端, 被動響應鏈接的叫服務器

好比:
當咱們在瀏覽器中訪問新浪主頁時,咱們本身的計算機就是客戶端,瀏覽器會主動向新浪的服務器發起鏈接。若是一切順利,新浪的服務器接受了咱們的鏈接,一個TCP鏈接就創建起來的,後面的通訊就是發送網頁內容了。

2.1什麼是Socket

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

能夠把Socket理解成相似插座的東西, 經過Socket就能夠發送和接受數據了, 就像插座插上電器以後就能夠向外提供電能了.

TCP編程的客戶端和服務器端都是經過Socket來完成的.

其實UDP協議通訊也是使用的套接字, 和TCP協議稍有差異. TCP是面向鏈接的套接字, 而UDP是面向無鏈接的套接字.


套接字的起源能夠追溯到20世紀70年末, 他是加利福尼亞大學的伯克利版本 Unix(也成 BSD Unix) 的一部分. 所以, 有時你可能會聽過將套接字稱爲伯克利套接字或 BSD 套接字.

套接字最初是爲同一主機上的應用程序鎖建立, 使得主機上一個程序(也叫一個進程)與另外一個容許的程序進行通訊. 這就是所謂的進程間通訊(Inter Process Communication IPC)


Socket families

有兩種類型的套接字: 基於文件的和麪向對象的.


基於文件的套接字:AF_UNIX

AF_UNIX是基於文件的套接字.

由於兩個進程容許在同一臺計算機上, 因此這些套接字都是基於文件的, 這意味着文件系統支持他們的底層基礎結構. 這是可以說的通的, 由於文件系統是一個運行在同一主機上的多個進程之間的共享常量.

AF_UNIX在編程的時候並非太經常使用.


基於網絡的套接字:AF_INET

AF_INET用於基於網絡的Socket. 還有一個地址家族AF_INET6, 用於IPv6.

其實還有一些其餘地址家族, 哪些要麼是專業的, 過期的, 不多使用的, 要麼是仍未實現的.

在全部的地址家族中, AF_INET是使用最普遍的.

由於本章重點討論網絡編程, 因此本章剩餘的內容中, 都是將使用AF_INET


socket模塊

要建立套接字, 必須使用socket模塊下的socket()函數.

他的通常語法以下:

import socket


socket.socket(socket_family, socket_type, protocal=0)

說明:

1, 其中, socket_familyAF_UNIX 或 AF_INET, Socket_type若是是TCP編程是SOCKET_STREAM, 若是是 UDP 編程則使用SOCKET_DGRAM. protocal一般省略, 默認是0

  1. 返回值就是Socket對象. Socket對象提供了一些方法來讓咱們操做這些套接字.

2.2 TCP 客戶端編程

客戶端代碼參考下面的代碼:

from socket import *

host = "localhost"  # 客戶端準備鏈接的服務器的地址
port = 10000  # 服務器的端口號
address = (host, port)  # 服務器的地址

bufSize = 1024  # 客戶端緩衝區的大小(單位字節)

tcpCliSock = socket(AF_INET, SOCK_STREAM)  # 全部的套接字都使用 socket 函數來建立
tcpCliSock.connect(address)  # 客戶端去鏈接服務器

while True:
    data = input("> ")  # 從鍵盤讀取數據
    if not data:
        break
    # 給服務器發送消息. 因爲 send 只能發送字節數據,因此把字符串編碼以後再發送
    tcpCliSock.send(data.encode("utf-8"))
    
    data = tcpCliSock.recv(bufSize)  # 接受服務器發送來的信息
    if not data:
        break
    # 因爲經過網絡傳遞過來的實際上是字節數據, 解碼以後再輸出
    print(data.decode("utf-8"))

tcpCliSock.close()

2.3 TCP 服務器編程

服務器代碼參考下面的代碼:

from socket import *

host = "localhost"  # 服務器要綁定的主機地址
port = 10000  # 服務器要監聽的端口號
address = (host, port)
bufSize = 1024  # 設置服務器的緩衝區大小
tcpSevSock = socket(AF_INET, SOCK_STREAM)  # 建立 socket 對象
tcpSevSock.bind(address)  # 把 socket 綁定到指定的地址和端口
tcpSevSock.listen()  # 開啓服務器監聽器

print("正在等等客戶端鏈接...")
tcpCliSock, cliAddress = tcpSevSock.accept()  # 接受客戶端的鏈接
print("來自:", cliAddress, "的鏈接...")

while True:
    data = tcpCliSock.recv(bufSize)  # 接受客戶端發來的數據
    if not data:
        break
    # 把接收到字節數據解碼
    data = data.decode("utf-8")
    # 向客戶端發送數據. 先把字符串編碼, 再發送
    tcpCliSock.send(("我是...服務器...你的信息是:" + data).encode("utf-8"))

tcpCliSock.close()  # 關閉客戶端
tcpSevSock.close()  # 關閉服務器

2.4 運行服務器和客戶端

執行 TCP 服務器和客戶端

如今開始運行服務器和客戶端程序, 看看他們是如何工做的.

那麼應該先運行客戶端仍是服務器呢?

固然是先運行服務器, 讓服務器先處於等等客戶端接入的狀態, 這樣在客戶端申請接入的時候纔不會出錯.

其實, 服務器是被動端, 客戶端是主動端.

3、UDP編程

UDP簡介

UDP也叫用戶數據報協議

UDP編程相比TCP編程簡單了不少.

由於UDP不是面向鏈接的, 而是面向無鏈接的.

TCP是面向鏈接的, 客戶端和服務端必須鏈接以後才能通信, 就像打電話, 必須先接通才能通話.

UDP是面向無鏈接的, 一方負責發送數據(客戶端), 只要知道對方(接受數據:服務器) 的地址就能夠直接發數據了, 可是能不能達到就沒有辦法保證了.

雖然用UDP傳輸面向無鏈接, 數據不可靠,但它的優勢是和TCP比,速度快,對於不要求可靠到達的數據,就可使用UDP協議。 好比局域網的視頻同步, 使用 udp 是比較合適的:快, 延遲越小越好

建立UDPSocket對象

建立方式和TCPSocket同樣的, 只是須要把socket_tpye的值設置爲SOCKET_DGRAM

socket(AF_INET, SOCKET_DGRAM)

3.1 UDP客戶端編程

參考下面的代碼:

from socket import *

host = "localhost"  # 對方地址
port = 20000  # 對方端口
address = (host, port)

bufSize = 1024

udpCliSock = socket(AF_INET, SOCK_DGRAM)

while True:
    data = input("> ")
    if not data:
        break
    # 把數據發送到指定的 udp 服務器
    udpCliSock.sendto(data.encode("utf-8"), address)
    
udpCliSock.close()

3.2 UDP服務器編程

UDP服務器須要作的事情比較少, 除了等待傳入的鏈接以外, 幾乎不須要作其餘工做.

參考下面的代碼:

from socket import *

host = "localhost"  # 服務器要綁定的地址
port = 20000  # 服務器要綁定的端口
address = (host, port)

bufSize = 1024

udpServeSock = socket(AF_INET, SOCK_DGRAM)
udpServeSock.bind(address)

while True:
    print("等待有人給我發信息:")
    data, cliAddress = udpServeSock.recvfrom(bufSize)
    print(cliAddress, "發來的信息是:", data.decode("utf-8"))

udpServeSock.close()

3.3 運行UDP服務器和客戶端

仍然須要先執行服務器再執行客戶端.

4、socket模塊其餘屬性和函數

socket模塊中, 除了目前熟悉的socket.socket()函數以外, 還提供了更多用於網絡應用開發的屬性.


屬性 描述
AF_UNIX, AF_INET, AF_INET6, AF_NETLINK, AF_TIPC python 中支持的套接字地址家族
SOCK_STREAM, SOCK_DGRAM 套接字類型(TCP=流, UDP=數據包)
has_ipv6 指示是否支持 IPv6的布爾標記

異常 描述
error 套接字相關錯誤
haserror 主機和地址相關錯誤
gaierror 地址相關錯誤
timeout 超時時間

函數 描述
socket() 建立套接字對象
getaddrinfor() 獲取一個五元組序列形式的地址信息
getnameinfo() 給定一個套接字地址, 返回二元組(主機名, 端口號)
getfqdn() 返回完整域名
gethostname() 返回當前主機名
gethostbyname() 將一個主機名, 映射到他的 ip 地址
gethostbyname_ex() gethostbyname()的擴展版本, 返回主機名, 別名主機集合和 ip 地址列表

5、python web 客戶端

TCPUDP是比較低級的協議, 是底層網絡通信協議, 是當今因特網中大部分客戶端/服務器協議的核心.

大部分狀況咱們並不會直接使用TCP, UDP去編程, 而是使用更加高級的協議去編程.

好比 HTTP(超文本傳輸協議), FTP(文件傳輸協議)等.

本節內容主要學習使用 HTTP 去訪問互聯網中的內容.

因此咱們先從 HTTP 協議開始講起, 他是目前互聯網上應用最普遍的通訊協議.

5.1 HTTP 協議簡介

5.1.1 什麼是 HTTP 協議

HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議。

HTTP 是基於 TCP/IP 協議的應用層協議。它不涉及數據包(packet)傳輸,主要規定了客戶端和服務器之間的通訊格式,默認使用80端口。


5.1.2 HTTP 協議發展簡史

  1. 最先版本是1991年發佈的0.9版。該版本極其簡單,只有一個命令GET。
  2. 1996年5月,HTTP/1.0 版本發佈,內容大大增長。
    任何格式的內容均可以發送。這使得互聯網不只能夠傳輸文字,還能傳輸圖像、視頻、二進制文件。這爲互聯網的大發展奠基了基礎。
  3. 1997年1月,HTTP/1.1 版本發佈,只比 1.0 版本晚了半年。它進一步完善了 HTTP 協議,一直用到了20年後的今天,直到如今仍是最流行的版本。

5.1.3 HTTP 協議工做原理

HTTP協議工做於客戶端-服務端架構爲上, 是一種請求應答式的.

瀏覽器(或其餘客戶端)做爲HTTP客戶端經過URL向HTTP服務端即WEB服務器發送全部請求。Web服務器根據接收到的請求後,向客戶端發送響應信息。


5.1.4 HTTP協議格式

通訊規則規定了客戶端發送給服務器的內容格式,也規定了服務器發送給客戶端的內容格式。

客戶端發送給服務器的格式叫「請求協議」;

服務器發送給客戶端的格式叫「響應協議」。

重點學習這兩個格式。


5.1.5 請求協議格式

請求行   例:GET /images/logo.gif HTTP/1.1,表示從/images目錄下請求logo.gif文件。
請求頭   例:Accept-Language: en(不少請求頭)
空行     必須的,服務經過這個空行來區別出請求頭和請求體
請求體   有時候也叫消息體,是可選的,get請求時無請求體,post請求會有。

瀏覽器向服務器發送請求時必須依據該格式,不然服務器沒法識別。http協議中的請求行中能夠有8種請求方法,可是目前爲止,通用和你們都在用的只有兩種:post請求和get請求。


5.1.6 響應協議格式

狀態行;
響應頭信息;
空行;
響應體(響應正文)。

5.1.7 GET 請求和 POST 請求的區別

注意區別就是請求數據的傳送方式:

1.GET 方法

查詢字符串(名稱/值對)是在 GET 請求的 URL 中發送的:

/test/demo_form.asp?name1=value1&name2=value2

2.POST 方法

請求數據(名稱/值對)是在 POST 請求的 HTTP 消息主體中發送的:

POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com

name1=value1&name2=value2

比較

項目 GET POST
後退按鈕/刷新 無害 數據會被從新提交(瀏覽器應該告知用戶數據會被從新提交)。
書籤 可收藏爲書籤 不可收藏爲書籤
緩存 能被緩存 不能緩存
編碼類型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multipart/form-data。爲二進制數據使用多重編碼。
歷史 參數保留在瀏覽器歷史中。 參數不會保存在瀏覽器歷史中。
對數據長度的限制 是的。當發送數據時,GET 方法向 URL 添加數據;URL 的長度是受限制的(URL 的最大長度是 2048 個字符)。 無限制。
對數據類型的限制 只容許 ASCII 字符。 沒有限制。也容許二進制數據。
安全性 與 POST 相比,GET 的安全性較差,由於所發送的數據是 URL 的一部分。在發送密碼或其餘敏感信息時毫不要使用 GET ! POST 比 GET 更安全,由於參數不會被保存在瀏覽器歷史或 web 服務器日誌中。
可見性 數據在 URL 中對全部人都是可見的。 數據不會顯示在 URL 中。

5.2 URL

前面咱們用瀏覽器使用 HTTP 協議去訪問網絡。 可是有一點你們須要記住, 瀏覽器只是 web 客戶端的一種。

  1. 能夠這麼說, 任何一個向 web 服務器發送求來得到應用程序的都是客戶端。
  2. 瀏覽器做爲一個比較特別的客戶端,主要用於瀏覽網頁內容並同其餘 web 站點交互。
  3. 而一個更普通的客戶端能夠完成更多的工做,不只能夠下載數據, 還能夠存儲、操做數據, 甚至能夠將其傳送到另一個地方或者傳給另一個應用。

python 提供的 urllib 模塊, 使用它, 就能夠編寫能夠下載或或者訪問互聯網上信息的簡單 web 客戶端。

你首先須要作的就是爲程序提供一個有效的 web 網址, 這個 web 網站就是一個URL

咱們先了解URL是什麼?

5.2.1 URL

URL 是Uniform Resource Locator的縮寫, 中文叫:統一資源定位符。

瀏覽網頁須要 URL, 這個 URL 就表示這個網頁的地址。 這個地址用來在 web 上定位定位一個文檔。

如街道地址同樣, URL 地址也有一些結構。URL 使用以下的這種格式:

prot_sch://net_loc/path;params?query#frag

URL組件 描述
pro_sch 網絡協議, 如:http, https
net_loc 服務器所在地
path 使用/分割的路徑
params 可選參數
? 可選, 表示後面是查詢參數
query 可選, 用鏈接符(&)分割的一系列鍵值對, 如: user=lisi&pwd=aaa
#frag 可選, 指定文檔內特色錨的部分

5.3 urllib包和parse模塊

urllib是一個package, 這個package包含了幾個模塊, 這幾個模塊都是使用url來工做.

  1. urllib.request模塊, 用於打開和讀取url
  2. urllib.error模塊, 包含了urllib.request拋出的一些異常.
  3. urllib.parse模塊, 解析url
  4. urllib.robotparser模塊, 解析robots.txt 文件

5.3.1 urllib

在python2中的模塊urllib, urlparse, urllib2, 以及其餘內容都整合在了urllib單一包中.

  1. urllib和urllib2的內容整合在了urllib.request模塊中
  2. urlparse的內容整合在了urllib.parse模塊中
  3. urllib包還包括其餘模塊如:response, error, robotparse, 後面再學習.

python支持兩種不一樣的模塊來處理url, 一個是parse, 另外一個是request.

兩種模塊的功能不同, 下面會分別介紹兩個模塊.

5.3.2 parse模塊

parse是包urllib下的模塊, 只是用來處理url這個字符串自己, 而不負責使用這個url去聯網獲取資源.

parse主要提供了三個功能: urlparse(), urlunparse(), urljoin()


1. parse.urlparse()

urlparse()用來將url字符串解析成咱們前面說的那些組件.

語法:

urlparse(urlstr, defProSch='', allowFrag=True)

**說明:

**

  1. 參數1就是url字符串. 返回值一個ParseResult類型的數據
  2. defProSch默認網絡協議.
  3. allowFrag 表示url中是否容許使用片斷.
from urllib.parse import *

o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html')
print(o)

結果:

ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', params='', query='', fragment='')

2. parse.urlunparse()

是把各個部分組合成url字符串.

from urllib.parse import *

o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html')
urlstr = urlunparse(o)
print(urlstr)
3. parse.urljoin()

urljoin()實現了url的鏈接功能.

urljoin(base, new_url, allow_fragments=True)

說明:

取得base的根路徑(不包括路徑中末端的文件), 而後與new_url鏈接起來.

from urllib.parse import *

newUrl = urljoin('http://www.cwi.nl/abc/Python.html', 'FAQ.html')
print(newUrl)

5.4 request模塊

urllib.request模塊提供了許多函數, 可用於從指定URL加載數據, 同時也能夠對字符串進行編碼解碼工做, 以便再URL中以正確的形式顯示出來.


request.urlopen()

urlopen(url, data=None[, timeout])

說明:

  1. 該函數, 打開指定的url並返回類文件對象, 可以使用該對象讀取返回的數據.
  2. 對全部HTTP請求, 最經常使用的是GET請求, 向服務器發送的請求參數應該是url的一部分. 注意使用到的參數應該是已經通過url編碼的(使用parse.urlencode()編碼).
  3. 若是是post請求, 請求的字符串(包括表單數據)應該放在第二個參數data中.
from urllib.request import *

with urlopen("http://www.yztcedu.com") as r:
    print(r)

urlopen()的返回值類文件對象

一旦鏈接成功, urlopen()會返還一個類文件對象, 就像在目標路徑下打開了一個可讀文件。

urlopne()類文件對象方法 描述
f.read([bytes]) 從文件中讀取全部或bytes個字節
f.readline() 從f中讀取一行
f.readlines() 從f中讀取全部行, 做爲列表返回
f.close() 關閉f的url鏈接
f.fileno() 返回f的文件句柄
f.info() 返回f的mime頭文件
f.geturl() 返回f的真正url
from urllib.request import *

with urlopen("http://www.yztcedu.com") as r:
    for line in r.readlines():
        print(line.decode("utf-8"))
相關文章
相關標籤/搜索