python 的網絡編程模塊主要支持兩種Internet協議: TCP 和 UDP.html
通訊協議也叫網絡傳輸協議或簡稱爲傳送協議(Communications Protocol),是指計算機通訊或網絡設備的共同語言。python
如今最普及的計算機通訊爲網絡通訊,因此「傳送協議」通常都指計算機通訊的傳送協議,如:TCP/IP、NetBEUI、HTTP、FTP等。web
然而,傳送協議也存在於計算機的其餘形式通訊,例如:面向對象編程裏面對象之間的通訊;操做系統內不一樣程序之間的消息,都須要有一個傳送協議,以確保傳信雙方可以溝通無間。編程
TCP/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等應用協議。
互聯網協議地址(英語:Internet Protocol Address,又譯爲網際協議地址),縮寫爲IP地址(英語:IP Address)
IP 地址是分配給網絡上使用網際協議(英語:Internet Protocol, IP)的設備的數字標籤。常見的IP地址分爲IPv4與IPv6兩大類。
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地址爲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。
在網絡技術中,端口(Port)包括邏輯端口和物理端口兩種類型。
物理端口指的是物理存在的端口,如ADSL Modem、集線器、交換機、路由器上用 於鏈接其餘網絡設備的接口,如RJ-45端口、SC端口等等。
邏輯端口是指邏輯意義上用於區分服務的端口,如TCP/IP協議中的服務端口,端口號的範圍從0到65535,好比用於瀏覽網頁服務的80端口,用於FTP服務的21端口等。因爲物理端口和邏輯端口數量較多,爲了對端口進行區分,將每一個端口進行了編號,這就是端口號。
咱們主要研究的是邏輯端口號.咱們平時所說的端口號也是指的邏輯端口號
端口是一個軟件結構,被客戶程序或服務程序用來發送和接收數據,一臺服務器有 256*256個端口。 端口號範圍: 0 - 65535
0-1023是公認端口號,即已經公認定義或爲將要公認定義的軟件保留的
1024-65535是並無公共定義的端口號,用戶能夠本身定義這些端口的做用。
端口與協議有關:TCP和UDP的端口互不相干
什麼是 TCP 協議
TCP(Transmission Control Protocol 傳輸控制協議)是一種面向鏈接(鏈接導向)的、可靠的、基於IP的傳輸層協議。
彌補了IP協議的不足,屬於一種較高級的協議,它實現了數據包的有力捆綁,經過排序和重傳來確保數據傳輸的可靠(即數據的準確傳輸以及完整性)。
排序能夠保證數據的讀取是按照正確的格式進行,重傳則保證了數據可以準確傳送到目的地!
使用 TCP 協議通訊是, 首先建立 TCP 鏈接, 主動發起鏈接的叫客戶端, 被動響應鏈接的叫服務器
好比:
當咱們在瀏覽器中訪問新浪主頁時,咱們本身的計算機就是客戶端,瀏覽器會主動向新浪的服務器發起鏈接。若是一切順利,新浪的服務器接受了咱們的鏈接,一個TCP鏈接就創建起來的,後面的通訊就是發送網頁內容了。
Socket
Socket
又稱"套接字",應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間能夠通信。
能夠把Socket
理解成相似插座的東西, 經過Socket
就能夠發送和接受數據了, 就像插座插上電器以後就能夠向外提供電能了.
TCP
編程的客戶端和服務器端都是經過Socket
來完成的.
其實UDP
協議通訊也是使用的套接字, 和TCP
協議稍有差異. TCP
是面向鏈接的套接字, 而UDP
是面向無鏈接的套接字.
套接字的起源能夠追溯到20世紀70年末, 他是加利福尼亞大學的伯克利版本 Unix(也成 BSD Unix) 的一部分. 所以, 有時你可能會聽過將套接字稱爲伯克利套接字或 BSD 套接字.
套接字最初是爲同一主機上的應用程序鎖建立, 使得主機上一個程序(也叫一個進程)與另外一個容許的程序進行通訊. 這就是所謂的進程間通訊(Inter Process Communication IPC)
有兩種類型的套接字: 基於文件的和麪向對象的.
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_family
是AF_UNIX 或 AF_INET
, Socket_type
若是是TCP
編程是SOCKET_STREAM
, 若是是 UDP 編程則使用SOCKET_DGRAM
. protocal
一般省略, 默認是0
Socket
對象. Socket
對象提供了一些方法來讓咱們操做這些套接字.客戶端代碼參考下面的代碼:
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()
服務器代碼參考下面的代碼:
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() # 關閉服務器
執行 TCP 服務器和客戶端
如今開始運行服務器和客戶端程序, 看看他們是如何工做的.
那麼應該先運行客戶端仍是服務器呢?
固然是先運行服務器, 讓服務器先處於等等客戶端接入的狀態, 這樣在客戶端申請接入的時候纔不會出錯.
其實, 服務器是被動端, 客戶端是主動端.
UDP
簡介
UDP
也叫用戶數據報協議
UDP
編程相比TCP
編程簡單了不少.
由於UDP
不是面向鏈接的, 而是面向無鏈接的.
TCP
是面向鏈接的, 客戶端和服務端必須鏈接以後才能通信, 就像打電話, 必須先接通才能通話.
UDP
是面向無鏈接的, 一方負責發送數據(客戶端), 只要知道對方(接受數據:服務器) 的地址就能夠直接發數據了, 可是能不能達到就沒有辦法保證了.
雖然用UDP傳輸面向無鏈接, 數據不可靠,但它的優勢是和TCP比,速度快,對於不要求可靠到達的數據,就可使用UDP協議。 好比局域網的視頻同步, 使用 udp 是比較合適的:快, 延遲越小越好
建立UDP
的Socket
對象
建立方式和TCP
的Socket
同樣的, 只是須要把socket_tpye
的值設置爲SOCKET_DGRAM
socket(AF_INET, SOCKET_DGRAM)
參考下面的代碼:
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()
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()
仍然須要先執行服務器再執行客戶端.
在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 地址列表 |
TCP
和UDP
是比較低級的協議, 是底層網絡通信協議, 是當今因特網中大部分客戶端/服務器協議的核心.
大部分狀況咱們並不會直接使用TCP, UDP
去編程, 而是使用更加高級的協議去編程.
好比 HTTP(超文本傳輸協議), FTP(文件傳輸協議)等.
本節內容主要學習使用 HTTP 去訪問互聯網中的內容.
因此咱們先從 HTTP 協議開始講起, 他是目前互聯網上應用最普遍的通訊協議.
HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議。
HTTP 是基於 TCP/IP 協議的應用層協議。它不涉及數據包(packet)傳輸,主要規定了客戶端和服務器之間的通訊格式,默認使用80端口。
HTTP協議工做於客戶端-服務端架構爲上, 是一種請求應答式的.
瀏覽器(或其餘客戶端)做爲HTTP客戶端經過URL向HTTP服務端即WEB服務器發送全部請求。Web服務器根據接收到的請求後,向客戶端發送響應信息。
通訊規則規定了客戶端發送給服務器的內容格式,也規定了服務器發送給客戶端的內容格式。
客戶端發送給服務器的格式叫「請求協議」;
服務器發送給客戶端的格式叫「響應協議」。
重點學習這兩個格式。
請求行 例:GET /images/logo.gif HTTP/1.1,表示從/images目錄下請求logo.gif文件。 請求頭 例:Accept-Language: en(不少請求頭) 空行 必須的,服務經過這個空行來區別出請求頭和請求體 請求體 有時候也叫消息體,是可選的,get請求時無請求體,post請求會有。
瀏覽器向服務器發送請求時必須依據該格式,不然服務器沒法識別。http協議中的請求行中能夠有8種請求方法,可是目前爲止,通用和你們都在用的只有兩種:post請求和get請求。
狀態行; 響應頭信息; 空行; 響應體(響應正文)。
注意區別就是請求數據的傳送方式:
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 中。 |
前面咱們用瀏覽器使用 HTTP 協議去訪問網絡。 可是有一點你們須要記住, 瀏覽器只是 web 客戶端的一種。
python 提供的 urllib 模塊, 使用它, 就能夠編寫能夠下載或或者訪問互聯網上信息的簡單 web 客戶端。
你首先須要作的就是爲程序提供一個有效的 web 網址, 這個 web 網站就是一個URL
。
咱們先了解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 | 可選, 指定文檔內特色錨的部分 |
urllib
是一個package
, 這個package
包含了幾個模塊, 這幾個模塊都是使用url
來工做.
urllib.request
模塊, 用於打開和讀取url
urllib.error
模塊, 包含了urllib.request
拋出的一些異常.urllib.parse
模塊, 解析url
urllib.robotparser
模塊, 解析robots.txt
文件urllib
包在python2中的模塊urllib, urlparse, urllib2
, 以及其餘內容都整合在了urllib
單一包中.
urllib和urllib2
的內容整合在了urllib.request
模塊中urlparse
的內容整合在了urllib.parse
模塊中urllib
包還包括其餘模塊如:response, error, robotparse
, 後面再學習.python支持兩種不一樣的模塊來處理url, 一個是parse
, 另外一個是request
.
兩種模塊的功能不同, 下面會分別介紹兩個模塊.
parse
模塊parse
是包urllib
下的模塊, 只是用來處理url
這個字符串自己, 而不負責使用這個url
去聯網獲取資源.
parse
主要提供了三個功能: urlparse(), urlunparse(), urljoin()
parse.urlparse()
urlparse()
用來將url
字符串解析成咱們前面說的那些組件.
語法:
urlparse(urlstr, defProSch='', allowFrag=True)
**說明:
**
url
字符串. 返回值一個ParseResult
類型的數據defProSch
默認網絡協議.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='')
parse.urlunparse()
是把各個部分組合成url
字符串.
from urllib.parse import * o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html') urlstr = urlunparse(o) print(urlstr)
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)
urllib.request
模塊提供了許多函數, 可用於從指定URL加載數據, 同時也能夠對字符串進行編碼解碼工做, 以便再URL中以正確的形式顯示出來.
request.urlopen()
urlopen(url, data=None[, timeout])
說明:
GET
請求, 向服務器發送的請求參數應該是url
的一部分. 注意使用到的參數應該是已經通過url編碼的(使用parse.urlencode()
編碼).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"))