近期學(xué)習(xí)python基礎(chǔ)知識,到網(wǎng)絡(luò)編程部分覺得很有意思,寫個博客記錄一下,首先可先了解一下計算機(jī)網(wǎng)絡(luò)的基礎(chǔ)知識,參考相關(guān)書籍即可,本文只做簡單知識介紹!
創(chuàng)新互聯(lián)是一家專注于網(wǎng)站設(shè)計制作、成都網(wǎng)站建設(shè)與策劃設(shè)計,西吉網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:西吉等地區(qū)。西吉做網(wǎng)站價格咨詢:189820811081.OSI七層協(xié)議,TCP、UDP協(xié)議
首先, 我們今天使用的計算機(jī)都是要聯(lián)網(wǎng)使用的. 很少有那種單機(jī)走天下的情況了. 那么我們的計算機(jī)是如何通過網(wǎng)絡(luò)實現(xiàn)通信的. 我們先了解一些關(guān)于網(wǎng)絡(luò)的基礎(chǔ)知識. 然后再開始學(xué)習(xí)一些關(guān)于網(wǎng)絡(luò)編程的內(nèi)容, 第一個要解釋的名詞叫協(xié)議. 我們只有明白協(xié)議是什么, 后面再看各種各樣的通信規(guī)則就容易的多了.
官話: 網(wǎng)絡(luò)協(xié)議是通信計算機(jī)雙方必須共同遵從的一組約定。如怎么樣建立連接、怎么樣互相識別等。只有遵守這個約定,計算機(jī)之間才能相互通信交流
普通話: 兩臺計算機(jī)之間約定好. 我發(fā)送的數(shù)據(jù)格式是什么. 你接收到數(shù)據(jù)之后. 使用相同的格式來拿到數(shù)據(jù)
例子: 你和一個韓國人交流. 你說中文, 他說韓文. 你倆是不能明白對方說的是什么的. 怎么辦. 你倆約定好, 都說英語.實現(xiàn)交流. 這個就叫協(xié)議.
網(wǎng)絡(luò)協(xié)議: 互聯(lián)網(wǎng)之間互相傳遞消息的時候使用統(tǒng)一的一系列約定
在今天的互聯(lián)網(wǎng)數(shù)據(jù)傳輸中一般使用的是OSI七層協(xié)議. 也有簡稱為五層, 四層協(xié)議. 只是對不同網(wǎng)絡(luò)層的定義不同.
內(nèi)部原理和作用是一樣的.
下面簡單介紹一下OSI七層協(xié)議的基本功能
首先是物理層
: 該層是網(wǎng)絡(luò)通信的數(shù)據(jù)傳輸介質(zhì),由連接不同結(jié)點的電纜與設(shè)備共同構(gòu)成。主要跟功能是:利用傳輸介質(zhì)為數(shù)據(jù)鏈路層提供物理連接,負(fù)責(zé)處理數(shù)據(jù)傳輸并監(jiān)控數(shù)據(jù)出錯率,以便數(shù)據(jù)流的透明傳輸數(shù)據(jù)鏈路層:
這一層負(fù)責(zé)裝配自己和對方主機(jī)的MAC地址. MAC地址: 每個網(wǎng)絡(luò)設(shè)備的唯一編碼. 全球唯一. 由不同廠商直接燒錄在網(wǎng)卡上.
作用: 在龐大的網(wǎng)絡(luò)系統(tǒng)中, 你要發(fā)送的數(shù)據(jù)到底是要給誰的. 由誰發(fā)送出來的. 這就相當(dāng)于你寫信的時候的信封. 上面得寫清楚收貨人地址.網(wǎng)絡(luò)層:
在有了MAC地址其實我們的電腦就可以開始通信了. 但是. 此時的通信方式是廣播. 相當(dāng)于通信基本靠吼. 你發(fā)送一個數(shù)據(jù)出去. 會自動的發(fā)給當(dāng)前網(wǎng)絡(luò)下的所有計算機(jī). 然后每個計算機(jī)的網(wǎng)卡會看一眼這個數(shù)據(jù)是不是發(fā)給自己的. 像這樣的通信方式, 如果計算機(jī)的數(shù)據(jù)量少. 是沒有問題的. 但是. 如果全球所有計算機(jī)都按照這樣的方式來傳輸消息. 那不僅是效率的問題了. 絕對是災(zāi)難性的. 那怎么辦. 大家就想到了一個新的方案, 這個方案叫IP協(xié)議. 使用IP協(xié)議就把不同區(qū)域的計算機(jī)劃分成一個一個的子網(wǎng). 子網(wǎng)內(nèi)的通信使用廣播來傳遞消息. 廣播外通過路由進(jìn)行傳遞消息. 你可以理解為不同快遞公司的分撥中心. 我給你郵寄一個快遞. 先看一下是不是自己區(qū)域的. 是自?己區(qū)域直接挨家挨戶找就OK了. 但是如果不是我這個區(qū)域的. 就通過分撥中心(路由器網(wǎng)關(guān))找到你所在的區(qū)域的分撥中心(路由?網(wǎng)關(guān)), 再通過你的分撥中心下發(fā)給你. 這里IP協(xié)議的作用就體現(xiàn)出來了. 專門用來劃分子網(wǎng)的.那么在傳輸數(shù)據(jù)的時候就必須要把對方的ip地址帶著. 有了這個ip再加上子網(wǎng)掩碼就可以判斷出該數(shù)據(jù)到底是屬于哪個子網(wǎng)下的數(shù)據(jù).
IP地址: 由4位點分?十進(jìn)制表示. 每位最?大255. 故IP地址的范圍: 0.0.0.0~255.255.255.255. 為什什么是255, 答:
28 每一位用8位2進(jìn)制表示, 合起來32位就可以表示一個計算機(jī)的ip地址
子網(wǎng)掩碼: 用來劃分子網(wǎng)的一個4位點分十進(jìn)制.
網(wǎng)關(guān): 路由?在子網(wǎng)內(nèi)的ip. 不同局域網(wǎng)進(jìn)行數(shù)據(jù)傳輸?shù)慕涌?分撥中心)
計算子網(wǎng)的過程:
1 ip1: 192.168.123.16
2 ip2: 192.168.123.45
3 子網(wǎng)掩碼: 255.255.255.0
4 全部轉(zhuǎn)化成二進(jìn)制
4 ip1: 11000000 10101000 01111011 00010000
5 ip2: 11000000 10101000 01111011 00101101
6 子網(wǎng): 11111111 11111111 11111111 00000000
7 讓ip1和ip2分別和子網(wǎng)進(jìn)行"與"運(yùn)算
8 ip1 & 子網(wǎng): 11000000 10101000 01111011 00000000
9 ip2 & 子網(wǎng): 11000000 10101000 01111011 00000000
10 相等. OK 這兩個IP就是同一個子網(wǎng)
傳輸層
: 我們現(xiàn)在解決了外界的數(shù)據(jù)傳輸問題. 使用MAC地址和IP地址可以唯一的定位到一臺計算機(jī)了. 那么還有一個問題沒有解決. 我們知道一臺計算機(jī)內(nèi)是很有可能運(yùn)行著多個網(wǎng)絡(luò)應(yīng)用程序的. 比如, 你開著LOL, 掛著DNF, 聊著QQ, 還看著快播. 那么此時你的計算機(jī)網(wǎng)卡接收到了來自遠(yuǎn)方的一條數(shù)據(jù). 那么這一條數(shù)據(jù)到底給那個應(yīng)用呢? 說白了, 快遞送到你公司了. 地址沒毛病了. 可是你公司那么多人. 這個快遞到底給誰? 不能亂給啊. 怎么辦呢? 互聯(lián)網(wǎng)大佬們想到了一個新詞叫端口.
傳輸層規(guī)定: 給每?一個應(yīng)?用程序分配一個唯一的端口號. 當(dāng)有數(shù)據(jù)發(fā)送過來之后. 通過端口號來決定該數(shù)據(jù)發(fā)送的具體應(yīng)用程序.但是根據(jù)不同應(yīng)用程序?qū)W(wǎng)絡(luò)的需求的不同(有的要求快, 有的要求可靠) 又把傳輸層劃分成兩個協(xié)議. 一個叫TCP, 一個叫UDP. 所以, 我們常說的TCP/IP協(xié)議中最重要, 也是我們最關(guān)注的其實就是IP和端口了了. 因為有了這兩個, 我們其實就可以定位到某一臺計算機(jī)上的某個網(wǎng)絡(luò)應(yīng)用程序了了. 也就可以給他發(fā)送消息了
32位計算機(jī)上的端口數(shù):
TCP : 65536個
UDP: 65536個
TCP和UDP的區(qū)別:
應(yīng)用層
: TCP+IP可以定位到計算機(jī)上的某個應(yīng)用了了. 但是不同用傳輸?shù)臄?shù)據(jù)格式可能是不一樣的. 就好比快遞. 有的是大包裹. 有的是小文件. 一個要用大快遞袋裝, 一個要用小快遞袋裝. 到了了應(yīng)用層. 我們一般是根據(jù)不同類型的應(yīng)用程序進(jìn)行的再一次封裝. 比如, HTTP協(xié)議, SMTP協(xié)議, FTP協(xié)議. 等等.2.初識Socket-TCP編程
在幾乎所有的編程語言中, 我們在編寫網(wǎng)絡(luò)程序的時候都要使用到socket. socket翻譯過來叫套接字. 我們上面也了解到了一次網(wǎng)絡(luò)通信的數(shù)據(jù)需要包裹著mac, ip, port等信息. 但是如果每次我們開發(fā)都要程序員去?個一個的去準(zhǔn)備數(shù)據(jù), 那工作量絕對是絕望的. 所以, 計算機(jī)提出了了socket. socket幫助我們完成了網(wǎng)絡(luò)通信中的絕大多數(shù)操作. 我們只需要告訴socket. 我要向哪臺計算機(jī)(ip, port)發(fā)送數(shù)據(jù). 剩下的所有東西都由socket幫我們完成. 所以使用socket完成數(shù)據(jù)傳輸是非常方便的.
話不多說,練起來吧~~
server端:
import socket
#創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088)) # 綁定ip和端口
sk.listen() # 開始監(jiān)聽
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept() # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address) # address是客戶端的ip和端口
while 1: #能持續(xù)向客戶端傳遞消息
conn.send(input(">>>").encode("utf-8")) # 發(fā)送的內(nèi)容只能是bytes
print(conn.recv(1024).decode("utf-8")) #展示接收到的消息
client端:
import socket
sk = socket.socket() # 創(chuàng)建通道
print("客戶端初始化完成")
sk.connect(("127.0.0.1", 8088)) # 建立連接
print("客戶端連接成功")
while 1: #持續(xù)向服務(wù)端發(fā)送消息
print(sk.recv(1024).decode("utf-8")) # 大接受1024字節(jié)的內(nèi)容
sk.send(input(">>>").encode("utf-8")) #展示接收到的消息
3.初識Socket-UDP編程
server端:
import socket
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1", 8089))
msg, address = sk.recvfrom(1024)
print(msg)
sk.sendto(b'hi', address)
client端:
import socket
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
sk.sendto(b'hello', ("127.0.0.1", 8089))
msg, address = sk.recvfrom(1024)
print(msg)
跟TCP編程原理差不多,只是UDP不用長時間建立連接,發(fā)送一個包,不用等回復(fù),也不需要理會對方收到還是沒收到,沒有嚴(yán)格意義上的服務(wù)端和客戶端
4.黏包現(xiàn)象
在使用TCP協(xié)議進(jìn)行數(shù)據(jù)傳輸?shù)臅r候, 會有以下問題出現(xiàn).
client端:
import socket
sk = socket.socket() # 創(chuàng)建通道
print("客戶端初始化完成")
sk.connect(("127.0.0.1", 8088)) # 建立連接
print("客戶端連接成功")
sk.send("哈哈".encode("utf-8"))
sk.send("哈哈".encode("utf-8")) #發(fā)送兩次
print("發(fā)送完畢")
sk.close()
server端:
import socket
# 創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088)) # 綁定ip和端口
sk.listen() # 開始監(jiān)聽
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept() # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address) # address是客戶端的ip和端口
msg1 = conn.recv(1024)
print(msg1.decode("utf-8"))
msg2 = conn.recv(1024)
print(msg2.decode("utf-8"))
sk.close()
server端收到的內(nèi)容:
可以看到,兩次發(fā)送的內(nèi)容,黏在一起,變成了一個包,就是典型的黏包現(xiàn)象。
那么如何解決黏包問題呢? 很簡單. 之所以出現(xiàn)黏包就是因為數(shù)據(jù)沒有邊界. 直接把兩個包混合成了一個包. 那么我可以在發(fā)送數(shù)據(jù)的時候. 指定邊界. 告訴對方. 我接下來這個數(shù)據(jù)包有多大. 對面接收數(shù)據(jù)的時候呢, 先讀取該數(shù)據(jù)包的大小.然后再讀取數(shù)據(jù). 就不會產(chǎn)生黏包了.
普通話: 發(fā)送數(shù)據(jù)的時候制定數(shù)據(jù)的格式: 長度+數(shù)據(jù) 接收的時候就知道有多少是當(dāng)前這個數(shù)據(jù)包的大小了. 也就相當(dāng)于定義了分隔邊界了.
展示一波操作:
client端:
import socket
sk = socket.socket() # 創(chuàng)建通道
print("客戶端初始化完成")
sk.connect(("127.0.0.1", 8088)) # 建立連接
print("客戶端連接成功")
s = "哈哈"
bs = sk.send(s.encode("utf-8"))
# 計算數(shù)據(jù)長度.格式化成四位數(shù)字
bs_len = format(len(bs), "04d").encode("utf-8")
# 發(fā)送數(shù)據(jù)之前,先發(fā)送數(shù)據(jù)的長度
sk.send(bs_len)
sk.send(bs)
# 發(fā)送第二次
sk.send(bs_len)
sk.send(bs)
print("發(fā)送完畢")
sk.close()
server端:
import socket
# 創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088)) # 綁定ip和端口
sk.listen() # 開始監(jiān)聽
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept() # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address) # address是客戶端的ip和端口
# 接收數(shù)據(jù)長度4個字節(jié),轉(zhuǎn)化成數(shù)字
bs_len = int(conn.recv(4).decode("utf-8"))
# 讀取數(shù)據(jù)
msg1 = conn.recv(1024)
print(msg1.decode("utf-8"))
# 再接收數(shù)據(jù)長度,讀取數(shù)據(jù)
bs_len = int(conn.recv(4).decode("utf-8"))
msg2 = conn.recv(1024)
print(msg2.decode("utf-8"))
sk.close()
但是,你應(yīng)該發(fā)現(xiàn)了,這種傳輸?shù)姆绞诫m然能解決黏包問題,但是每次接收都要先定義接收的長度,喵的豈不是太累了??這個問題,python也有自己的態(tài)度,代碼堅決要給你搞簡單,就引入了一個新的模塊struct
,這是重點,拿小本本記下來,要考!!
展示一下struct的用法:
import struct
ret = struct.pack("i", 123456789) # 把數(shù)字打包成字節(jié)
print(ret)
print(len(ret)) # 4 不論數(shù)字大小,定死了4個字節(jié)
# 把字節(jié)還原回數(shù)字
ds = b'\x15\xcd[\x07'
num = struct.unpack("i", ds)[0] # num返回的是一個元祖,取索引為0的元素,也就是第一個元素,就是我們傳輸?shù)臄?shù)字
print(num) # 123456789
好的,回歸到黏包的問題上來,論如何優(yōu)雅的解決黏包的問題:
client端:
import socket
import struct
sk = socket.socket() # 創(chuàng)建通道
print("客戶端初始化完成")
sk.connect(("127.0.0.1", 8088)) # 建立連接
print("客戶端連接成功")
msg_bs = "你好呀".encode("utf-8")
msg_struct_len = struct.pack("i", len(msg_bs))
sk.send(msg_struct_len)
sk.send(msg_bs)
print("發(fā)送完畢")
# 發(fā)送第二次
sk.send(msg_struct_len)
sk.send(msg_bs)
print("發(fā)送完畢")
sk.close()
server端:
import socket
import struct
# 創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088)) # 綁定ip和端口
sk.listen() # 開始監(jiān)聽
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept() # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address) # address是客戶端的ip和端口
msg_struct_len = conn.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = conn.recv(msg_len)
print(data.decode("utf-8"))
print("接收完畢")
# 接收第二個包
msg_struct_len = conn.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = conn.recv(msg_len)
print(data.decode("utf-8"))
print("接收完畢")
sk.close()
但是吧,這樣也是稍顯繁瑣了,如果多次使用,也可將用到的struct模塊,封裝起來,需要用到的時候,就導(dǎo)入這個自定義的模塊:
封裝的模塊my_struct_util.py:
import struct
def my_send(sk, msg):
msg_len = msg.encode("utf-8")
msg_struct_len = struct.pack("i", len(msg_len))
sk.send(msg_struct_len)
sk.send(msg_len)
def my_recv(sk):
msg_struct_len = sk.recv(4)
msg_len = struct.unpack("i", msg_struct_len)[0]
data = sk.recv(msg_len)
print(data.decode("utf-8"))
client端:
import socket
import my_socket_util as msu
sk = socket.socket() # 創(chuàng)建通道
print("客戶端初始化完成")
sk.connect(("127.0.0.1", 8088)) # 建立連接
print("客戶端連接成功")
msu.my_send(sk, "你好嗎")
msu.my_send(sk, "你好嗎")
sk.close()
server端:
import socket
import my_socket_util as msu
# 創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088)) # 綁定ip和端口
sk.listen() # 開始監(jiān)聽
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept() # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address) # address是客戶端的ip和端口
msu.my_recv(conn)
msu.my_recv(conn)
sk.close()
這樣,是不是就簡便了很多呢~~
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
網(wǎng)頁名稱:初識python網(wǎng)絡(luò)編程-01-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://jinyejixie.com/article16/jgcdg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、品牌網(wǎng)站建設(shè)、外貿(mào)建站、App設(shè)計、品牌網(wǎng)站設(shè)計、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容