網路程式設計
網路程式設計
什麼是網路程式設計
網路程式設計最主要的工作就是在傳送端把資訊通過規定好的協議進行組裝包,在接收端按照規定好的協議把包進行解析,從而提取出
對應的資訊,達到通訊的目的。
軟體開發的架構
我們經常使用的程式之間的通訊大致分為兩種:
這兩種通訊方式就是兩種軟體開發架構.
c/s架構
C/S的全稱是client和serve,中文就是客戶端和服務端架構,這種架構也是從使用者層面(物理層面)來劃分的.這裡的客戶端是需要使用者在電腦或者手機上先安裝才能執行的.

image.png
B/S架構
B/S就是Browser與Server 中文意思就是:瀏覽器端與服務端架構,這種架構是從使用者層面來劃分的.
Browser瀏覽器,其實也是一種Client客戶端,但是不用安裝什麼應用程式.

image.png
網路基礎
早期的時候兩個計算機要通訊的話需要一根網線連線,稱為聯機

image.png
但是這種方法只能是一對一的交流,不能同時多個機子交流所以後邊出現了 乙太網:區域網與交換機

image.png
廣播
廣播是主機一對多的一種通訊模式,一臺主機要傳送一條訊息,網路會不選擇的把這條資訊都發給所有電腦,就像有限電視一樣.在資料網路中其被限制在二層交換機的區域網範圍內,禁止廣播資料穿過路由器.
ip地址與ip協議
規定萬羅地址的協議叫ip協議,它定義的地址稱之為ip地址,廣泛採用的是V4就是ipv4的版本,它規定網路地址由32位2進製表示.
範圍:0.0.0.0-255.255.255.255
mac地址
在每塊網絡卡出廠時都會燒製上世界上唯一的mac地址,長度為48位的2進位制,通常由12位16進位制數表示.是傳送端和接受端的地址.
ARP協議
arp協議是查詢IP地址和MAC地址的對應關係,被稱為地址解析協議,是根據IP地址獲取實體地址的一個TCP/ip協議.
TCP協議和UDP協議
應用程式間怎麼通訊呢?計算機之間的通訊可以靠IP地址和MAC地址來幫我們確定唯一的一臺機器,TCP和UDP就是幫我們找到一臺機器上的軟體.
埠
一臺擁有IP地址的主機可以提供很多服務,這些服務雖然是同一個IP但是會有埠來區分不同服務的。
TCP協議
當應用程式希望通過程式通訊時,它會發送一個通訊請求,這個請求必須送到一個確切的地方,在達成共識返回一個已經連線時,TCP將在兩個應用程式之間建立一個雙全工的通訊。
當要結束時,雙方都返回一個斷開連線的請求和回執,一共四條命令。

image.png
TCP三次握手
TCP是因特網中的傳輸層協議,使用三次握手協議建立連線。當主動方發出SYN連線請求後,等待對方回答·SYN+ACK[1]·,並最終對對方的 SYN 執行 ACK 確認。這種建立連線的方法可以防止產生錯誤的連線。[1]
TCP三次握手的過程如下:
客戶端傳送·SYN(SEQ=x)·報文給伺服器端,進入SYN_SEND狀態。
伺服器端收到SYN報文,迴應一個·SYN (SEQ=y)··ACK(ACK=x+1)·報文,進入·SYN_RECV·狀態。
客戶端收到伺服器端的SYN報文,迴應一個·ACK(ACK=y+1)·報文,進入Established狀態。
三次握手完成,TCP客戶端和伺服器端成功地建立連線,可以開始傳輸資料了。
TCP四次揮手
建立一個連線需要三次握手,而終止一個連線要經過四次握手,這是由TCP的半關閉(half-close)造成的。
(1) 某個應用程序首先呼叫close,稱該端執行“主動關閉”(active close)。該端的TCP於是傳送一個FIN分節,表示資料傳送完畢。
(2) 接收到這個FIN的對端執行 “被動關閉”(passive close),這個FIN由TCP確認。
注意:FIN的接收也作為一個檔案結束符(end-of-file)傳遞給接收端應用程序,放在已排隊等候該應用程序接收的任何其他資料之後,因為,FIN的接收意味著接收端應用程序在相應連線上再無額外資料可接收。
(3) 一段時間後,接收到這個檔案結束符的應用程序將呼叫close關閉它的套接字。這導致它的TCP也傳送一個FIN。
(4) 接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。[1]
既然每個方向都需要一個FIN和一個ACK,因此通常需要4個分節。
注意:
(1) “通常”是指,某些情況下,步驟1的FIN隨資料一起傳送,另外,步驟2和步驟3傳送的分節都出自執行被動關閉那一端,有可能被合併成一個分節。[2]
(2) 在步驟2與步驟3之間,從執行被動關閉一端到執行主動關閉一端流動資料是可能的,這稱為“半關閉”(half-close)。
(3) 當一個Unix程序無論自願地(呼叫exit或從main函式返回)還是非自願地(收到一個終止本程序的訊號)終止時,所有開啟的描述符都被關閉,這也導致仍然開啟的任何TCP連線上也發出一個FIN。
無論是客戶還是伺服器,任何一端都可以執行主動關閉。通常情況是,客戶執行主動關閉,但是某些協議,例如,HTTP/1.0卻由伺服器執行主動關閉。[2]
UDP協議
當應用程式希望通過UDP與一個應用程式通訊時,傳輸資料之前源端和終端不建立連線。
當它想傳送時就簡單地去抓取來自應用程式的資料,並儘可能快地把它扔到網路上。
TCP和UDP對比
TCP---傳輸控制協議,提供的是面向連線、可靠的位元組流服務。當客戶和伺服器彼此交換資料前,必須先在雙方之間建立一個TCP連線,之後才能傳輸資料。TCP提供超時重發,丟棄重複資料,檢驗資料,流量控制等功能,保證資料能從一端傳到另一端。
UDP---使用者資料報協議,是一個簡單的面向資料報的運輸層協議。UDP不提供可靠性,它只是把應用程式傳給IP層的資料報傳送出去,但是並不能保證它們能到達目的地。由於UDP在傳輸資料報前不用在客戶和伺服器之間建立一個連線,且沒有超時重發等機制,故而傳輸速度很快
TCP(Transmission Control Protocol)可靠的、面向連線的協議(eg:打電話)、傳輸效率低全雙工通訊(傳送快取&接收快取)、面向位元組流。使用TCP的應用:Web瀏覽器;電子郵件、檔案傳輸程式。
UDP(User Datagram Protocol)不可靠的、無連線的服務,傳輸效率高(傳送前時延小),一對一、一對多、多對一、多對多、面向報文,盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視訊流;IP語音(VoIP)。

image.png
osi七層模型
網際網路的核心就是由一堆協議組成,協議就是標準,比如全世界人通訊的標準是英語,如果把計算機比作人,網際網路協議就是計算機界的英語。所有的計算機都學會了網際網路協議,那所有的計算機都就可以按照統一的標準去收發資訊從而完成通訊了。

image.png

image.png

image.png

image.png
python網路程式設計
Python提供了兩個級別訪問的網路服務:
socket socketserver
什麼是socket
Socket又稱"套接字",應用程式通常通過"套接字"向網路發出請求或者應答網路請求,使主機間或者一臺計算機上的程序間可以通訊。
socket層

image.png
socket()函式
Python 中,我們用 socket()函式來建立套接字,語法格式如下:
socket.socket([family[, type[, proto]]])
引數
- family:套接字家族可以使AF_UNIX或者AF_INET
- type: 套接字型別可以根據是面向連線的還是非連線分為SOCK_STREAM或SOCK_DGRAM
- protocol:一般不填預設0
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.connect_ex() | connect()函式的擴充套件版本,出錯時返回出錯碼,而不是丟擲異常 |
公共用途的套接字函式 | |
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.setsockopt(level,optname,value) | 設定給定套接字選項的值。 |
s.getsockopt(level,optname[.buflen]) | 返回套接字選項的值。 |
s.settimeout(timeout) | 設定套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛建立套接字時設定,因為它們可能用於連線的操作(如connect()) |
s.gettimeout() | 返回當前超時期的值,單位是秒,如果沒有設定超時期,則返回None。 |
s.fileno() | 返回套接字的檔案描述符。 |
s.setblocking(flag) | 如果flag為0,則將套接字設為非阻塞模式,否則將套接字設為阻塞模式(預設值)。非阻塞模式下,如果呼叫recv()沒有發現任何資料,或send()呼叫無法立即傳送資料,那麼將引起socket.error異常。 |
s.makefile() | 建立一個與該套接字相關連的檔案 |
socket的使用
基於TCP連線
tcp基於的連線,必須先啟動服務端,再去啟動客服端
server端 伺服器端
import socket sk = socket.socket() sk.bind(('127.0.0.1',8898))#把地址繫結到套接字 sk.listen()#監聽連結 conn,addr = sk.accept() #接受客戶端連結 ret = conn.recv(1024)#接收客戶端資訊 print(ret)#列印客戶端資訊 conn.send(b'hi')#向客戶端傳送資訊 conn.close()#關閉客戶端套接字 sk.close()#關閉伺服器套接字(可選
client端 客戶端
import socket sk = socket.socket()# 建立客戶套接字 sk.connect(('127.0.0.1',8898))# 嘗試連線伺服器 sk.send(b'hello!') ret = sk.recv(1024)# 對話(傳送/接收) print(ret) sk.close()# 關閉客戶套接字
如果在重啟伺服器的時候碰到下面情況:

image.png
解決方案
#加入一條socket配置,重用ip和埠 import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 sk.bind(('127.0.0.1',8898))#把地址繫結到套接字 sk.listen()#監聽連結 conn,addr = sk.accept() #接受客戶端連結 ret = conn.recv(1024)#接收客戶端資訊 print(ret)#列印客戶端資訊 conn.send(b'hi')#向客戶端傳送資訊 conn.close()#關閉客戶端套接字 sk.close()#關閉伺服器套接字(可選)
基於UDP協議的socket
udp是無連結的,啟動服務之後可以直接接受訊息,不需要提前建立連結
server端
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM)#建立一個伺服器的套接字 udp_sk.bind(('127.0.0.1',9000))#繫結伺服器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr)# 對話(接收與傳送) udp_sk.close()# 關閉伺服器套接字
client端
import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)