Python-100days-14

Day14 - 網(wǎng)絡(luò)編程入門

計算機基礎(chǔ)

mark一本《計算機網(wǎng)絡(luò)》書籍

計算機網(wǎng)絡(luò)發(fā)展史

TCP/IP模型

協(xié)議畦攘,就是通信計算機雙方必須共同遵守的一組約定集绰,網(wǎng)絡(luò)協(xié)議三要素:語法错妖、語義、時序疚沐。協(xié)議族暂氯,就是一系列的協(xié)議及其構(gòu)成的通信模型,我們通常把這套東西成為TCP/IP模型亮蛔。與國際標準化組織發(fā)布的OSI/RM七層模型不同痴施,TCP/IP只是一個四層模型,自下而上依次是:網(wǎng)絡(luò)接口層究流、網(wǎng)絡(luò)層辣吃、傳輸層、應用層芬探。


image.png

IP神得,常被翻譯為網(wǎng)際協(xié)議,它服務(wù)于網(wǎng)絡(luò)層偷仿,主要實現(xiàn)尋址和路由的功能哩簿。接入網(wǎng)絡(luò)的每一臺及其都要有自己的IP地址,IP地址就是主機在計算機網(wǎng)絡(luò)上的身份標識酝静。由于IPv4地址匱乏卡骂,我們平常使用IP地址并不是全球唯一的IP地址,而是一個局域網(wǎng)中的內(nèi)部IP地址形入,通過網(wǎng)絡(luò)地址轉(zhuǎn)換服務(wù)我們也可以實現(xiàn)對網(wǎng)絡(luò)的訪問。計算機網(wǎng)絡(luò)有大量被我們稱為“路由器”的中繼設(shè)備缝左,它們會存儲轉(zhuǎn)發(fā)我們發(fā)送到網(wǎng)絡(luò)上的數(shù)據(jù)分組亿遂,讓從源頭發(fā)出的數(shù)據(jù)最終能夠找到傳送到的目的地通路,這項功能就是所謂的路由渺杉。
TCP全稱是傳輸控制協(xié)議蛇数,它是基于IP提供的尋址和路由服務(wù)而建立起來的負責實現(xiàn)端到端可靠傳輸?shù)膮f(xié)議,之所以將TCP成為可靠的傳輸協(xié)議是因為TCP向調(diào)用者承諾了三件事:

  • 數(shù)據(jù)不傳丟不傳錯(利用握手是越,校驗耳舅,重傳機制)
  • 流量控制(通過窗口活動匹配數(shù)據(jù)發(fā)送者和接受者之間的傳輸速度)
  • 擁塞控制(通過RTT時間以及對滑動窗口的控制緩解網(wǎng)絡(luò)擁堵)
網(wǎng)絡(luò)應用模式
  • C/S和B/S模式,即通過客戶端到服務(wù)器倚评,或者瀏覽器到服務(wù)器浦徊,進行網(wǎng)絡(luò)訪問。
  • 去中心化的網(wǎng)絡(luò)應用模式天梧。意思就是去中心化的網(wǎng)絡(luò)應用通常沒有固定的服務(wù)器或者固定的客戶端盔性,所有應用的使用者既可以作為資源的提供者也可以作為資源的訪問者。

基于HTTP協(xié)議的網(wǎng)絡(luò)資源訪問

HTTP超文本傳輸協(xié)議

HTTP是一種用于分布式呢岗、協(xié)作式和超媒體信息系統(tǒng)的應用層協(xié)議冕香,它是萬維網(wǎng)數(shù)據(jù)通信的基礎(chǔ)蛹尝,設(shè)計HTTP最初目的是為了提供一種發(fā)布和接受HTML頁面的方法,通過HTTP或者HTTPS(超文本傳輸安全協(xié)議)請求的資源由URL(統(tǒng)一資源標識符)來標識悉尾。

ps.有空翻翻突那,Mark一篇博客《HTTP協(xié)議入門》
JSON格式

json是一種輕量級的數(shù)據(jù)交換語言,為了讓人容易閱讀的基礎(chǔ)上构眯,用來傳輸由屬性值或者序列性的值組成的數(shù)據(jù)對象愕难。

requests庫

requests 是一個基于HTTP協(xié)議來使用網(wǎng)絡(luò)的第三方庫,它可以很方便使用HTTP鸵赖,避免安全缺陷务漩、冗余代碼以及重復造輪子。
敲一個例子它褪,下載圖片

from time import time
from threading import Thread
import requests

class DownloadHanlder(Thread):

    def __init__(self, url):
        super().__init__()
        self.url = url

    def run(self):
        # rfind 返回字符串最后一次出現(xiàn)的位置饵骨,從右向左查詢,沒有匹配到則返回-1
        filename = self.url[self.url.rfind('/') + 1:]
        print(filename)
        resp = requests.get(self.url)
        with open('data/' + filename, 'wb') as f:
            f.write(resp.content)

def main():
    resp = requests.get(
        'http://api.tianapi.com/meinv/?key=19f7261742115ad6d5656318d2c4b778&num=10')

    data_model = resp.json()
    for mm_dict in data_model['newslist']:
        url = mm_dict['picUrl']
        DownloadHanlder(url).start()

if __name__ == '__main__':
    main()

基于傳輸層協(xié)議的套接字編程

套接字就是一套用C語言攜程的應用程序開發(fā)庫茫打,主要用于實現(xiàn)進程間的通信和網(wǎng)絡(luò)編程居触,在網(wǎng)絡(luò)應用開發(fā)中被廣泛使用。在Python中也可以基于套接字來使用傳輸層提供的傳輸服務(wù)老赤,并基于此開發(fā)自己的網(wǎng)絡(luò)應用轮洋。實際開發(fā)中使用的套接字分三類:流套接字(TCP套接字)、數(shù)據(jù)報套接字和原始套接字抬旺。

TCP套接字

TCP套接字就是使用TCP協(xié)議提供的傳輸服務(wù)來實現(xiàn)網(wǎng)絡(luò)通信的編程接口弊予。在Python中可通過創(chuàng)建socket對象并指定type屬性為SOCK_STREAM來使用TCP套接字。由于一臺主機可有多個IP地址开财,而且很可能會配置多個不同服務(wù)汉柒,所以作為服務(wù)器端的程序,需要在創(chuàng)建套接字對象后將其綁定到指定的IP地址和端口上责鳍。
實現(xiàn)一個提供時間日期的服務(wù)器(注釋代碼很詳細就不刪掉了)

from socket import socket, SOCK_STREAM, AF_INET
from datetime import datetime

def main():
    # 1.創(chuàng)建套接字對象并指定使用哪種傳輸服務(wù)
    # family=AF_INET - IPv4地址
    # family=AF_INET6 - IPv6地址
    # type=SOCK_STREAM - TCP套接字
    # type=SOCK_DGRAM - UDP套接字
    # type=SOCK_RAW - 原始套接字
    server = socket(family=AF_INET, type=SOCK_STREAM)
    # 2.綁定IP地址和端口(端口用于區(qū)分不同的服務(wù))
    # 同一時間在同一個端口上只能綁定一個服務(wù)否則報錯
    server.bind(('192.168.1.2', 6789))
    # 3.開啟監(jiān)聽 - 監(jiān)聽客戶端連接到服務(wù)器
    # 參數(shù)512可以理解為連接隊列的大小
    server.listen(512)
    print('服務(wù)器啟動開始監(jiān)聽...')
    while True:
        # 4.通過循環(huán)接收客戶端的連接并作出相應的處理(提供服務(wù))
        # accept方法是一個阻塞方法如果沒有客戶端連接到服務(wù)器代碼不會向下執(zhí)行
        # accept方法返回一個元組其中的第一個元素是客戶端對象
        # 第二個元素是連接到服務(wù)器的客戶端的地址(由IP和端口兩部分構(gòu)成)
        client, addr = server.accept()
        print(str(addr) + '連接到了服務(wù)器.')
        # 5.發(fā)送數(shù)據(jù)
        client.send(str(datetime.now()).encode('utf-8'))
        # 6.斷開連接
        client.close()

if __name__ == '__main__':
    main()

實現(xiàn)客戶端

from socket import socket

def main():
    # 1.創(chuàng)建套接字對象默認使用IPv4和TCP協(xié)議
    client = socket()
    # 2.連接到服務(wù)器(需要指定IP地址和端口)
    client.connect(('192.168.1.2', 6789))
    # 3.從服務(wù)器接收數(shù)據(jù)
    print(client.recv(1024).decode('utf-8'))
    client.close()

if __name__ == '__main__':
    main()

上述例子沒有使用多線程或者異步I/O的處理方式碾褂,只能一個一個客戶執(zhí)行。
下面設(shè)計一個多線程處理多用戶請求的服務(wù)器历葛,服務(wù)器向用戶發(fā)送一張圖片正塌。

from socket import socket, SOCK_STREAM, AF_INET
from base64 import b64encode
from json import dumps
from threading import Thread


def main():
    # 自定義線程類
    class FileTransferHandler(Thread):

        def __init__(self, cclient):
            super().__init__()
            self.cclient = cclient

        def run(self):
            my_dict = {}
            my_dict['filename'] = '131.jpg'
            # JSON是純文本不能攜帶二進制數(shù)據(jù)
            # 所以圖片的二進制數(shù)據(jù)要處理成base64編碼
            my_dict['filedata'] = data
            # 通過dumps函數(shù)將字典處理成JSON字符串
            json_str = dumps(my_dict)
            # 發(fā)送JSON字符串
            self.cclient.send(json_str.encode('utf-8'))
            self.cclient.close()

    # 1.創(chuàng)建套接字對象并指定使用哪種傳輸服務(wù)
    server = socket()
    # 2.綁定IP地址和端口(區(qū)分不同的服務(wù))
    server.bind(('127.0.0.1', 5566))
    # 3.開啟監(jiān)聽 - 監(jiān)聽客戶端連接到服務(wù)器
    server.listen(512)
    print('服務(wù)器啟動開始監(jiān)聽...')
    with open('131.jpg', 'rb') as f:
        # 將二進制數(shù)據(jù)處理成base64再解碼成字符串
        data = b64encode(f.read()).decode('utf-8')
    while True:
        client, addr = server.accept()
        # 啟動一個線程來處理客戶端的請求
        FileTransferHandler(client).start()

if __name__ == '__main__':
    main()

客戶端

from socket import socket
from json import loads
from base64 import b64decode

def main():
    client = socket()
    client.connect(('127.0.0.1', 5566))
    # 定義一個保存二進制數(shù)據(jù)的對象
    in_data = bytes()
    # 由于不知道服務(wù)器發(fā)送的數(shù)據(jù)有多大每次接收1024字節(jié)
    data = client.recv(1024)
    while data:
        # 將收到的數(shù)據(jù)拼接起來
        in_data += data
        data = client.recv(1024)
    # 將收到的二進制數(shù)據(jù)解碼成JSON字符串并轉(zhuǎn)換成字典
    # loads函數(shù)的作用就是將JSON字符串轉(zhuǎn)成字典對象
    my_dict = loads(in_data.decode('utf-8'))
    filename = my_dict['filename']
    filedata = my_dict['filedata'].encode('utf-8')
    with open('data/' + filename, 'wb') as f:
        # 將base64格式的數(shù)據(jù)解碼成二進制數(shù)據(jù)并寫入文件
        f.write(b64decode(filedata))
    print('圖片已保存.')

if __name__ == '__main__':
    main()

地址改成127.0.0.1,否則會報下面的錯誤恤溶。沒搞清楚為啥乓诽。

OSError: [WinError 10049] 在其上下文中,該請求的地址無效咒程。
ps.JSON并不能攜帶二進制數(shù)據(jù)问裕,因此對圖片的二進制數(shù)據(jù)進行了Base64編碼的處理。Base64是一種用64個字符表示所有二進制數(shù)據(jù)的編碼方式孵坚,通過將二進制數(shù)據(jù)每6位一組的方式重新組織粮宛,剛好可以使用0~9的數(shù)字窥淆、大小寫字母以及“+”和“/”總共64個字符表示從000000到111111的64種狀態(tài)。
UDP套接字

UDP巍杈,即用戶數(shù)據(jù)報協(xié)議忧饭。TCP和UDP都是提供端到端傳輸服務(wù)的協(xié)議。UDP不對傳輸?shù)目煽啃院涂蛇_性做出任何承諾從而避免了TCP中握手和重傳的開銷筷畦,所以在強調(diào)性能和不要求數(shù)據(jù)完整性的場景中(例如傳輸網(wǎng)絡(luò)音視頻數(shù)據(jù))词裤,UDP可能是更好的選擇。

網(wǎng)絡(luò)應用開發(fā)

發(fā)送電子郵件

發(fā)送郵件使用的是SMTP(簡單郵件傳輸協(xié)議)鳖宾,SMTP是建立在TCP提供的可靠數(shù)據(jù)傳輸服務(wù)的基礎(chǔ)上的應用級協(xié)議吼砂,它規(guī)定郵件的發(fā)送者如何跟發(fā)送郵件的服務(wù)器進行通信的細節(jié),而Python中的smtplib模塊將這些操作簡化成幾個函數(shù)鼎文。
利用Python發(fā)送郵件渔肩,可以參考菜鳥教程的文章很詳細。《Python SMTP發(fā)送郵件》

發(fā)送短信

發(fā)送短信拇惋,其實就是看使用的短信平臺提供的API周偎,根據(jù)API寫代碼就完事了。
mark一下示例撑帖,用到的時候會寫就完事了蓉坎。

import urllib.parse
import http.client
import json

def main():
    host  = "106.ihuyi.com"
    sms_send_uri = "/webservice/sms.php?method=Submit"
    # 下面的參數(shù)需要填入自己注冊的賬號和對應的密碼
    params = urllib.parse.urlencode({'account': '你自己的賬號', 'password' : '你自己的密碼', 'content': '您的驗證碼是:147258。請不要把驗證碼泄露給其他人胡嘿。', 'mobile': '接收者的手機號', 'format':'json' })
    print(params)
    headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'}
    conn = http.client.HTTPConnection(host, port=80, timeout=30)
    conn.request('POST', sms_send_uri, params, headers)
    response = conn.getresponse()
    response_str = response.read()
    jsonstr = response_str.decode('utf-8')
    print(json.loads(jsonstr))
    conn.close()

if __name__ == '__main__':
    main()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛉艾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子衷敌,更是在濱河造成了極大的恐慌勿侯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逢享,死亡現(xiàn)場離奇詭異,居然都是意外死亡吴藻,警方通過查閱死者的電腦和手機瞒爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沟堡,“玉大人侧但,你說我怎么就攤上這事『铰蓿” “怎么了禀横?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粥血。 經(jīng)常有香客問我柏锄,道長酿箭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任趾娃,我火速辦了婚禮缭嫡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抬闷。我一直安慰自己妇蛀,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布笤成。 她就那樣靜靜地躺著评架,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炕泳。 梳的紋絲不亂的頭發(fā)上纵诞,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音喊崖,去河邊找鬼挣磨。 笑死,一個胖子當著我的面吹牛荤懂,可吹牛的內(nèi)容都是我干的茁裙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼节仿,長吁一口氣:“原來是場噩夢啊……” “哼晤锥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起廊宪,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矾瘾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后箭启,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壕翩,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年傅寡,在試婚紗的時候發(fā)現(xiàn)自己被綠了放妈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荐操,死狀恐怖芜抒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情托启,我是刑警寧澤宅倒,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站屯耸,受9級特大地震影響拐迁,放射性物質(zhì)發(fā)生泄漏蹭劈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一唠亚、第九天 我趴在偏房一處隱蔽的房頂上張望链方。 院中可真熱鬧,春花似錦灶搜、人聲如沸祟蚀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽前酿。三九已至,卻和暖如春鹏溯,著一層夾襖步出監(jiān)牢的瞬間罢维,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工丙挽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肺孵,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓颜阐,卻偏偏與公主長得像平窘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凳怨,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容