真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

初識(shí)python網(wǎng)絡(luò)編程-01

近期學(xué)習(xí)python基礎(chǔ)知識(shí),到網(wǎng)絡(luò)編程部分覺(jué)得很有意思,寫(xiě)個(gè)博客記錄一下,首先可先了解一下計(jì)算機(jī)網(wǎng)絡(luò)的基礎(chǔ)知識(shí),參考相關(guān)書(shū)籍即可,本文只做簡(jiǎn)單知識(shí)介紹!

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)推出海南免費(fèi)做網(wǎng)站回饋大家。


1.OSI七層協(xié)議,TCP、UDP協(xié)議

首先, 我們今天使用的計(jì)算機(jī)都是要聯(lián)網(wǎng)使用的. 很少有那種單機(jī)走天下的情況了. 那么我們的計(jì)算機(jī)是如何通過(guò)網(wǎng)絡(luò)實(shí)現(xiàn)通信的. 我們先了解一些關(guān)于網(wǎng)絡(luò)的基礎(chǔ)知識(shí). 然后再開(kāi)始學(xué)習(xí)一些關(guān)于網(wǎng)絡(luò)編程的內(nèi)容, 第一個(gè)要解釋的名詞叫協(xié)議. 我們只有明白協(xié)議是什么, 后面再看各種各樣的通信規(guī)則就容易的多了.
官話: 網(wǎng)絡(luò)協(xié)議是通信計(jì)算機(jī)雙方必須共同遵從的一組約定。如怎么樣建立連接、怎么樣互相識(shí)別等。只有遵守這個(gè)約定,計(jì)算機(jī)之間才能相互通信交流
普通話: 兩臺(tái)計(jì)算機(jī)之間約定好. 我發(fā)送的數(shù)據(jù)格式是什么. 你接收到數(shù)據(jù)之后. 使用相同的格式來(lái)拿到數(shù)據(jù)
例子: 你和一個(gè)韓國(guó)人交流. 你說(shuō)中文, 他說(shuō)韓文. 你倆是不能明白對(duì)方說(shuō)的是什么的. 怎么辦. 你倆約定好, 都說(shuō)英語(yǔ).實(shí)現(xiàn)交流. 這個(gè)就叫協(xié)議.
網(wǎng)絡(luò)協(xié)議: 互聯(lián)網(wǎng)之間互相傳遞消息的時(shí)候使用統(tǒng)一的一系列約定
在今天的互聯(lián)網(wǎng)數(shù)據(jù)傳輸中一般使用的是OSI七層協(xié)議. 也有簡(jiǎn)稱為五層, 四層協(xié)議. 只是對(duì)不同網(wǎng)絡(luò)層的定義不同.
內(nèi)部原理和作用是一樣的.
初識(shí)python網(wǎng)絡(luò)編程-01

下面簡(jiǎn)單介紹一下OSI七層協(xié)議的基本功能
首先是物理層: 該層是網(wǎng)絡(luò)通信的數(shù)據(jù)傳輸介質(zhì),由連接不同結(jié)點(diǎn)的電纜與設(shè)備共同構(gòu)成。主要跟功能是:利用傳輸介質(zhì)為數(shù)據(jù)鏈路層提供物理連接,負(fù)責(zé)處理數(shù)據(jù)傳輸并監(jiān)控?cái)?shù)據(jù)出錯(cuò)率,以便數(shù)據(jù)流的透明傳輸
數(shù)據(jù)鏈路層: 這一層負(fù)責(zé)裝配自己和對(duì)方主機(jī)的MAC地址. MAC地址: 每個(gè)網(wǎng)絡(luò)設(shè)備的唯一編碼. 全球唯一. 由不同廠商直接燒錄在網(wǎng)卡上.
作用: 在龐大的網(wǎng)絡(luò)系統(tǒng)中, 你要發(fā)送的數(shù)據(jù)到底是要給誰(shuí)的. 由誰(shuí)發(fā)送出來(lái)的. 這就相當(dāng)于你寫(xiě)信的時(shí)候的信封. 上面得寫(xiě)清楚收貨人地址.
網(wǎng)絡(luò)層: 在有了MAC地址其實(shí)我們的電腦就可以開(kāi)始通信了. 但是. 此時(shí)的通信方式是廣播. 相當(dāng)于通信基本靠吼. 你發(fā)送一個(gè)數(shù)據(jù)出去. 會(huì)自動(dòng)的發(fā)給當(dāng)前網(wǎng)絡(luò)下的所有計(jì)算機(jī). 然后每個(gè)計(jì)算機(jī)的網(wǎng)卡會(huì)看一眼這個(gè)數(shù)據(jù)是不是發(fā)給自己的. 像這樣的通信方式, 如果計(jì)算機(jī)的數(shù)據(jù)量少. 是沒(méi)有問(wèn)題的. 但是. 如果全球所有計(jì)算機(jī)都按照這樣的方式來(lái)傳輸消息. 那不僅是效率的問(wèn)題了. 絕對(duì)是災(zāi)難性的. 那怎么辦. 大家就想到了一個(gè)新的方案, 這個(gè)方案叫IP協(xié)議. 使用IP協(xié)議就把不同區(qū)域的計(jì)算機(jī)劃分成一個(gè)一個(gè)的子網(wǎng). 子網(wǎng)內(nèi)的通信使用廣播來(lái)傳遞消息. 廣播外通過(guò)路由進(jìn)行傳遞消息. 你可以理解為不同快遞公司的分撥中心. 我給你郵寄一個(gè)快遞. 先看一下是不是自己區(qū)域的. 是自?己區(qū)域直接挨家挨戶找就OK了. 但是如果不是我這個(gè)區(qū)域的. 就通過(guò)分撥中心(路由器網(wǎng)關(guān))找到你所在的區(qū)域的分撥中心(路由?網(wǎng)關(guān)), 再通過(guò)你的分撥中心下發(fā)給你. 這里IP協(xié)議的作用就體現(xiàn)出來(lái)了. 專(zhuān)門(mén)用來(lái)劃分子網(wǎng)的.那么在傳輸數(shù)據(jù)的時(shí)候就必須要把對(duì)方的ip地址帶著. 有了這個(gè)ip再加上子網(wǎng)掩碼就可以判斷出該數(shù)據(jù)到底是屬于哪個(gè)子網(wǎng)下的數(shù)據(jù).
IP地址: 由4位點(diǎn)分?十進(jìn)制表示. 每位最?大255. 故IP地址的范圍: 0.0.0.0~255.255.255.255. 為什什么是255, 答:
28 每一位用8位2進(jìn)制表示, 合起來(lái)32位就可以表示一個(gè)計(jì)算機(jī)的ip地址
子網(wǎng)掩碼: 用來(lái)劃分子網(wǎng)的一個(gè)4位點(diǎn)分十進(jìn)制.
網(wǎng)關(guān): 路由?在子網(wǎng)內(nèi)的ip. 不同局域網(wǎng)進(jìn)行數(shù)據(jù)傳輸?shù)慕涌?分撥中心)
計(jì)算子網(wǎng)的過(guò)程:

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 這兩個(gè)IP就是同一個(gè)子網(wǎng)

傳輸層: 我們現(xiàn)在解決了外界的數(shù)據(jù)傳輸問(wèn)題. 使用MAC地址和IP地址可以唯一的定位到一臺(tái)計(jì)算機(jī)了. 那么還有一個(gè)問(wèn)題沒(méi)有解決. 我們知道一臺(tái)計(jì)算機(jī)內(nèi)是很有可能運(yùn)行著多個(gè)網(wǎng)絡(luò)應(yīng)用程序的. 比如, 你開(kāi)著LOL, 掛著DNF, 聊著QQ, 還看著快播. 那么此時(shí)你的計(jì)算機(jī)網(wǎng)卡接收到了來(lái)自遠(yuǎn)方的一條數(shù)據(jù). 那么這一條數(shù)據(jù)到底給那個(gè)應(yīng)用呢? 說(shuō)白了, 快遞送到你公司了. 地址沒(méi)毛病了. 可是你公司那么多人. 這個(gè)快遞到底給誰(shuí)? 不能亂給啊. 怎么辦呢? 互聯(lián)網(wǎng)大佬們想到了一個(gè)新詞叫端口.
傳輸層規(guī)定: 給每?一個(gè)應(yīng)?用程序分配一個(gè)唯一的端口號(hào). 當(dāng)有數(shù)據(jù)發(fā)送過(guò)來(lái)之后. 通過(guò)端口號(hào)來(lái)決定該數(shù)據(jù)發(fā)送的具體應(yīng)用程序.但是根據(jù)不同應(yīng)用程序?qū)W(wǎng)絡(luò)的需求的不同(有的要求快, 有的要求可靠) 又把傳輸層劃分成兩個(gè)協(xié)議. 一個(gè)叫TCP, 一個(gè)叫UDP. 所以, 我們常說(shuō)的TCP/IP協(xié)議中最重要, 也是我們最關(guān)注的其實(shí)就是IP和端口了了. 因?yàn)橛辛诉@兩個(gè), 我們其實(shí)就可以定位到某一臺(tái)計(jì)算機(jī)上的某個(gè)網(wǎng)絡(luò)應(yīng)用程序了了. 也就可以給他發(fā)送消息了
32位計(jì)算機(jī)上的端口數(shù):
TCP : 65536個(gè)
UDP: 65536個(gè)
TCP和UDP的區(qū)別:

  1. TCP, 它是基于連接的. 是連續(xù)的, 可靠的. 效率比較低. 更像是打電話. 聊天的過(guò)程中不能中斷.
  2. UDP, 它不是基于連接的. 是不連續(xù)的, 不可靠的. 效率比較高. 更像是寄信, 今兒一封, 明兒一封. 想啥時(shí)候發(fā)就啥時(shí)候發(fā).
    應(yīng)用層: TCP+IP可以定位到計(jì)算機(jī)上的某個(gè)應(yīng)用了了. 但是不同用傳輸?shù)臄?shù)據(jù)格式可能是不一樣的. 就好比快遞. 有的是大包裹. 有的是小文件. 一個(gè)要用大快遞袋裝, 一個(gè)要用小快遞袋裝. 到了了應(yīng)用層. 我們一般是根據(jù)不同類(lèi)型的應(yīng)用程序進(jìn)行的再一次封裝. 比如, HTTP協(xié)議, SMTP協(xié)議, FTP協(xié)議. 等等.

2.初識(shí)Socket-TCP編程
在幾乎所有的編程語(yǔ)言中, 我們?cè)诰帉?xiě)網(wǎng)絡(luò)程序的時(shí)候都要使用到socket. socket翻譯過(guò)來(lái)叫套接字. 我們上面也了解到了一次網(wǎng)絡(luò)通信的數(shù)據(jù)需要包裹著mac, ip, port等信息. 但是如果每次我們開(kāi)發(fā)都要程序員去?個(gè)一個(gè)的去準(zhǔn)備數(shù)據(jù), 那工作量絕對(duì)是絕望的. 所以, 計(jì)算機(jī)提出了了socket. socket幫助我們完成了網(wǎng)絡(luò)通信中的絕大多數(shù)操作. 我們只需要告訴socket. 我要向哪臺(tái)計(jì)算機(jī)(ip, port)發(fā)送數(shù)據(jù). 剩下的所有東西都由socket幫我們完成. 所以使用socket完成數(shù)據(jù)傳輸是非常方便的.
話不多說(shuō),練起來(lái)吧~~

server端:

import socket

#創(chuàng)建socket通道
sk = socket.socket()
sk.bind(("127.0.0.1", 8088))  # 綁定ip和端口
sk.listen()  # 開(kāi)始監(jiān)聽(tīng)
print(">>").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.初識(shí)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不用長(zhǎng)時(shí)間建立連接,發(fā)送一個(gè)包,不用等回復(fù),也不需要理會(huì)對(duì)方收到還是沒(méi)收到,沒(méi)有嚴(yán)格意義上的服務(wù)端和客戶端


4.黏包現(xiàn)象
在使用TCP協(xié)議進(jìn)行數(shù)據(jù)傳輸?shù)臅r(shí)候, 會(huì)有以下問(wèn)題出現(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()  # 開(kāi)始監(jiān)聽(tīng)
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)容:
初識(shí)python網(wǎng)絡(luò)編程-01
可以看到,兩次發(fā)送的內(nèi)容,黏在一起,變成了一個(gè)包,就是典型的黏包現(xiàn)象。
那么如何解決黏包問(wèn)題呢? 很簡(jiǎn)單. 之所以出現(xiàn)黏包就是因?yàn)閿?shù)據(jù)沒(méi)有邊界. 直接把兩個(gè)包混合成了一個(gè)包. 那么我可以在發(fā)送數(shù)據(jù)的時(shí)候. 指定邊界. 告訴對(duì)方. 我接下來(lái)這個(gè)數(shù)據(jù)包有多大. 對(duì)面接收數(shù)據(jù)的時(shí)候呢, 先讀取該數(shù)據(jù)包的大小.然后再讀取數(shù)據(jù). 就不會(huì)產(chǎn)生黏包了.
普通話: 發(fā)送數(shù)據(jù)的時(shí)候制定數(shù)據(jù)的格式: 長(zhǎng)度+數(shù)據(jù) 接收的時(shí)候就知道有多少是當(dāng)前這個(gè)數(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"))
# 計(jì)算數(shù)據(jù)長(zhǎng)度.格式化成四位數(shù)字
bs_len = format(len(bs), "04d").encode("utf-8")
# 發(fā)送數(shù)據(jù)之前,先發(fā)送數(shù)據(jù)的長(zhǎng)度
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()  # 開(kāi)始監(jiān)聽(tīng)
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept()  # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address)  # address是客戶端的ip和端口

# 接收數(shù)據(jù)長(zhǎng)度4個(gè)字節(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ù)長(zhǎng)度,讀取數(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然能解決黏包問(wèn)題,但是每次接收都要先定義接收的長(zhǎng)度,喵的豈不是太累了??這個(gè)問(wèn)題,python也有自己的態(tài)度,代碼堅(jiān)決要給你搞簡(jiǎn)單,就引入了一個(gè)新的模塊struct,這是重點(diǎn),拿小本本記下來(lái),要考?。?br/>展示一下struct的用法:

import struct

ret = struct.pack("i", 123456789)  # 把數(shù)字打包成字節(jié)
print(ret)

print(len(ret))  # 4  不論數(shù)字大小,定死了4個(gè)字節(jié)

# 把字節(jié)還原回?cái)?shù)字
ds = b'\x15\xcd[\x07'
num = struct.unpack("i", ds)[0]   # num返回的是一個(gè)元祖,取索引為0的元素,也就是第一個(gè)元素,就是我們傳輸?shù)臄?shù)字
print(num)   # 123456789

好的,回歸到黏包的問(wèn)題上來(lái),論如何優(yōu)雅的解決黏包的問(wèn)題:
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()  # 開(kāi)始監(jiān)聽(tīng)
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("接收完畢")
# 接收第二個(gè)包
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模塊,封裝起來(lái),需要用到的時(shí)候,就導(dǎo)入這個(gè)自定義的模塊:
封裝的模塊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()  # 開(kāi)始監(jiān)聽(tīng)
print("服務(wù)器就緒,等待連接")
conn, address = sk.accept()  # 程序阻塞,等待連接
print("有客戶端連接,ip是:", address)  # address是客戶端的ip和端口

msu.my_recv(conn)
msu.my_recv(conn)
sk.close()

這樣,是不是就簡(jiǎn)便了很多呢~~


網(wǎng)站標(biāo)題:初識(shí)python網(wǎng)絡(luò)編程-01
網(wǎng)頁(yè)鏈接:
http://weahome.cn/article/gggide.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部