天天看點

細說Web頁面與本地電腦通訊

話說在很久很久以前。Web頁面與客戶的本地電腦Localhost通訊,有兩種方式:

1。Flash  

2。ActiveX控件

細說Web頁面與本地電腦通訊

為什麼 Adobe 公司要主動放棄這樣一款已經營運了十多年,且功能也相對完善的插件呢?這與它的競争對手——“HTML5 标準”的發展有着極大的關系。

HTML 是一種公開、通用的網頁語言标準。Flash 當年能大獲成功,很大程度上是因為當時的 HTML 太菜。不過随着蘋果、微軟等網際網路公司這些年來的不斷推進,HTML5 現在已經能實作和 Flash 差不多的功能了。

如今各大 App 的年終盤點頁面、搶紅包活動之類,幾乎都是用 HTML5 技術實作的。

雖然 HTML5 似乎足以取代 Flash,但這并非是後者被淘汰的真正緣由。Flash 的死因在于自身封閉的環境,這也是它誕生時就埋下的禍根——Flash 是一款 Adobe 公司私有的版權軟體。

對于蘋果、微軟這樣的網際網路巨頭來說,他們并不希望如此關鍵的技術被掌握在其他公司的手中。

這也是為什麼他們會選擇推行 HTML5 這樣公開的行業标準,因為 HTML 屬于所有人,而非某個公司。

蘋果公司早在十年以前就不待見 Flash 了,那時喬布斯專門針對 Flash 發表了一篇通告,實際讀起來更像是批評。

文中表示 Flash 的漏洞多、不安全、運作效率低,還不适配觸屏,有着許多弊端。

在這之後,蘋果與谷歌都相繼停止了 Flash 在自家手機平台上的支援,随着 HTML5 在移動化時代崛起,Flash 也逐漸顯露出疲态。

Adobe 旗下還有很多産品

就這樣,在 HTML5 和它背後大廠的壓力之下,Adobe 選擇結束對 Flash Player 的支援,并宣布将于 2020 年底正式停止它的運作。

他走後,歲月依然靜好

對于 Flash 的離開,很多人沒有實感——因為在主流浏覽器的預設屏蔽政策之下,網頁上的 Flash 插件早在幾年前就開始逐漸消失,或是以 HTML5 的方式更換了,甚至我們現在看到的彈窗小廣告,也幾乎都是用 HTML5 制作的。

Flash 早已名存實亡,2021 年 Win10 更新後的強制抹殺,不過是在 Flash 的棺材闆上又釘了幾根釘而已。

但在國内,情況有些特殊。由于 Flash 在國内是被代理營運的,在官網安裝的也都是“國行特供版”,更糟的是,這個國行版不僅會像流氓軟體一樣,彈窗各種低俗新聞和頁遊廣告,還會正大光明地收集使用者的資訊。

由于Flash本人不是很了解,也給出不了什麼示例代碼,

對于ActiveX控件來說,可以直接在在網絡上搜尋“ActiveX控件”,就會有很多相關的答案

但是:

  對于現代浏覽器來說,以上兩種方式都通通不支援了

  對于Flash來說,雖然現在浏覽器還有支援,但是都不是預設加載Flash插件了

  要使用者手工點同意才會加載。

  對于ActiveX控件來說,隻在IE浏覽器裡面才受支援,而且也要使用者同意,更别說現代浏覽器了

是以說。對于現代的浏覽器。要與本地電腦通訊。上面兩種方式已經行不通了。

那有沒有不要使用者點同意,就可以直接通過Web頁面與本地電腦通訊呢?

答案是當然有了。不然也不會有此文章了。

對于Web與伺服器通訊,我們走的是HTTP協定。這也是所有浏覽器都支援的

也不會管現在浏覽器和遠古時代的浏覽器也都支援HTTP協定。

那麼

如果Web要與本地電腦通訊,隻要Web與本地電腦走HTTP協定通訊。問題不就解決了嗎?

那怎麼實作呢?

既然要與本地電腦走HTTP協定通訊,那本地就必需要有一個支援HTTP協定的通道

平常我們與遠端伺服器通訊是怎麼通路的,比如說百度吧,我們輸入http://www.baidu.com就能正常打開了

那我們本地怎麼辦呢,我們做開發測試的時候,是不是輸入:http://localhost 或者 http://127.0.0.1是不是就通路的我們本地電腦

那麼,我們在頁面裡向本地發送一個請求。會有什麼結果?

我們來實驗一下:

首先,既然要與本地伺服器通訊。那麼我們在本地要監聽一下本地的http協定通訊,代碼如下

namespace LocalApp
{
    class Program
    {
        public static HttpListener listener = new HttpListener();
        static void Main(string[] args)
        {
            listener.Start();
            listener.Prefixes.Add("http://127.0.0.1:8976/");//我們監聽本地電腦8976端口的HTTP協定
            Thread t = new Thread(new ThreadStart(clientListener));
            t.Start();
            while (true)
            {
                string s = Console.ReadLine();
                listener.Stop();
                listener.Close();
            }
        }
        public static void clientListener()
        {
            while (true)
            {
                try
                {
                    //如果收到講求,我們就開啟一個線程去處理請求
                    HttpListenerContext request = listener.GetContext();
                    ThreadPool.QueueUserWorkItem(processRequest, request);
                }
                catch (Exception e) { Console.WriteLine(e.Message); }
            }
        }
        public static void processRequest(object listenerContext)
        {
            try
            {
                var context = (HttpListenerContext)listenerContext;
                var dicPar = new Dictionary<string, string>();
                var listPar = new List<string>();
                //拿到請求參數
                foreach (var item in context.Request.QueryString.AllKeys)
                {
                    listPar.Add(String.Format("{0}={1}", item, context.Request.QueryString[item]));
                    dicPar.Add(item, context.Request.QueryString[item]);
                }
                //設定傳回值
                var resultJson = JsonConvert.SerializeObject(dicPar);
                Console.WriteLine(String.Join(Environment.NewLine, listPar));
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                context.Response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(resultJson);
                context.Response.ContentType = "application/json";
                context.Response.ContentEncoding = Encoding.UTF8;
                context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
                //傳回結果
                System.IO.Stream output = context.Response.OutputStream;
                using (StreamWriter writer = new StreamWriter(output))
                {
                    writer.Write(resultJson);
                    writer.Close();
                }
            }
            catch
            {
            }
        }
    }
}      

上面的代碼就是監聽本地電腦8976端口的HTTP協定,

如果收到請求。就提取參數,然後以Json的形式傳回

那麼,我們寫一個Html頁面,向本地8976端口發送資料會出現什麼情況

代碼如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="Scripts/jquery-3.3.1.js"></script>
</head>
<body>
    <form id="form1" enctype="application/x-www-form-urlencoded">
        <div><label>參數1:<input type="text" name="Par1" /></label></div>
        <div><label>參數2:<input type="text" name="Par2" /></label></div>
        <div><button type="button" onclick="btnSubmit()">送出</button></div>
    </form>
    <script>
        function btnSubmit() {
            $.get("http://127.0.0.1:8976/", $("#form1").serialize(), function (result) {
                console.log(result);
            }, "json");
        }
    </script>
</body>
</html>      

打開Chrome浏覽器做一下測試。結果如下:

細說Web頁面與本地電腦通訊

頁面成功的發起了請求,本地電腦APP也監聽到了Web發來了請求,參數也都拿到了。

是不是我們就可以用這種方式用Web端來調用本地電腦的資源了

以知問題:

當我們的站點是以https通路的時候,如果用http去請求本地資源,而不是用https去請求的話,有些浏覽器會報Mixed Content錯誤

但是在新版本的谷歌與火狐已經解決了此問題,參考:https://bugzilla.mozilla.org/show_bug.cgi?id=903966

另外一個有一個秘密,一般人我不告訴他

你們知道當你們在本地登入了QQ後,然後打開QQ的Web站點,他為什麼就能自動知道你目前登入的QQ嗎?

沒錯,他也用了這種方案與QQ通訊,進而知道你登入了哪個QQ,這樣才會有此快速登入的功能

思維擴充:

這種訪案隻能是Web發起請求,本地電腦接受請求。隻支援單一方向通訊,

那麼,有沒有支援雙向通訊的方案呢?(Web到本地,本地到Web)

如果有知道答案的。可以在此博文下說出你的想法。

繼續閱讀