python之網絡編程


一、前言

  我們現在已經學習了python基礎編程,我們知道在本機中,可以編寫兩個程序,通過讀寫同一主機磁盤中固定內存,可以實現兩個程序之間的通信,單機程序通信過程如下:

  而如果兩台主機之間要實現通信,就需要網絡編程。

  網絡通信就像寄信件,是信息與數據的交換,而在生活中我們寄信件,信件也不是從我們手里瞬間到收件人手里,每一次信件通信,都會經歷這樣幾個固定流程:寫信、裝信封、投到郵箱、郵局取件、運輸到目的地郵局、目的地郵局根據詳細地址派送、收件人收件、拆信封、讀信。

  網絡通信也是同樣的道理,數據的傳輸總有一定的流程:發送端程序將數據打包,給數據包印上目標地址,將數據包交給網關,通過路由轉發到達目的網絡,目的網絡網關在根據詳細地址分發、目的主機接收數據、拆包、讀數據。

  二.軟件開發的架構

我們了解的涉及到兩個程序之間通訊的應用大致可以分為兩種:

第一種是應用類:qq、微信、網盤、優酷這一類是屬於需要安裝的桌面應用

第二種是web類:比如百度、知乎、博客園等使用瀏覽器訪問就可以直接使用的應用

這些應用的本質其實都是兩個程序之間的通訊。而這兩個分類又對應了兩個軟件開發的架構~

1.C/S架構

C/S即:Client與Server ,中文意思:客戶端與服務器端架構,這種架構也是從用戶層面(也可以是物理層面)來划分的。

這里的客戶端一般泛指客戶端應用程序EXE,程序需要先安裝后,才能運行在用戶的電腦上,對用戶的電腦操作系統環境依賴較大。

 

2.B/S架構

B/S即:Browser與Server,中文意思:瀏覽器端與服務器端架構,這種架構是從用戶層面來划分的。

Browser瀏覽器,其實也是一種Client客戶端,只是這個客戶端不需要大家去安裝什么應用程序,只需在瀏覽器上通過HTTP請求服務器端相關的資源(網頁資源),客戶端Browser瀏覽器就能進行增刪改查。

 

 三、網絡編程理論基礎

網絡協議處理 + ip 地址標注 + 端口限制 ——>>>形成成熟的互聯網計算機系統

3.1 網絡概述

 3.1.1 網絡的由來

  早期的計算機都是獨立的一台一台以數據運算為主的機器,隨着時代的發展,運算的數據的共享需求變得迫切,為了解決這一個問題,就有了網絡的產生,通過物理路徑(有線網或無線網)將多台計算機連接起來組成一個互聯網計算機平台,實現多台計算機之間特定的數據交互模式。

3.1.2 IP地址的由來

  和作用在計算機可以進行數據交互的基礎上,又出現了新問題,如果主機要與某一台主機進行通信,如果所連接的主機數量少的情況下,可以通過對主機進行編號來識別要發送給哪一台主機,但是如果連接數量過大時,就是出現數據溢出或錯誤的情況的,給數據交互帶來很大的麻煩。為了解決這個問題,能夠在眾多計算機中找到特定的計算機,int cerf 在實驗室模擬階段使用了 32 位標記的網絡地址協議[internet protocal],用於標識網絡上唯一的一台計算機,也就是后來的 ip 地址;
根據 ip 地址,按照網絡主機數量【限制】,網絡可以分為:
1. 局域網
2. 城域網
3. 廣域網—>>全球網絡

3.1.3  端口port的由來

  和作用在通過 IP 地址確定了網絡上的某個具體主機之后,具體的數據通信主要是通過工作在計算機中的軟件執行的,不同的軟件通信很容易造成問題,那么問題又來了,隨着計算行業的發展,計算機中會安裝各種各樣的應用軟件,當主機A向主機B發送信息時,具體會由主機B上的哪個程序接收呢?由此計算機中出現了端口port的概念,端口port主要用於區別不同軟件的通信渠道,用於正確的將數據通過指定的端口渠道傳輸給對應的軟件!

3.1.4 網絡協議

  網絡協議的發展過程中,比較混亂,所以有一個非盈利性質的民間組織推出了網絡協議公共標准,任何計算機的廠商,制造的計算機必須符合這樣的標准才允許上市。
這個組織稱為:國際標准化組織/歐洲計算機制造協會聯盟組織/ISO。
ISO組織推出了網絡協議標准模型:開放互聯系統模型(OSI/RM模型)

網絡協議處理 + ip 地址標注 + 端口限制 ——>>>形成成熟的互聯網計算機系統

3.2 IP&PORT

  首先,程序必須要啟動,其次,必須有這台機器的地址,我們都知道我們人的地址大概就是國家\省\市\區\街道\樓\門牌號這樣字。那么每一台聯網的機器在網絡上也有自己的地址,它的地址是怎么表示的呢?

 就是使用一串數字來表示的,例如:100.4.5.6。 因此ip地址精確到具體的一台電腦,而端口精確到具體的程序。

3.2.1 ip地址   

   IP地址是指互聯網協議地址(英語:Internet Protocol Address,又譯為網際協議地址),是IP Address的縮寫。IP地址是IP協議提供的一種統一的地址格式,它為互聯網上的每一個網絡和每一台主機分配一個邏輯地址,以此來屏蔽物理地址的差異。 IP地址是一個32位的二進制數,通常被分割為4個“8位二進制數”(也就是4個字節)。IP地址通常用“點分十進制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之間的十進制整數。例:點分十進IP地址(100.4.5.6),實際上是32位二進制數(01100100.00000100.00000101.00000110)。

IP 地址根據使用的用戶性質,主要分為 5 類 IP 地址(A類、B類、C類、D類、E類)

  IP地址由四段組成,每個字段是一個字節,8位,最大值是255,,

  IP地址由兩部分組成,即網絡地址主機地址。網絡地址表示其屬於互聯網的哪一個網絡,主機地址表示其屬於該網絡中的哪一台主機。二者是主從關系。網絡地址和主機地址的區分主要通過子網掩碼進行划分

  IP地址的四大類型標識的是網絡中的某台主機。IPv4的地址長度為32位,共4個字節,但實際中我們用點分十進制記法。

                           圖 3.1 五類互聯網地址  

IP地址根據網絡號和主機號來分,分為A、B、C三類及特殊地址D、E。    全0和全1的都保留不用。

A類:(1.0.0.0-126.0.0.0)(默認子網掩碼:255.0.0.0或 0xFF000000)第一個字節為網絡號,后三個字節為主機號。該類IP地址的最前面為“0”,所以地址的網絡號取值於1~126之間。一般用於大型網絡。

B類:(128.0.0.0-191.255.0.0)(默認子網掩碼:255.255.0.0或0xFFFF0000)前兩個字節為網絡號,后兩個字節為主機號。該類IP地址的最前面為“10”,所以地址的網絡號取值於128~191之間。一般用於中等規模網絡。

C類:(192.0.0.0-223.255.255.0)(子網掩碼:255.255.255.0或 0xFFFFFF00)前三個字節為網絡號,最后一個字節為主機號。該類IP地址的最前面為“110”,所以地址的網絡號取值於192~223之間。一般用於小型網絡。

D類:是多播地址。該類IP地址的最前面為“1110”,所以地址的網絡號取值於224~239之間。一般用於多路廣播用戶 。

E類:是保留地址。該類IP地址的最前面為“1111”,所以地址的網絡號取值於240~255之間。

在IP地址3種主要類型里,各保留了3個區域作為私有地址,其地址范圍如下: 
A類地址:10.0.0.0~10.255.255.255 
B類地址:172.16.0.0~172.31.255.255 
C類地址:192.168.0.0~192.168.255.255

回送地址:127.0.0.1。 也是本機地址,等效於localhost或本機IP。一般用於測試使用。例如:ping 127.0.0.1來測試本機TCP/IP是否正常。

IP地址和子網掩碼求與運算就可以獲得網絡地址,確定該子網在互聯網中的身份。主機地址可以確定該子網中具體的主機。

3.2.2 "端口"是英文port的意譯,可以認為是設備與外界通訊交流的出口

 

在通過 IP 地址確定了網絡上的某個具體主機之后,具體的數據通信主要是通過工作在計算機中的軟件執行的,不同的軟件通信很容易造成問題。

不同的端口號分類
計算機中的端口號的范圍是 0 ~ 65535 之間
端口好根據使用場景,一般區分為公用端口/動態端口/保留端口
公用端口:0 ~ 1023
動態端口:1024 ~ 65535
保留端口:一般是UNIX系統中超級用戶進程分配保留端口號

3.3 協議

 3.3.1 協議

   須知一個完整的計算機系統是由硬件、操作系統、應用軟件三者組成,具備了這三個條件,一台計算機系統就可以自己跟自己玩了(打個單機游戲,玩個掃雷啥的)

  如果你要跟別人一起玩,那你就需要上網了,什么是互聯網?

  互聯網的核心就是由一堆協議組成,協議就是標准,比如全世界人通信的標准是英語,如果把計算機比作人,互聯網協議就是計算機界的英語。所有的計算機都學會了互聯網協議,那所有的計算機都就可以按照統一的標准去收發信息從而完成通信了。

 3.3.2 osi七層模型

人們按照分工不同把互聯網協議從邏輯上划分了層級:

 
                      圖 3.2 osi七層模型

分層:
應用層 (Application): 網絡服務與最終用戶的一個接口。

協議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

表示層(Presentation Layer): 數據的表示、安全、壓縮。(在五層模型里面已經合並到了應用層)

格式有,JPEG、ASCll、DECOIC、加密格式等

會話層(Session Layer): 建立、管理、終止會話。(在五層模型里面已經合並到了應用層)

對應主機進程,指本地主機與遠程主機正在進行的會話

傳輸層 (Transport): 定義傳輸數據的協議端口號,以及流控和差錯校驗。

協議有:TCP UDP,數據包一旦離開網卡即進入網絡傳輸層

網絡層 (Network): 進行邏輯地址尋址,實現不同網絡之間的路徑選擇。

協議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

數據鏈路層 (Link): 建立邏輯連接、進行硬件地址尋址、差錯校驗等功能。(由底層網絡定義協議)

將比特組合成字節進而組合成幀,用MAC地址訪問介質,錯誤發現但不能糾正。

物理層(Physical Layer): 建立、維護、斷開物理連接。(由底層網絡定義協議)

3.3.3 協議簇

  協議通常指代單獨的一個協議,協議族通常指代互相關聯的一組協議,協議棧指代某一組互相關聯的協議和他們所屬的 OSI 模型的層級結構。

四、socket編程 

 4.1 socket層

 

 

            圖 4.1 socket層是介於應用層和傳輸層之間

理解socket:Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標准的。

 注意:

1 將socket說成ip+port,ip是用來標識互聯網中的一台主機的位置,而port是用來標識這台機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啟的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序
2 而程序的pid是同一台機器上不同進程或者線程的標識(Google Chrome會有多個PID)

4.2 套接字

    套接字的概念很簡單,每台主機有一個唯一的主機地址標識,同時主機內還有標識自己進程的序號id,稱作端口,將這兩個標識符結合就構成了一個套接字(socket),這個套接字能唯一標識網絡中的一個進程。(網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個socket, 又稱為“套接字”。)

 4.2.1 套接字的發展歷史和分類

   套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一台主機上多個應用程序之間的通訊。這也被稱進程間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是 基於文件型的和基於網絡型的。 
  基於文件類型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信

基於網絡類型的套接字家族

套接字家族的名字:AF_INET

(還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要么是只用於某個平台,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由於我們只關心網絡編程,所以大部分時候我們只使用AF_INET)

 4.2.2 語法

套接字模型對象,為了能明確的表示網絡中一台數據交互的主機,需要通過 IP 地址尋址確定主機位置,通過 PORT 端口號確定主機交互接口.
在網絡套接字交互過程中,出現了兩種類型的套接字模型:

a.面向連接的套接字模型
b.面向無連接的套接字模型
python 中提供的網絡套接字,主要包含在 socket 模塊中,socket模塊提供了socket函數創建套接字。
基本語法結構:

基本語法:
socket.socket(socket_family, socket_type, protocal=0)
參數:
socket_family: socket 地址家族, AF_UNIX/AF_LOCAL 或者 AF_INET
socket_type: socket 連接類型:1)面向連接的(SOCK_STREAM) 2)面向無連接的(SOCK_DGRAME)
protocal:傳輸協議,一般不用設置,使用默認值進行自動匹配就好

 

4.3 tcp協議和udp協議

TCP(Transmission Control Protocol)可靠的、面向連接的協議(eg:打電話)、傳輸效率低全雙工通信(發送緩存&接收緩存)、面向字節流。使用TCP的應用:Web瀏覽器;電子郵件、文件傳輸程序。

UDP(User Datagram Protocol)不可靠的、無連接的服務,傳輸效率高(發送前時延小),一對一、一對多、多對一、多對多、面向報文,盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視頻流;IP語音(VoIP)。

 

 4.4 實戰演示

關於常用函數簡介:

socket()模塊函數用法:

  創建socket對象socket.socket(socket_family, socket_type, protocal=0)

  獲取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
獲取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

服務端套接字函數

 
         

s.bind()     綁定(主機,端口號)到套接字
s.listen()    開始TCP監聽
s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來

客戶端套接字函數
s.connect()                       主動初始化TCP服務器連接
s.connect_ex() connect()  函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數
s.recv()         接收TCP數據
s.send()        發送TCP數據(send在待發送數據量大於己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall()     發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom()  接收UDP數據
s.sendto()     發送UDP數據
s.getpeername()   連接到當前套接字的遠端的地址
s.getsockname()   當前套接字的地址
s.getsockopt()      返回指定套接字的參數
s.setsockopt()       設置指定套接字的參數
s.close()               關閉套接字

面向鎖的套接字方法
s.setblocking()   設置套接字的阻塞與非阻塞模式
s.settimeout()    設置阻塞套接字操作的超時時間
s.gettimeout()    得到阻塞套接字操作的超時時間

面向文件的套接字的函數
s.fileno()        套接字的文件描述符
s.makefile()   創建一個與該套接字相關的文件

4.4.1 TCP編程

4.4.1.1 建立TCP服務器連接的6個步驟

 1、創建socket對象。調用socket構造函數:

基本語法: socket.socket(socket_family, socket_type, protocal=0)

參數:

socket_family: socket 地址家族, AF_UNIX/AF_LOCAL 或者 AF_INET

socket_type:socket 連接類型:1)面向連接的(SOCK_STREAM) 2)面向無連接的(SOCK_DGRAME) protocal:

傳輸協議,一般不用設置,使用默認值進行自動匹配就好

我們這里選擇基於網絡類型的數據通信,socket連接類型為面向的連接
socket(AF_INET,SOCK_STREAM)
 
2、將socket綁定(指派)到指定的地址上:
基本語法:socket.bind(address)
參數:address必須為一個雙元素的元組,(host,port),主機名或ip地址+端口號。如果端口號正在被使用或者保留,或主機名或ip地址錯誤,則引發socket.error異常。

如果IP地址為 0.0.0.0 代表本機的任意一個IP           端口  0--1024 為系統保留

IP地址為localhost代表本地主機,指這台計算機,相對應的ip地址為127.0.0.1

3、綁定后,必須准備好套接字,以便接受連接請求:

基本語法:socket.listen(backlog)
參數:backlog
backlog指定最多的連接數,至少為1,接到連接請求后,這些請求必須排隊,如果隊列已滿,則拒絕請求。
 
4、服務器套接字通過socket的accept方法等待客服請求一個連接:
connection,address=socket.accept()
調用accept方法時,socket會進入‘waiting’(阻塞)狀態。客服請求連接時,方法建立連接並返回服務器。accept方法返回一個含有兩個元素的元組,形如(connection,address).第一個元素是新的socket對象,服務器通過它與客服端通信,第二個元素是客戶端internet的地址。
 
5、處理階段,服務器通過send和recv方法通信
 
  服務器調用send,並采用字符串的形式向客服發送信息。send方法返回已發送的字符個數。服務器使用recv方法從客服接受信息。調用recv時,必須指定一個整數來控制本次調用所接受最大的數據量。recv方法在接受數據時會blocket狀態,最后返回一個字符串,用它來表示接受的數據。如果發送的量超過recv所允許,數據會被截斷。多余的數據將緩沖於接收端。
 
6、調用接受,服務器調用socket的close方法以關閉連接

4.4.1.2 建立TCP客服端

 1、創建socket對象。調用socket構造函數:
2、使用socket的connect方法連接服務器socket.connect((host,port))
3、客戶和服務器通過send和recv方法通信:
4、結束后,客戶通過調用socket的close方法來關閉理連接
附上代碼展示:
 1 import socket
 2 # 定義服務器信息
 3 print('初始化服務器主機信息')
 4 HOST = socket.gethostname() # 本地主機,指這台計算機,相對應的ip地址為127.0.0.1
 5 PORT = 5000  #端口  0--1024 為系統保留
 6 ADDRESS = (HOST, PORT)
 7 BUFFER = 1024 #數據發送和接受的最大數據大小
 8 
 9 # 創建TCP服務socket對象
10 print("初始化服務器主機套接字對象......")
11 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
12 # 面向網絡的套接字:通過網絡進行數據交互, TCP協議,server就是socket的實例
13 
14 # 綁定主機信息
15 print('綁定的主機信息......')
16 server.bind(ADDRESS)  #元組,相當於一個參數
17 
18 # 啟動服務器 一個只能接受一個客戶端請求,可以有10個請求排隊
19 print("開始啟動服務器......")
20 server.listen(10)
21 
22 #等待連接
23 while True:
24     # 等待來自客戶端的連接
25     print('等待客戶端連接')
26     conn, addr = server.accept()  # 等電話
27     print('連接的客服端套接字對象為:{}\n客服端的IP地址(撥進電話號碼):{}'.format(conn, addr))
28     # 發送給客戶端的數據
29     #server.send("歡迎訪問服務器".encode('utf-8'))
30 
31     #收發信息,可以收發多次
32     while True:
33         # 接收客戶端發送的數據
34         print("等待客戶端發送信息:")
35         msg = conn.recv(BUFFER)
36         try:
37             print("client客戶端:{}".format(msg.decode("utf-8")))
TCP server端
 1 import socket
 2 # 定義要連接的服務器信息
 3 HOST = socket.gethostname()
 4 PORT = 5000
 5 ADDRESS = (HOST, PORT)
 6 
 7 #創建客戶端套接字對象
 8 client = socket.socket()# 相當於聲明socket類型,同時生成socket鏈接對象,默認值
 9 
10 #連接服務器
11 client.connect(ADDRESS)
12 print('歡迎連接服務器')
13 # 和服務器進行數據交互
14 while True:
15     # 給服務器發送消息
16     info = input("請輸入要發送的信息:")
17     client.send(info.encode("utf-8"))
18 
19     if info == 'bye':
20         client.close()
21         print("客戶端退出")
22         break
23     #接受服務端信息
24     print("等待服務端發送信息:")
25     data = client.recv(1024)
26     try:
27         print("server服務端端:{}".format(data.decode("utf-8")))
28 
29     except Exception:
30         print("server服務端端:{}".format(data.decode("gbk")))
TCP client端

 4.4.2 UDP編程

  udp語法格式:

udp服務端

1 ss = socket()   #創建一個服務器的套接字
2 ss.bind()       #綁定服務器套接字
3 inf_loop:       #服務器無限循環
4     cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送)
5 ss.close()

udp客戶端

1 cs = socket()   # 創建客戶套接字
2 comm_loop:      # 通訊循環
3     cs.sendto()/cs.recvfrom()   # 對話(發送/接收)
4 cs.close()                      # 關閉客戶套接字
UDP的建立過程和TCP一樣,這里不再贅述。
 
 1 from socket import *
 2 
 3 ip_port = ('127.0.0.1', 8080)
 4 buffer_size = 1024
 5 
 6 udp_server = socket(AF_INET, SOCK_DGRAM)  # 數據報套接字
 7 udp_server.bind(ip_port)
 8 
 9 while True:
10     data, addr = udp_server.recvfrom(buffer_size)
11     print(data.decode('utf-8'))
12     print('data from', addr)
13     udp_server.sendto(data.upper(), addr)  # upper() 小寫變大寫
UDP_server端
 1 from socket import *
 2 ip_port = ('127.0.0.1', 8080)  #服務端IP+端口
 3 buffer_size = 1024
 4 
 5 udp_client = socket(AF_INET, SOCK_DGRAM) #udp數據報套接字
 6 
 7 while True:
 8     msg = input('>>:').strip()
 9     udp_client.sendto(msg.encode('utf-8'), ip_port)
10     #數據,ip地址+端口
11     data, addr = udp_client.recvfrom(buffer_size)
12     print(data.decode('utf-8'))
13     print('data from %s',addr)
UDP_client端

 

 
send() sendto() recv() recvfrom()
  我們發現在TCP連接中,傳送數據用的時send()和recv()方法,而在UDP中用的是sendto(),和recvfrom()。這兩種方法實現作用差不多,但是參數不同,TCP中,send(data)和recv(buffer),因為在TCP中,傳送數據之前連接已經建立,也就是說我們知道給誰法或者誰接收。而在UDP中,recvfrom(buffer)和sendto(dat,addr),recvfrom()可以接收到sender的addr,然后發送時,根據addr發送。因為UDP不是面向連接的,所以我們必須知道這個數據要發給誰,它的地址是什么。
 
 
 
 
 
 
 
 
 
 
q


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM