Head First設計模式——命令模式

前言:命令模式我們平常可能會經常使用,如果我們不了解命令模式的結構和定義那麼在使用的時候也不會將它對號入座。

舉個例子:在winform開發的時候我們常常要用同一個界面來進行文件的下載,但是並不是所有地方都用同一個下載邏輯處理文件,然後下載界面卻可以是同一個界面。

為了以後復用下載界面(下載显示,進度條等)我們常常將下載執行操作定義成一個接口,在具體使用的時候實現接口,將具體執行對象設置到下載界面。當下載按鈕被按下的時候,就調用設置的具體執行對象(接收者)來執行下載的處理。

那接下來我們就看下命令模式的具體細節和實現,再回頭想想我們平時什麼時候不經意就使用到了命令模式,這樣以後交流使用專業的術語不僅能裝還能用。

1、遙控器應用場景

HeadFirst設計模式一書中以遙控器為例實現命令模式,以餐館點餐講解命令模式的對象和結構。為了邏輯清晰我們不混合兩種講解方式,只以遙控器為例講解。

現在需求是有一個遙控器,遙控器上面有控制各種電器的開關,而開關的執行控制電器是由各個廠家開發的設備(對象)插入到對應開關位置的卡槽裏面,基於這些條件我們來實現遙控器系統。

簡單粗暴的解決方案可以對開關做一個標識,當某個開關被按下時根據開關類型進行if判斷。形如 if slot1==Light ,then light.on(), else if slot1==Tv then tv.on() 這種代碼將出現一堆,對於以後增加減少開關或者更換開關都是比較糟糕的。而對於設計遙控器類來說我們應該讓遙控器代碼盡量保持簡單,而不用去關心具體廠商類怎麼執行。所以我們應該將執行封裝在一個命令對象里中,那麼我們就試着一步步實現遙控器。

  首先我們為命令對象定義一個統一的接。

  接口只有一個簡單的execute執行命令方法。

    public interface Command
    {
        //執行命令的方法
        public void execute();
    }

  接下來我們實現一個打開電燈的命令

    public class Light
    {
        public void on() {
            Console.WriteLine("打開電燈");
        }

        public void off()
        {
            Console.WriteLine("關閉電燈");
        }
    }

    public class LightOnCommand : Command
    {
        Light light;

        public LightOnCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.on();
        }
    }

  為了簡單我們假設遙控器只有一個開關,實現遙控器。

    public class SimpleRemoteControl
    {
        //卡槽
        Command slot;

        public void setCommand(Command command)
        {
            slot = command;
        }

        //按下開關
        public void ButtonWasPressed() {
            slot.execute();
        }

    }

  測試

     static void Main(string[] args)
        {
            SimpleRemoteControl remoteControl = new SimpleRemoteControl();
            //廠商提供的電燈類,命令的接收者
            Light light = new Light();

            //我們封裝的命令對象,設置接收者
            LightOnCommand lightOnCommand = new LightOnCommand(light);

            //設置遙控器開關對應的命令對象
            remoteControl.setCommand(lightOnCommand);
            remoteControl.ButtonWasPressed();
            Console.ReadKey();
        }

  

2、命令模式、類圖

通過上面的例子我們已經使用了命令模式來實現一個簡單的遙控器,再回顧【前言】我們說的界面下載文件按鈕操作是不是就是一個典型的可以使用命令模式的應用場景。

只是有一點我們可能不會有什麼其他廠商設計好的執行類,我們也許直接就在繼承接口的命令對象中實現execute的邏輯,而不用再調用其他接收者執行。

這就是“聰明”命令對象,上面我們實現的是“傻瓜”命令對象。這個稍後再說,我們先看命令模式定義和畫出類圖。

命令模式:將“請求”封裝成對象,以便使用不同的請求、隊列或日誌來參數化其他對象。命令模式也支持撤銷的操作。

3、完成多開關遙控器和撤銷操作

假設遙控器現在有五個開關。我們已經有簡單遙控器的經驗,那麼其他4個開關我們也將對應的命令對象設置上去就行了。定義兩個數組用來記錄開關對應的命令對象。

    public class RemoteControl
    {
        Command[] onCommands;
        Command[] offCommands;
        public RemoteControl()
        {
            onCommands = new Command[5];
            offCommands = new Command[5];
            Command noCommand = new NoCommand();
            for (int i = 0; i < 5; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        public void setCommand(int slot,Command commandOn, Command commandOff)
        {
            onCommands[slot] = commandOn;
            offCommands[slot] = commandOff;
        }

        //按下開關
        public void OnButtonWasPressed(int slot)
        {
            onCommands[slot].execute();
        }
        //關閉開關
        public void OffButtonWasPressed(int slot)
        {
            offCommands[slot].execute();
        }

        //打印出數組命令對象
        public override string ToString() {
            var sb = new StringBuilder("\n------------Remote Control-----------\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n");
            }
            return sb.ToString();
        }

    }

  在遙控器中我們定義了一個Nocommand類,是為了對遙控器對應的開關初始化命令對象,避免為空報錯或者消除開關調用命令對象時檢查對象是否為空的判斷。 

     public void OnButtonWasPressed(int slot)
        {
            if(onCommand[slot]!=null))
                onCommands[slot].execute();
        }

  在許多設計模式中我們都能看到這種初始值或者空對象的使用。甚至有時候,空對象本身也被視為一種設計模式。(感覺這樣代碼比較優雅O(∩_∩)O)

遙控器完成了,我們還有做一項工作,就是撤銷操作。

撤銷操作我們同樣在命令接口裡面定義一個undo 方法。

    public interface Command
    {
        //執行命令的方法
        public void execute();
        //撤銷命令方法
        public void undo();
    }

  然後我們讓LightOnCommand實現undo方法,添加LightOffCommand命令對象。

    public class LightOnCommand : Command
    {
        Light light;

        public LightOnCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.on();
        }
        public void undo() {
            light.off();
        }
    }


    class LightOffCommand : Command
    {
        Light light;

        public LightOffCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.off();
        }

        public void undo()
        {
            light.on();
        }
    }

 遙控器裏面添加撤銷按鈕操作UndoButtonWasPressed並用undoCommand屬性存儲上一次操作。

    public class RemoteControl
    {
        Command[] onCommands;
        Command[] offCommands;
        Command undoCommand;
        public RemoteControl()
        {
            onCommands = new Command[5];
            offCommands = new Command[5];
            Command noCommand = new NoCommand();
            for (int i = 0; i < 5; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        public void setCommand(int slot,Command commandOn, Command commandOff)
        {
            onCommands[slot] = commandOn;
            offCommands[slot] = commandOff;
        }

        //按下開關
        public void OnButtonWasPressed(int slot)
        {
            onCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
        //關閉開關
        public void OffButtonWasPressed(int slot)
        {
            offCommands[slot].execute();
            undoCommand = offCommands[slot];
        }

        public void UndoButtonWasPressed() {
            undoCommand.undo();
        }
        //打印出數組命令對象
        public override string ToString() {
            var sb = new StringBuilder("\n------------Remote Control-----------\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n");
            }
            return sb.ToString();
        }

    }

測試:

4、補充總結

補充:

①命令模式的接收者不一定要存在,之前提到過“聰明”和“傻瓜”命令對象,如果以“聰明”命令對象設計,調用者和接收者之間解耦程度比不上“傻瓜”命令對象,但是我們在使用比較簡單的時候仍然可以使用“聰明”命令對象設計。

②撤銷例子我們只做了返回最後一次操作,如果要撤銷許多次我們可以對操作記錄進行保存到堆棧,不管什麼時候撤銷,我們都可以從堆棧中取出最上層命令對象執行撤銷操作。

命令模式常被用於隊列請求,日誌請求。當隊列按照順序取到存放的命令對象后調用執行方法就行了而不用去管具體執行什麼。

日誌請求在某些場合可以用來將所有動作記錄在日誌中,並能在系統死機后通過日誌記錄進行恢復到之前的狀態(撤銷)。對於更高級的的應用而言,這些技巧可以應用到事務(transaction)處理中。

 通過簡單到更進一步的實現講解了命令模式和一些靈活點和需要注意的點,有什麼理解不到位的歡迎指正。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

Asciinema文章勘誤及Web端使用介紹

欠下的債遲早是要還的,查文檔,重驗證,出結果,不誤導

文章勘誤

在上一篇文章中有兩個地方表述有錯誤或瑕疵,這裏更正一下

第一個地方為錄製時的參數--stdin,參數的意思是啟用標準輸入錄製,原文中說看不到效果,可能官方還未支持,實際上官方已經支持了,且查看錄製文件內容時可以看到區別,以下兩個對比的例子來說明

例一:執行下方的命令進行錄製,錄製開始之後執行ssh命令輸入密碼連接另一台主機

asciinema rec ops-coffee.cast

執行asciinema cat命令查看執行命令

# asciinema cat ops-coffee.cast 
root@onlinegame:~# ssh root@192.168.106.192 ls ops-coffee.cn
root@192.168.106.192's password: 
ops-coffee.cn
root@onlinegame:~# exit
exit

打印錄製的文件內容如下:

# cat ops-coffee.cast 
{"version": 2, "width": 237, "height": 55, "timestamp": 1574060513, "env": {"SHELL": "/bin/bash", "TERM": "linux"}}
[0.012221, "o", "root@onlinegame:~# "]
[0.607184, "o", "exit"]
[1.07092, "o", "\b\b\b\bssh root@192.168.106.192 ls ops-coffee.cn"]
[1.703405, "o", "\r\n"]
[1.762974, "o", "root@192.168.106.192's password: "]
[4.550759, "o", "\r\n"]
[4.558138, "o", "ops-coffee.cn\r\n"]
[4.559187, "o", "root@onlinegame:~# "]
[5.182817, "o", "e"]
[5.582643, "o", "x"]
[5.838648, "o", "i"]
[6.03067, "o", "t"]
[6.759346, "o", "\r\nexit\r\n"]

例二:執行同樣的命令,加上--stdin參數

asciinema rec --stdin ops-coffee.1.cast

執行asciinema cat命令查看執行命令

# asciinema cat ops-coffee.1.cast 
root@onlinegame:~# ssh root@192.168.106.192 ls ops-coffee.cn
root@192.168.106.192's password: 
ops-coffee.cn
root@onlinegame:~# exit
exit

這次再看錄製文件的內容:

# cat ops-coffee.1.cast
{"version": 2, "width": 237, "height": 55, "timestamp": 1574060808, "env": {"SHELL": "/bin/bash", "TERM": "linux"}}
[0.01012, "o", "root@onlinegame:~# "]
[1.654752, "i", "\u001b[A"]
[1.654971, "o", "exit"]
[2.014568, "i", "\u001b[A"]
[2.014727, "o", "\b\b\b\bssh root@192.168.106.192 ls ops-coffee.cn"]
[3.7185, "i", "\r"]
[3.719167, "o", "\r\n"]
[3.781231, "o", "root@192.168.106.192's password: "]
[5.198467, "i", "s"]
[5.542343, "i", "m"]
[5.774451, "i", "i"]
[5.85435, "i", "l"]
[5.990628, "i", "e"]
[6.342587, "i", "\r"]
[6.342817, "o", "\r\n"]
[6.351245, "o", "ops-coffee.cn\r\n"]
[6.351475, "o", "root@onlinegame:~# "]
[7.182384, "i", "e"]
[7.182585, "o", "e"]
[7.461976, "i", "x"]
[7.462183, "o", "x"]
[7.543019, "i", "i"]
[7.543306, "o", "i"]
[7.686868, "i", "t"]
[7.68703, "o", "t"]
[7.87045, "i", "\r"]
[7.871348, "o", "\r\nexit\r\n"]

會發現在實際執行命令完全一致的情況下,錄像文件與上一個沒有加--stdin時的不一樣,其中就多了輸入密碼的記錄smile

且在asciinema文件IO流信息的第二個字段不僅有了o,還有i的出現,上一篇文章講到o是一個固定字符串不知道作用,經過深入查詢確認,IO信息流的第二個字段就是固定string字符串,且只會是io之間的一種,分別表示stdin標準輸入或stdout標準輸出

--stdin的效果無論是通過asciinema play命令播放或是asciinema cat命令查看都是無法察覺的,在實現WebSSH錄像回放時又對錄像文件進行了深入研究,最終發現問題,這裏查漏補缺,予以更正,對於之前的錯誤,深表歉意

Web端使用

asciinema錄製文件在web端播放是通過asciinema-player組件來實現的,使用也是非常的簡單

分別引入css和js文件,添加一個asciinema-player的標籤即可播放標籤內文件的錄像

<html>
<head>
  ...
  <link rel="stylesheet" type="text/css" href="/asciinema-player.css" />
  ...
</head>
<body>
  ...
  <asciinema-player src="/ops-coffee.cast"></asciinema-player>
  ...
  <script src="/asciinema-player.js"></script>
</body>
</html>

asciinema-player標籤內可以添加如下一些屬性:

cols: 播放終端的列數,默認為80,如果cast文件的header頭有設置width,這裏無需設置

rows: 播放終端的行數,默認為24,如果cast文件的header頭有設置height,這裏無需設置

autoplay: 是否自動開始播放,默認不會自動播放

preload: 預加載,如果你想為錄像配音,這裏可以預加載聲音

loop: 是否循環播放,默認不循環

start-at: 從哪個地方開始播放,可以是123這樣的秒數或者是1:06這樣的時間點

speed: 播放的速度,類似於play命令播放時的-s參數

idle-time-limit: 最大空閑秒數,類似於play命令播放時的-i參數

poster: 播放之前的預覽,可以是npt:1:06這樣給定時間點的畫面,也可以是data:text/plain,ops-coffee.cn這樣給定的文字,其中文字支持ANSI編碼,例如可以給文字加上顏色data:text/plain,\x1b[1;32mops-coffee.cn\x1b[1;0m

font-size: 文字大小,可以是smallmediumbig或者直接是14px這樣的css樣式大小

theme: 終端顏色主題,默認是asciinema,也提供有tangosolarized-darksolarized-light或者monokai可選擇,當然你也可以自定義主題

還有幾個參數titleauthorauthor-urlauthor-img-url分別表示了錄像的標題、作者、作者的主頁、作者的頭像,這些配置會在全屏觀看錄像時显示在標題欄中,像下邊這樣

最後使用以下參數設置asciinema-player,看看播放的效果

<asciinema-player id="play" 
    title="WebSSH Record" 
    author="ops-coffee.cn" 
    author-url="https://ops-coffee.cn" 
    author-img-url="/static/img/logo.png" 
    src="/static/record/ops-coffee.cast" 
    speed="3" idle-time-limit="2" 
    poster="data:text/plain,\x1b[1;32m2019-11-18 16:26:18\x1b[1;0m用戶\x1b[1;32madmin\x1b[1;0m連接主機\x1b[1;32m192.168.106.101:22\x1b[1;0m的錄像記錄">
</asciinema-player>

播放效果如下

同時asciinema-player播放時還支持以下快捷鍵的使用

  • space 空格,播放或暫停
  • f 全屏播放,可以看到title等設置
  • / 快進或快退,每次5秒
  • 0,1,6 ... 9 跳轉到錄像的0%,10%,60% … 90%
  • < / > 增加或降低播放速度,play的-s參數

相關文章推薦閱讀:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

計算機網絡原理超詳解

計算機網絡重磅來襲——一文讓你撥開迷霧,直擊網絡原理

前言

為了保持學習網絡編程的連貫性和系統性,我在之前已經更新了《》《》,我只能說寫的確實很詳細,能讓你全盤吸收,不過你要沒看也不要緊,我會在此篇再來一個前情回顧,不會像上兩篇那樣詳細,但也能讓你理解。

一、前情回顧——計算機網絡概述

時代不同了,大家現在生活都好了,家家戶戶都有電腦了,而且我們都習慣了使用電腦辦公,打打遊戲,聊聊天。那我們來想一個事兒:如果沒有了網絡,我們是不是就不能使用電腦進行聊天了啊。那網絡到底是如何幫助我們來完成網絡聊天的?此篇我就跟大家繼續聊計算機網絡到底是怎麼回事兒。

這回我和以往反着來,先說官方定義,再解釋:

計算機網絡是由通信介質地理位置不同的且相互獨立的計算機連接起來,實現數據通信資源共享

我們知道兩個獨立且毫不相關的計算機,一台在青海,一台在河南,想要進行數據傳輸(聊天),沒網是不行的。這個網指的就是互聯網(Internet)。這個Internet它是一個通信協議。什麼是協議?打個比方,就好比我們打電話,在中國,中國有十幾億人,地大物博,全國各地都有自己的方言,還有些地方使用自己的民族語言,這時候想要良好的溝通就必須使用統一的一個標準,就是普通話。大家都講普通話,溝通起來就沒有問題了。那如果是不同國家進行溝通呢?我們可以選擇使用英語進行交流,那英語就是全世界國家通用的一個標準,計算機就好比是分佈在全世界各個角落的人,計算機之間通話也要找一個統一的標準,這個標準就是Internet標準,又叫做Internet協議。

二、網絡介紹及分類之隔壁老王的故事

先給大家講一個虛構版隔壁老王的故事:
有一個人叫隔壁老王,他有一個愛好就是看電影。有一天,這個隔壁老王想看一部電影,可是電腦裏面存儲的電影太多了,他費了老大勁才从里面找到,覺得很不爽。於是他想,我能不能把所有電影做個分類,把同一種類型的電影放在同一個文件夾下,然後把所有的文件夾整合在一起,自己寫個瀏覽器軟件,把文件夾信息放到瀏覽器上,到時候找電影的時候就好找了,只要找到相應的文件夾直接點進去就能找到電影了,老王說干就干,沒多久就把所有的文件夾整理好了,然後把所有文件夾放到同一個頁面上,到時候他想點哪個就點哪個,So easy!(那些網站可能就是這樣來的)。

這回出來一個新人物,就叫小王吧,不是小王八,老王就是住在小王隔壁,有一天小王看到老王電腦上有那麼多電影,就跟老王商量,能不能在他電腦上也弄個跟老王一樣的,讓他也看看。老王也不是個小氣的人,好東西就是要分享的嘛,想都沒想就答應了,給小王說在你電腦上插根線接到我電腦上,然後下載我這個軟件,直接訪問我的電腦就行了。小王很高興,他馬上回家按老王說的做,沒多久他的電腦上也能看老王電腦上的東西了。有了小王,就還會有小張,小李,互相插根網線,他們都能互相共享電影了。這樣,局域網也就產生了,就比如在一個公司或者一個學校用的網絡,都稱為局域網。那學校有很多呀,不同的學校用的局域網是不同的,不同城市的有不同的局域網,以局域網為單位,散布在一個區或一個城市的各個局域網加一起叫城域網,然後全世界所有城市的城域網加一起就叫廣域網

過了一段時間,老王去小王家,看到小王電腦里的電影比自己的還多,而且還好看,然後老王跟小王說,把你的這些電影給我一份吧,我也想看看。不用想,小王肯定立馬答應了,說你自己弄吧,想看哪個隨便看。姜還是老的辣,老王想到一個好辦法,他跟小王說,我再找一台電腦,把我倆電腦上的資源全部放到那台電腦里,我倆只要在那個電腦上插根線連到自己電腦上,就都能訪問那電腦上的東西了,這樣豈不是很方便。小王一拍腦門,呀!我咋就沒想到,那還不趕緊的。老王另外找了一台電腦,然後他倆把自己的電腦和那台電腦用線連起來,再把自己電腦里的所有資源全傳進那台電腦中,最後他倆就能共享資源了。(那台電腦就是服務器)(附加內容)

總結一下就是:

 

網絡按地域分類:根據參照物不同、類型不同分為

  • 局域網:一個公司、一個家庭、一個學校······
  • 城域網:一個地區、一個城市······
  • 廣域網:一個國家、全世界······

三、互聯網協議是如何分佈和設計的

我在上面說了Internet協議,互聯網協議按照功能的不同,分為osi七層tcp/ip五層tcp/ip四層協議。如下圖:

osi的七層協議體繫結構的概念清楚,理論也比較完善,但它既複雜又不實用,ISO制定的osi協議參考模型的過於龐大、複雜招致了許多批評。於此對照,由技術人員自己開發的TCP/IP協議獲得了更為廣泛的應用。因此,我們只需要弄明白TCP/IP五層協議就能了解和明白計算機最底層的通信是怎麼回事。

 

四、TCP/IP五層協議

如圖,從最下方的物理層到最上方的應用層,對於我們用戶而言,最直接的是應用層。從上到下每一層都依賴於下一層,所以我從最下一層開始給大家講解:

 

注意:每一層都運行着一個特定的協議,共同組合成互聯網協議

一、物理層

物理層主要是由雙絞線光纜電纜無線電波組成,其作用很簡單,就是連接不同的計算機,並傳遞底層電信號,高電壓:1,低電壓:0。

二、數據鏈路層

我們從物理層上接收或者發送單純的0、1是沒有意義的,為什麼呢?想想哈,我想給女朋友發送一句話:“你好漂亮”,那我們要把“你好漂亮”轉換成01之後,交給網卡,網卡就懵逼了,發給誰???不知道。那怎麼辦?必須要確定數據發給誰。就像我們的快遞一樣,是不是在外層包裝上有商家地址和個人地址,這樣我們不管是還是,都能準確定位了。網絡傳送數據也一樣,我們就在數據前面加上目標地址,為了能接收到回信,也要把自己的地址也加上。但是,如果數據和地址放在一起,又亂了,比如,我給你一堆01,1010101000101010101,你也分不清哪裡是數據,哪裡是地址。這時我們就要對要發送的01進行分組,規定前面xxx位是地址,後面xxx位是數據,並且,大家想互相都能通信,就必須都遵守這樣的規則(協議),這個協議叫以太網協議。在以太網協議出現之前,各個公司都有自己的分組規則,後來都統一使用以太網協議了。

以太網協議規定:一組電信號構成一個數據包,叫,每一幀分為報頭(head)和數據(data)兩部分。


報頭(head):固定18個字節

發送者/
源地址: 6個字節

接收者/
目標地址: 6個字節


數據類型:6個字節


數據(data):最短46個字節,最長1500字節

數據包的具體內容(發送給女朋友的話/快遞貨物)

 

以太網協議中的地址叫MAC地址,MAC地址是每台計算機唯一的物理地址,是被寫在網卡上的。以太網協議規定,每一台接收和發送數據的設備必須要裝有網卡,負責發送和接收數據的設備,發送端和接收端的地址,指的就是網卡的地址,即MAC地址

MAC地址

MAC地址是每個網卡在出廠的時候,由各個廠商直接燒錄在網卡上的,而且,這個地址必須是全世界唯一的。
MAC地址是由12位16進制的數字錶示(前六位是廠商編號,后六位是流水線號),這樣不同的廠商之間就不會產生衝突了,自己生產自己的就好了。

交換機

在這給大家介紹一個東西,我們說兩個電腦要通信要先連根線,但是如果電腦多了之後,電腦間通信連的線也就多了,這樣太亂了,我介紹的這個東西就是交換機,它是負責組件局域網,研究的是MAC地址,它有什麼用你看下面圖片中的接口就知道了

有了MAC地址,以太網就可以進行工作了.理論上講,我們可以和世界上的每一台連接了互聯網的計算機進行通信了,此時通信的方案是:廣播

廣播

廣播又是怎麼一回事?其實廣播的方式很原始,基本通信就是靠吼。就像你想跟女朋友求婚一樣,你會大喊:“xxx,嫁給我吧”,旁邊能聽到這句話的人有很多,但是只有你女朋友會回復你。其他人會把你當傻逼一樣看待。
沒錯,廣播就是這樣進行通信的。首先組織好了一個數據包之後,把這個數據包通過電信號發出去,這時整個網絡上所有的人都會收到你發的這條數據,然後看看這個數據是不是自己的。如果不是就當他不存在,如果是,就接收。雖然效率低點,但畢竟能通信了。

廣播帶來什麼問題呢?如果是在一個小的網絡環境里。比方說,你們宿舍幾個人,一起玩CS,沒問題,你喊一嗓子,你室友也能回應你,也就效率低點。但是如果你連接到全世界的互聯網上,還使用廣播的方式來通信,就不是效率問題了,而是一個巨大的通信災難。全世界60多億人,每個人吼一嗓子,每個人發送一條信息,那每個人都會收到60多億條信息,網絡瞬間癱瘓,這種問題被稱為廣播風暴,那如何解決呢?

三、網絡層

首先,我們要了解一個事情,世界大網絡(廣域網)是由一個一個的互相隔離的小型局域網(子網)組成的,不同的局域網之間使用路由來連接。

 

路由器

上面說的交換機是負責組建局域網,研究的是MAC地址,而路由器是負責組件廣域網,研究的是IP地址,這個IP地址下面我再解釋。

剛才說的廣播,只能在一個局域網內進行通信,不可以在大網絡上進行廣播,有了路由器,就避免了廣播風暴的問題。每個局域網被稱為一個廣播域,局域網和局域網之間使用路由的方式進行通信(向不同的廣播域/子網發送數據包),用路由器把一個局域網裡的所有計算機劃分成一個個子網。

 

現在大家先想一想生活中,如果是之前說的廣播,就相當於在一間教室里你正在上課,你要給某個女孩傳紙條,那你要在紙條外面寫上你和你要傳的女孩的名字,再在裏面寫上你要寫的話,寫好后折起來,你和女孩的名字在外,內容在內,然後讓同學一個一個幫你傳,每個同學在傳的時候都會看外面寫的名字,如果不是他,就繼續傳,直到傳到的人是你寫的那個女孩為止。那這間教室就相當於一個局域網。好,現在我們要實現局域網和局域網之間的通信,比如此時位於青海的你要給河南的朋友送東西,你不可能用廣播的方式了,你只能快遞,這個青海和河南以及還有很多不同的省份是我國的土地劃分,每一個省也可以看作是一個個局域網,那在網絡中,我國就相當於廣域網,那不同的局域網是如何劃分的呢?MAC地址是沒辦法區分的,因為MAC地址上只有廠商的流水號,這就引出我要說的網絡層,網絡層引出了一套新的地址來區分不同的局域網/子網,這一套地址就是網絡地址。

規定網絡地址的協議叫IP協議,它定義的地址叫IP地址。其實跟我國的省份名差不多,繼續說送快遞,你要把送的東西包裝好,在外面寫上你自己的地址和省份地址,還有朋友的地址和省份地址,交給青海的快遞公司,然後青海的快遞公司轉交給河南的快遞公司,最後由河南的快遞公司分配給你朋友,你朋友就收到快遞了。在這有兩點需要注意:

  • 你要同時寫兩個地址,自己的地址(MAC地址)和省份地址(IP地址),這樣就能確定所要接收和發送人的具體的位置了。
  • 青海的快遞公司和河南的快遞公司就相當於路由器
子網掩碼

在這給大家普及一下子網掩碼,我們上面說有了IP地址MAC地址,我們就能讓任何計算機之間進行通信了,那現在再想想,如果我要用我的計算機給另外一台計算機實現通信,我是不是要判斷要通信的計算機是否和我的計算機在同一個IP地址中,相當於上面例子中我和我要送東西的朋友是否在一個省,這個時候就需要用子網掩碼,我拿着我的IP地址和對方的子網掩碼通過計算,判斷是否在同一個IP地址下,如果在同一個IP地址下,我可以用廣播的形式進行通信,如果不在同一個IP地址下,我可以先把數據傳給我方的路由器,再由我方的路由器把數據傳給對方的路由器,最後由對方的路由器把數據傳給要接收數據的計算機。這樣說,再聯繫上面已經說過的,我想不難理解了。

IP地址

目前我們普遍使用的是IPV4,它規定,一個網絡地址由32位二進制組成,把32位平均分成四份,每一份8位,8位最大能表示的數據是255,所以IP地址的範圍:0.0.0.0-255.255.255.255.

一個IP地址分為兩部分,分別是網絡位主機位
網絡位用來標識不同的子網
主機位用來標識子網下主機的編號

為什麼要分兩部分呢?很簡單,就好比你想寫信給你的女朋友,假設你女朋友的地址是西寧市平安路128號,那麼網絡位就會直接找到西寧市,主機位幫你找到你的女朋友。

網絡位和主機位是如何劃分的?使用子網掩碼來劃分。子網掩碼和IP地址差不多,都是由32位二進制數來表示,子網掩碼也分為網絡部分主機部分,網絡部分由1組成,主機部分由0組成。

那說了這麼多,IP協議是如何發送數據的?協議規定,IP協議使用IP數據包進行發送數據。IP數據包同樣把數據分為了兩部分,headdata,並且在發送數據的時候,直接用IP數據包直接裝載以太網的data部分。

head:長度為20到600字節
data:最長為65515字節
而以太網數據包的“數據”部分,最長只有1500字節,因此,如果IP數據包超過了1500字節,它就需要分割成幾個以太網數據包,分開發送了。

 

再回顧一下啊,以太網的頭是包含了自己的MAC地址目標MAC地址的,那如何查找目標MAC地址呢?前輩們弄了一個叫ARP協議的東西專門來解析目標MAC地址。它是如何工作的?首先,它是數據鏈路層的東西,在我們發送一個數據包的時候是包含着對方的IP地址的。例如我(172.13.4.58)想發送一條數據給女朋友(172.13.4.90),首先,我得先拿到女朋友的MAC地址才可以通信。此時,我們會先計算一下我和女朋友是否在一個子網內(子網掩碼)

  • 在一個子網內,直接廣播發送一個數據包

    子網內的計算機發現了這個包之後會返回一個數據包並且帶有MAC地址,這樣就通過IP地址找到了目標主機的MAC地址,接下來就可以進行數據傳輸了。

  • 不在一個子網內,單純的用廣播就不行了,因為廣播只是針對自己內網而言。那怎麼辦,此時會把數據包發給網關,由網關發給其它路由,這樣在整個萬維網裡就可以找到你想要的那個計算機的MAC地址了。
總結
  • ARP就是通過IP地址來查找MAC地址的一套固定協議,它是數據鏈路層的內容。
  • 網絡層的意義:定義了子網, 區分各個局域網
  • IP地址:網絡地址
  • 子網掩碼:計算是否是同一個子網

四、傳輸層

到目前為止,前三層內容已經可以進行數據傳輸了。但是,我們的一台計算機上可以一次性運行多個網絡應用程序,比如,QQ、微信、LOL三個軟件一起運行,都要進行網絡傳輸,但是就前面學習的這三層內容,是沒辦法區分開數據是要發送給哪一個軟件的。那怎麼辦?引入第四層,傳輸層,傳輸層定義了端口的概念,每一個網絡應用程序佔用一個網絡端口,不同的程序就用端口把數據隔離,兩兩互相不影響。

  • 端口:應用程序和網卡的關聯編號
  • 傳輸層:建立端口到端口的通信。
  • 傳輸層有兩種協議:TCPUDP
TCP協議

TCP協議:可靠傳輸,TCP數據包沒有長度限制,理論上可以無限長,但是為了保證網絡的效率,通常TCP數據包的長度不會超過IP數據包的長度,以確保單個TCP數據包不必再分割。

TCP頭放的主要是
源端口
目標端口

 

UDP協議

UDP協議:不可靠傳輸,“報頭”部分一共只有8個字節,總長度不超過65535字節,正好放進一個IP數據包。

其實和郵信是一樣的,寫好地址,寫好接收人,直接裝進信封里,丟進郵箱里就不用你管了,對方什麼時候收,收沒收到,你不知道。

 

五、應用層

用戶使用的都是應用程序,均工作於應用層,大家都可以開發自己的應用程序,數據多種多樣,必須規定好數據的組織形式。對於用於而言最直觀的就是應用層。

  • 應用層:規定應用程序的數據格式

:TCP協議可以為各種各樣的程序傳遞數據,比如Email、WWW、FTP等,那麼,必須有不同協議規定电子郵件、網頁、FTP數據的格式,這些應用程序協議就構成了“應用層”。

總結

以上是對TCP/IP5層協議的解讀,總結一下:

發送數據其實就是一個封裝數據的過程

最後從物理層發出,對方接收到了之後再自下而上一層一層打開拿到數據,以上內容就是一個網絡傳輸的大致過程,其中還有好多細節沒有闡述,但大家知道和了解以上內容,對開發而言足夠了。

 

結束

本來還想加上TCP協議的三次握手四次斷開,奈何考慮到篇幅過大,對讀者不利,權衡再三,決定單獨去寫這部分。

看完有收穫?那麼希望老鐵別吝嗇你的三連擊哦
1、點個推薦,讓更多的人看到這篇文章
2、關注我的原創微信公眾號【泰斗賢若如】,第一時間閱讀我的文章
3、歡迎關注我的博客

 【原創聲明】:本人原創:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

[ASP.NET Core 3框架揭秘] 文件系統[2]:總體設計

在《》中,我們通過幾個簡單的實例演示從編程的角度對文件系統做了初步的體驗,接下來我們繼續從設計的角度來進一步認識它。這個抽象的文件系統以目錄的形式來組織文件,我們可以利用它讀取某個文件的內容,還可以對目錄或者文件實施監控並及時得到變化的通知。由於IFileProvider對象提供了針對文件系統變換的監控功能,在.NET Core下里類似的功能大都利用一個IChangeToken對象來實現,所以我們在對IFileProvider進行深入介紹之前有必要先來了解一下IChangeToken。

一、IChangeToken

從字面上理解的IChangeToken對象就是一個與某組監控數據關聯的“令牌(Token)”,它能夠在檢測到數據改變的時候及時地對外發出一個通知。如果IChangeToken關聯的數據發生改變,它的HasChanged屬性將變成True。我們可以調用其RegisterChangeCallback方法註冊一個在數據發生改變時可以自動執行的回調,該方法會返回一個IDisposable對象,我們通過用其Dispose方法解除註冊的回調。至於IChangeToken接口的另一個屬性ActiveChangeCallbacks,它表示當數據發生變化時是否需要主動執行註冊的回調操作。

public interface IChangeToken
{
    bool HasChanged { get; }
    bool ActiveChangeCallbacks { get; }
    IDisposable RegisterChangeCallback(Action<object> callback, object state);
}

.NET Core提供了若干原生的IChangeToken實現類型,我們最常使用的是一個名為CancellationChangeToken的實現。CancellationChangeToken的實現原理很簡單,它基本上就是按照如下的形式藉助我們熟悉的CancellationToken對象來發送通知。

public class CancellationChangeToken : IChangeToken
{
    private readonly CancellationToken _token;
    public CancellationChangeToken(CancellationToken token)  => _token = token;
    public bool HasChanged  => _token.IsCancellationRequested; 
    public bool ActiveChangeCallbacks  => true;    
    public IDisposable RegisterChangeCallback(Action<object> callback, object state)  => _token.Register(callback, state);
}

除了CancellationChangeToken,有時也我們也會使用到一個名為CompositeChangeToken的實現。顧名思義,CompositeChangeToken代表由多個IChangeToken組合而成的複合型IChangeToken對象。如下面的代碼片段所示,我們在調用構造函數創建一個CompositeChangeToken對象的時候,需要提供這些IChangeToken對象。對於一個CompositeChangeToken對象來說,只要組成它的任何一個IChangeToken發生改變,其HasChanged屬性將會變成True,而註冊的回調自然會被執行。至於ActiveChangeCallbacks屬性,只要任何一個IChangeToken的同名屬性返回True,該屬性就會返回True。

public class CompositeChangeToken : IChangeToken
{
    public bool  ActiveChangeCallbacks { get; }
    public IReadOnlyList<IChangeToken>  ChangeTokens { get; }
    public bool  HasChanged { get; }
   
    public CompositeChangeToken(IReadOnlyList<IChangeToken> changeTokens);   
    public IDisposable RegisterChangeCallback(Action<object> callback, object state);   
}

我們可以直接調用IChangeToken提供的RegisterChangeCallback方法來註冊在接收到數據變化通知后的回調操作,但是更常用的方式則是直接調用靜態類型ChangeToken提供的如下兩個OnChange方法重載來進行回調註冊,這兩個方法的第一個參數需要被指定為一個用來提供IChangeToken對象的Func<IChangeToken>委託。

public static class ChangeToken
{
    public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer,  Action changeTokenConsumer) ;
    public static IDisposable OnChange<TState>(Func<IChangeToken> changeTokenProducer,  Action<TState> changeTokenConsumer, TState state) ;
}

二、IFileProvider

在了解了IChangeToken是怎樣一個對象之後,我們將關注轉移到文件系統的核心接口IFileProvider上,該接口定義在NuGet包“Microsoft.Extensions.FileProviders.Abstractions”中。我們在《》做了幾個簡單的實例演示,它們實際上體現了文件系統承載的三個基本功能,而這三個基本功能分別體現在IFileProvider接口如下所示的三個方法中。

public interface IFileProvider
{    
    IFileInfo GetFileInfo(string subpath);
    IDirectoryContents GetDirectoryContents(string subpath);
    IChangeToken Watch(string filter);
}

三、IFileInfo

雖然文件系統採用目錄來組織文件,但是不論是目錄還是文件都通過一個IFileInfo對象來表示,至於具體是目錄還是文件則通過IFileInfo的IsDirectory屬性來確定。對於一個IFileInfo對象,我們可以通過只讀屬性Exists判斷指定的目錄或者文件是否真實存在。至於另外兩個屬性NamePhysicalPath,它們分別表示文件或者目錄的名稱和物理路徑。屬性LastModified返回一個時間戳,表示目錄或者文件最終一次被修改的時間。對於一個表示具體文件的IFileInfo對象來說,我們可以利用屬性Length得到文件內容的字節長度。如果我們希望讀取文件的內容,可以藉助於CreateReadStream方法返回的Stream對象來完成。

public interface IFileInfo
{
    bool Exists { get; }
    bool  IsDirectory { get; }
    string Name { get; }
    string PhysicalPath { get; }
    DateTimeOffset LastModified { get; }
    long Length { get; }

    Stream CreateReadStream();
}

IFileProvider接口的GetFileInfo方法會根據指定的路徑得到表示所在文件的IFileInfo對象。換句話說,雖然一個IFileInfo對象可以用於描述目錄和文件,但是GetFileInfo方法的目的在於得到指定路徑返回的文件而不是目錄(我個人不太認同這種令人產生歧義的API設計)。一般來說,不論指定的文件是否存在,該方法總會返回一個具體的IFileInfo對象,因為目標文件的存在與否是由該對象的Exists屬性來確定的。

四、IDirectoryContents

如果希望得到某個目錄的內容,比如需要查看多少文件或者子目錄包含在這個目錄下,我們可以調用IFileProvider對象的GetDirectoryContents方法並將所在目錄的路徑作為參數。目錄內容通過該方法返回的IDirectoryContents對象來表示。如下面的代碼片段所示,一個IDirectoryContents對象實際上是一組IFileInfo對象的集合,組成這個集合的所有IFileInfo自然就是對包含在這個目錄下的所有文件和子目錄的描述。和GetFileInfo方法一樣,不論指定的目錄是否存在,GetDirectoryContents方法總是會返回一個具體的IDirectoryContents對象,它的Exists屬性會幫助我們確定指定目錄是否存在。

public interface IDirectoryContents : IEnumerable<IFileInfo>
{
    bool Exists { get; }
}

五、監控目錄或者文件更新

如果我們希望監控IFileProvider所在目錄或者文件的變化,我們可以調用它的Watch方法,當然前提是對應的IFileProvider對象提供了這樣的監控功能。這個方法接受一個字符串類型的參數filter,我們可以利用這個參數指定一個針對“文件匹配模式(File Globing Pattern)”表達式(以下簡稱Globing Pattern表達式)來篩選需要監控的目標目錄或文件。

Globing Pattern表達式比正則表達式簡單多了,它只包含“*”一種“通配符”,如果硬說它包含兩種通配符的話,那麼另一個通配符是“**”。Globing Pattern表達式體現為一個文件路徑,其中“*”代表所有不包括路徑分隔符(“/”或者“\”)的所有字符,而“**”則代表包含路徑分隔符在內的所有字符。下錶給出了幾個典型的Globing Pattern表達式和它們代碼的文件匹配語義。






















Globing Pattern表達式

匹配的文件

src/foobar/foo/settings.*

 

子目錄“src/foobar/foo/”(不含其子目錄)下名為“settings”的所有文件,比如settings.jsonsettings.xmlsettings.ini等。

src/foobar/foo/*.cs

 

子目錄“src/foobar/foo/”(不含其子目錄)下的所有.cs文件。

src/foobar/foo/*.*

子目錄“src/foobar/foo/”(不含其子目錄)下所有文件。

src/**/*.cs

子目錄“src”(含其子目錄)下的所有.cs文件。

一般來說,不論是調用IFileProvider對象的GetFileInfo或GetDirectoryContents方法所指定的目標文件或目錄的路徑,還是調用Watch方法指定的篩選表達式,都是一個針對當前IFileProvider對象映射根目錄的相對路徑。指定的這個路徑可以採用“/”字符作為前綴,但是這個前綴是不必要的。換句話說,如下所示的這兩組程序是完全等效的。

路徑不包含前綴“/”

var dirContents = fileProvider.GetDirectoryContents("foobar");
var fileInfo = fileProvider.GetFileInfo("foobar/foobar.txt");
var changeToken = fileProvider.Watch("foobar/*.txt");

路徑包含前綴“/”

var dirContents = fileProvider.GetDirectoryContents("/foobar");
var fileInfo = fileProvider.GetFileInfo("/foobar/foobar.txt");
var changeToken = fileProvider.Watch("/foobar/*.txt");

總的來說,以IFileProvider對象為核心的文件系統在設計上看是非常簡單的。除了IFileProvider接口之外,文件系統還涉及到其他一些對象,比如IDirectoryContents、IFileInfo和IChangeToken等,下圖所示的UML展示了這些接口以及它們之間的關係。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

南極沉睡巨人熱醒 研究:持續暖化恐融最大冰層

摘錄自2018年9月20日中央社巴黎報導

科學雜誌「自然」(Nature)19日刊登的研究發現,被稱為南極洲「沉睡的巨人」的東南極冰蓋也受到暖化影響。研究分析澳洲南方的威爾克斯冰下盆地(Wilkes Subglacial Basin)上次融化、約12萬5000年前留下的海床沉積層。

專家發現,只要氣溫持續上升攝氏2度,就會使威爾克斯盆地開始又融化。增幅攝氏2度是2015年具里程碑意義的巴黎氣候協定為避免全球暖化失控所設定的上限,最好控制在攝氏1.5度以下。

研究主筆、倫敦帝國學院(Imperial College)的威爾森(David Wilson)表示:「這是我們首次看到直接證據顯示,從過去地質看到這部分的冰層如何受微幅暖化影響。」

團隊發現,如果全球平均氣溫控制在較工業革命前水平上升攝氏2度,這整個盆地就會融化,導致全球海平上升大約4公尺,危及小島國家,影響沿海地區數以億計居民。

照目前溫室氣體排放速度,到了本世紀末,全球氣溫升幅將遠遠超過攝氏2度,可能多達攝氏4至5度。

威爾森表示:「由於目前全球氣溫已較工業時代前高出攝氏1度,如果我們無法減少碳排放,未來冰層融化似乎無可避免。」

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

醜聞、禁令、產業競爭  德國交通不得不向低碳轉型

環境資訊中心特約記者 陳文姿報導

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

霧霾瀰漫87天 南加州人三個月沒能好好呼吸

摘錄自2018年9月22日蘋果日報美國加州報導

南加州人對今夏的記憶大概就是熱浪、野火、髒空氣。該地區空氣的臭氧含量連續87天超標,據《洛杉磯時報》報導,這創出至少20年來最長的紀錄。環保官員表示,日期長短不是判斷空污程度的指標,但這確實對健康有影響。

臭氧是導致氣喘和其他呼吸疾病發作的原因之一。加州空氣監測數據顯示,該州南部包含洛杉磯郡、橘郡、河濱郡等地,自6月19日至9月13日,空氣中臭氧含量都高於標準值70ppb;上周末雖略有緩和,但並未散退,本周又持續超標。

加州南岸空氣品質管理局(South Coast Air Quality Management District)表示,用連續日期數據衡量臭氧抑制的進展,並不恰當。今年的霧霾季比去年晚開始,目前為止,霧霾狀況沒有去年嚴重,臭氧含量最高值和「非常不健康」的日數都比較少。

去年臭氧超標的總日數為145天,前年為132天。該管理局無法確定最長的連續超標日紀錄,發言人表示,因為那不是美國環保局規定的空氣品質測量方式,所以沒追蹤。監管機構將空氣品質劣化歸咎於天氣變熱,使污染物質被光分解的速度加快;逆溫層較往年持久,污染物不易散開。

環保團體和監督機構都不認同此番解釋。南岸空氣品質委員會成員喬瑟夫盧(Joseph Lyou)表示,無論法律是否以此作為標準,長期空氣不良的趨勢都令人不安。環保團體譴責,官員應該可以有更多政策取締污染源,例如煉油廠或排放廢氣的「烏賊車」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

SpringBoot基本配置詳解

SpringBoot項目有一些基本的配置,比如啟動圖案(banner),比如默認配置文件application.properties,以及相關的默認配置項。

示例項目代碼在:

一、啟動圖案banner

編寫banner.txt放入resources文件夾下,然後啟動項目即可修改默認圖案。

關於banner的生成,可以去一些專門的網站。

比如:https://www.bootschool.net/ascii

二、配置文件application

2.1 application.properties/yml

resources下通常會默認生成一個application.properties文件,這個文件包含了SpringBoot項目的全局配置文件。裏面的配置項通常是這樣的:

server.port=8080

在這個文件里我們可以添加框架支持的配置項,比如項目端口號、JDBC連接的數據源、日誌級別等等。

現在比較流行的是將properties文件改為yml文件。yml文件的格式yaml是這樣的:

server:
    port: 8080

yml和properties的作用是一樣的。而yml的好處是顯而易見的——更易寫易讀。

屬性之間互相調用使用${name}:

eknown:
    email: eknown@163.com
    uri: http://www.eknown.cn
    title: 'hello, link to ${eknown.uri} or email to ${eknown.email}'

鏈接:

2.2 多環境配置文件

通常開發一個應用會有多個環境,常見如dev/prod,也會有test,甚至其他一些自定義的環境,SpringBoot支持配置文件的靈活切換。

定義新配置文件需要遵循以下格式:application-{profile}.properties 或者application-{profile}.yml

比如現在有dev和prod兩個環境,我需要在application.yml文件之外新建兩個文件:

  1. application-dev.yml

    server:
       port: 8080
  2. application-prod.yml

    server:
      port: 8081

然後在application.yml中通過application.profiles.active={profile}指明啟用那個配置:

application:
    profiles:
      active: dev

除了在application.yml中指定配置文件外,還可以通過啟動命令指定:java -jar xxx.jar --spring.profiles.active=dev

2.2 自定義配置項並獲取它

主要介紹兩種方式,獲取單個配置項和獲取多個配置項。

舉例:

eknown:
    email: eknown@163.com
    uri: http://www.eknown.cn

2.2.1 使用@Value註解獲取單個配置項

@Value("${eknown.email}")
private String email;

@Value("${eknown.uri}")
private String url;

注意:使用@Value註解的時候,所在類必須被Spring容器管理,也就是被@Component、@Controller、@Service等註解定義的類。

2.2.2 獲取多個配置項

第一種,定義一個bean類,通過@Value獲取多個配置項:

@Component
public class MyConfigBean {
  
}

然後我們通過get方法來獲取這些值:

@RestController
public class BasicAction {
  
  @Autowired
  private MyConfigBean myConfigBean;

}

第二種,使用註解@ConfigurationProperties:

@Component
@ConfigurationProperties(perfix="eknown")
public class MyConfigBean {

  private String email;
  private String uri;
}

這裏只需要通過prefix指定前綴即可,後面的值自動匹配。

這裏我們還使用了@Component註解來讓spring容器管理這個MyConfigBean。

此外,我們可以不需要引入@Component,轉而在Application啟動類上加上@EnableConfigurationProperties({MyConfigBean.class})來啟動這個配置。

注意:我們這裡是從主配置文件,也就是SpringBoot默認的application-profile文件中獲取配置數據的。

而從自定義的配置文件,比如test.yml這種形式中獲取配置項時,情況是有點不大一樣的。

三、自定義配置文件

上面介紹的配置文件都是springboot默認的application開頭的文件。如果要自定義一個配置文件呢,比如test.yml或test.properties,怎麼獲取其中的配置項呢?

使用@PageResource註解即可。

首先我們來看一下讀取自定義的properties文件里的內容:

test.properties

hello.time=2019.11.19
hello.name=eknown

定義Configuration類:

@Configuration
@PropertySource("classpath:test.properties")
//@PropertySource("classpath:test.yml") // 注意,yml文件不能直接這樣寫,會讀不出數據
@ConfigurationProperties(prefix = "hello")
public class TestConfiguration {
    private String name;
    private String time;

    // hide get and set methods
}

測試一下:

@RestController
@RequestMapping(value = "test")
public class TestAction {

    @Autowired
    private TestConfiguration testConfiguration;

    @GetMapping(value = "config")
    public String test() {
        return testConfiguration.getName() + "<br/>" + testConfiguration.getTime();
    }
}

如果將properties文件換成yml文件呢?

我們嘗試一下,發現:

讀不出數據?

分析一下@PropertySource註解,發現其使用的PropertySourceFactory是DefaultPropertySourceFactory.

這個類的源碼如下:

public class DefaultPropertySourceFactory implements PropertySourceFactory {
    public DefaultPropertySourceFactory() {
    }

    public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
        return name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource);
    }
}

這個類只能處理properties文件,無法處理yml文件。所以我們需要自定義一個YmlSourceFactory。

public class YamlSourceFactory extends DefaultPropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        return new YamlPropertySourceLoader().load(resource.getResource().getFilename()
                , resource.getResource()).get(0);
    }
}

然後定義test.yml文件的config類:

@Configuration
@PropertySource(value = "classpath:test.yml", encoding = "utf-8", factory = YamlSourceFactory.class)
@ConfigurationProperties(prefix = "yml.hello")
public class TestYamlConfiguration {
    private String name;
    private String time;

    // hide get and set methods
}

注:為了區分test.properties和test.yml,這裏的test.yml中的屬性以yml.hello開頭。

編寫一下測試:

    @Autowired
    private TestYamlConfiguration ymlConfiguration;

    @GetMapping(value = "yml")
    public String testYml() {
        return "yml config: <br/>" + ymlConfiguration.getName() + "<br/>" + ymlConfiguration.getTime();
    }

訪問:

四、補充@ConfigurationProperties

網上一些資料中,為配合使用@ConfigurationProperties,還使用了@EnableConfigurationProperties註解。

經過測試發現:

  1. 從SpringBoot默認配置文件讀取配置信息,使用@ConfigurationProperties + @Component/@Configuration,或者@ConfigurationProperties + 在啟動類添加@EnableConfigurationProperties({class})。這兩種方式都能解決問題

  2. 從非默認配置文件讀取配置信息,需要利用@PropertySource註解。同樣兩種方式:

    2.1 @PropertySource + @ConfigurationProperties + @Component/@Configuration

    2.2 @PropertySource + @ConfigurationProperties + @Component/@Configuration + @EnableConfigurationProperties,第二種方式存在一個問題,即還是必須要使用@Component註解,如果不使用,則會導致讀取配置信息為null,但程序不會報錯;而如果採用了,則會導致bean類的set方法被執行兩次(也就是生成了兩個同樣類型的bean類)。這種方式不建議!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

Appium+python自動化(四十一)-Appium自動化測試框架綜合實踐 – 即將落下帷幕(超詳解)

1.簡介

  今天我們緊接着上一篇繼續分享Appium自動化測試框架綜合實踐 – 代碼實現。到今天為止,大功即將告成;框架所需要的代碼實現都基本完成。

2.data數據封裝

2.1使用背景

在實際項目過程中,我們的數據可能是存儲在一個數據文件中,如txt,excel、csv文件類型。我們可以封裝一些方法來讀取文件中的數據來實現數據驅動。

2.2案例

將測試賬號存儲在account.csv文件,內容如下:

account.csv

hg2018

hg2018

hg2019

zxw2019

666

222

參考代碼

2.3enumerate()簡介

enumerate()是python的內置函數

  • enumerate在字典上是枚舉、列舉的意思
  • 對於一個可迭代的(iterable)/可遍歷的對象(如列表、字符串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值
  • enumerate多用於在for循環中得到計數。

2.4enumerate()使用

如果對一個列表,既要遍歷索引又要遍曆元素時,首先可以這樣寫:

參考代碼
list = ["", "", "一個", "測試","數據"]

for i in range(len(list)):

    print(i,list[i])

上述方法有些累贅,利用enumerate()會更加直接和優美:

參考代碼
list1 = ["", "", "一個", "測試","數據"]

for index, item in enumerate(list1):

        print(index,item)

3.數據讀取方法封裝

  數據讀取方法也屬於公共方法,這裏我們首先實現一下,然後將其封裝到裡邊即可。

3.1數據讀取方法實現的參考代碼

import csv


     def get_csv_data(csv_file,line):

        with open(csv_file, 'r', encoding='utf-8-sig') as file:

            reader=csv.reader(file)

            for index, row in enumerate(reader,1):

                if index == line:

                    return row

 

    csv_file='../data/account.csv'

    data=get_csv_data(csv_file,3)

    print(data)

3.2封裝

將其封裝在公共方法中,在其他地方用到的時候,直接導入調用即可。

4.utf-8與utf-8-sig兩種編碼格式的區別

UTF-8以字節為編碼單元,它的字節順序在所有系統中都是一樣的,沒有字節序的問題,也因此它實際上並不需要BOM(“ByteOrder Mark”)。但是UTF-8 with BOM即utf-8-sig需要提供BOM。

5.config文件配置

各種配置文件都放在這個目錄下。

5.1日誌文件配置 

主要是一些日誌信息的配置。

log.config

 參考代碼
[loggers]
keys=root,infoLogger

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0

[handlers]
keys=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('../logs/runlog.log', 'a')

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

6.測試用例封裝

這裏宏哥舉例給小夥伴們演示:封裝註冊和登錄兩個測試用例。

6.1測試用例執行開始結束操作封裝

測試用例執行開始和結束的封裝,其他模塊用到直接導入,調用即可。

myunit.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-20
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
import unittest
from kyb_testProject.common.desired_caps import appium_desired
import logging
from time import sleep

class StartEnd(unittest.TestCase):
    def setUp(self):
        logging.info('=====setUp====')
        self.driver=appium_desired()

    def tearDown(self):
        logging.info('====tearDown====')
        sleep(5)
        self.driver.close_app()

6.2註冊用例

開始註冊用例代碼邏輯的實現。

test_register.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-20
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
from kyb_testProject.common.myunit import StartEnd
from kyb_testProject.businessView.registerView import RegisterView
import logging,random,unittest

class RegisterTest(StartEnd):
    def test_user_register(self):
        logging.info('======test_user_register======')
        r=RegisterView(self.driver)

        username = 'bjhg2019' + 'fly' + str(random.randint(1000, 9000))
        password = 'bjhg2020' + str(random.randint(1000, 9000))
        email = 'bjhg' + str(random.randint(1000, 9000)) + '@163.com'

        self.assertTrue(r.register_action(username,password,email))

if __name__ == '__main__':
    unittest.main()

6.3登錄用例

開始登錄用例代碼邏輯的實現。

test_login.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-13
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
from kyb_testProject.common.myunit import StartEnd
from kyb_testProject.businessView.loginView import LoginView
import unittest
import logging

class TestLogin(StartEnd):
    csv_file='../data/account.csv'

    @unittest.skip('test_login_zxw2018')
    def test_login_zxw2018(self):
        logging.info('======test_login_zxw2018=====')
        l=LoginView(self.driver)
        data=l.get_csv_data(self.csv_file,2)

        l.login_action(data[0],data[1])
        self.assertTrue(l.check_loginStatus())

    # @unittest.skip('skip test_login_zxw2017')
    def test_login_zxw2017(self):
        logging.info('======test_login_zxw2017=====')
        l=LoginView(self.driver)
        data = l.get_csv_data(self.csv_file, 1)

        l.login_action(data[0], data[1])
        self.assertTrue(l.check_loginStatus())

    @unittest.skip('test_login_error')
    def test_login_error(self):
        logging.info('======test_login_error=====')
        l = LoginView(self.driver)
        data = l.get_csv_data(self.csv_file, 3)

        l.login_action(data[0], data[1])
        self.assertTrue(l.check_loginStatus(),msg='login fail!')

if __name__ == '__main__':
    unittest.main()

7.小結

到此,Appium自動化測試框架就差下一篇就全部完成了,聰明的你都懂了嗎???嘿嘿!慢慢地來吧。

下節預告

下一篇,講解執行測試用例,生成測試報告,以及自動化平台,請關注宏哥,敬請期待!!!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

日本伊方核電廠機組 法院裁定重啟運轉

摘錄自2018年9月25日中央社報導

日本伊方核電廠三號機組去年底被廣島高等法院做出假處分裁定而持續停止運轉,但四國電力公司提出異議要求撤銷假處分,廣島高院今天(25日)撤銷原裁定,三號機組可重啟運轉。

根據日本讀賣新聞、日本放送協會(NHK)及日本經濟新聞等報導,目前由民眾提出聲請禁止三號機組運轉的假處分,還有高松高等法院、大分地方法院及山口地方法院岩國支部等正在審理,其中大分地方法院將在28日做出裁定。

因此,廣島高院今天撤銷假處分裁定後,三號機組雖然暫時又可重啟運轉,但未來視其他法院裁定結果,仍存在立刻停止運轉的可能性。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?