blk.dat
Raw blockchain data files 原始區塊鏈數據文件: https://learnmeabitcoin.com/guide/blkdat
Magic Bytes: https://learnmeabitcoin.com/guide/magic-bytes
完整解析,包括程式讀寫細節:
https://zhuanlan.zhihu.com/p/31924911
https://blog.csdn.net/t46414704152abc/article/details/90174727
几乎所有数据都是以小端类型(Little-Endian)存储的,但是 Transaction Input/Output 里的script除外。它们是以大端方式(Big-Endian)存储的。
magic number是一个固定的数,其值为 0xD9B4BEF9
variable integer本身的长度不定,在 1 到 9 字节之间。具体的解析规则如下:
如果第一个字节小于 253,那么直接返回该字节所代表的数值
如果第一个字节等于 253,那么往后再读一个字节作,将其值作为返回值
如果第一个字节等于 254,那么往后再读四个字节作,将其值作为返回值
如果第一个字节等于 255,那么往后再读八个字节作,将其值作为返回值
如果我们把之前的数据按照上述规则带入的话可以得到如下图表。
MySQL 的 varchar 到底可以存幾個中文字或幾個英文字呢
utf8 編碼為 1~3 個 字节
utf8mb4 編碼為 1~4 個 字节
UTF-8:一个汉字 = 3个字节,英文是一个字节
varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。
MySQL检查长度,可用SQL语言
SELECT LENGTH(fieldname) FROM tablename
以下為網友使用 UTF-8 的實驗
>> "我爱Ruby".size
=> 6
>> "I Love Ruby!".size
=> 12
mysql> select * from str_test;
+------------+--------------+
| name_chn | name_en |
+------------+--------------+
| 我爱Ruby | I Love Ruby! |
+------------+--------------+
1 row in set (0.02 sec)
mysql> select length(name_chn) from str_test;
+------------------+
| length(name_chn) |
+------------------+
| 10 |
+------------------+
1 row in set (0.01 sec)
3[一个汉字三字节] * 2 + 1[一个英文一字节] * 4 = 10
mysql> select length(name_en) from str_test;
+-----------------+
| length(name_en) |
+-----------------+
| 12 |
+-----------------+
1 row in set (0.00 sec)
10[一个英文一字节] * 1 + 2[空格一字节] * whitespace = 12
关于varchar 最多能存多少值
mysql的记录行长度是有限制的,不是无限长的,这个长度是64K,即65535个字节,对所有的表都是一样的。
MySQL对于变长类型的字段会有1-2个字节来保存字符长度。
当字符数小于等于255时,MySQL只用1个字节来记录,因为2的8次方减1只能存到255。
当字符数多余255时,就得用2个字节来存长度了。
在utf-8状态下的varchar,最大只能到 (65535 - 2) / 3 = 21844 余 1。
結論:
從 MySQL 8 開始,預設的 CHARSET 為 utf8mb4,COLLATE 為 utf8mb4_0900_ai_ci
所以 VARCHAR 理論上可以儲存大約 (65535-2)/4 = 16383,
實測後最大為 VARCHAR(16331)
參考資料:
https://21st.each1.net/2017/03/mysql.html
https://ruby-china.org/topics/24920
utf8mb4 編碼為 1~4 個 字节
UTF-8:一个汉字 = 3个字节,英文是一个字节
varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。
MySQL检查长度,可用SQL语言
SELECT LENGTH(fieldname) FROM tablename
以下為網友使用 UTF-8 的實驗
>> "我爱Ruby".size
=> 6
>> "I Love Ruby!".size
=> 12
mysql> select * from str_test;
+------------+--------------+
| name_chn | name_en |
+------------+--------------+
| 我爱Ruby | I Love Ruby! |
+------------+--------------+
1 row in set (0.02 sec)
mysql> select length(name_chn) from str_test;
+------------------+
| length(name_chn) |
+------------------+
| 10 |
+------------------+
1 row in set (0.01 sec)
3[一个汉字三字节] * 2 + 1[一个英文一字节] * 4 = 10
mysql> select length(name_en) from str_test;
+-----------------+
| length(name_en) |
+-----------------+
| 12 |
+-----------------+
1 row in set (0.00 sec)
10[一个英文一字节] * 1 + 2[空格一字节] * whitespace = 12
关于varchar 最多能存多少值
mysql的记录行长度是有限制的,不是无限长的,这个长度是64K,即65535个字节,对所有的表都是一样的。
MySQL对于变长类型的字段会有1-2个字节来保存字符长度。
当字符数小于等于255时,MySQL只用1个字节来记录,因为2的8次方减1只能存到255。
当字符数多余255时,就得用2个字节来存长度了。
在utf-8状态下的varchar,最大只能到 (65535 - 2) / 3 = 21844 余 1。
結論:
從 MySQL 8 開始,預設的 CHARSET 為 utf8mb4,COLLATE 為 utf8mb4_0900_ai_ci
所以 VARCHAR 理論上可以儲存大約 (65535-2)/4 = 16383,
實測後最大為 VARCHAR(16331)
參考資料:
https://21st.each1.net/2017/03/mysql.html
https://ruby-china.org/topics/24920
廣達將佈局量子電腦
廣達董事長林百里對量子電腦看法大翻盤,他13日出席媒體說明會,首度透露已指派幾位工程師投入量子電腦研究,為十年後更大量的雲端與IoT(物聯網)商機做準備。
時間回到今年4月,林百里出席員工家庭日活動時,談到量子電腦仍保守以對,認為演算法及材料使用都具不確定性,談量子電腦言之過早,那是15-20年後的事。
不過,半年後他的態度丕變,似乎跟台積電董事長劉德音透露台積電不會缺席量子電腦機會有關,也與重要客戶Google與IBM近期在量子電腦頻頻較勁上難脫關係。
「假如有一天,每一個人的口袋都有一個量子電腦,我想TSMC(台積電)一定不會缺席。」台積電董事長劉德音9月在一場論壇中表示,市場預期這將是一奈米以下製程的產品。
林百里:台積電是上游,我們下游
「我其實很怕十年後量子計算出來,這世界又將天翻地覆。」林百里說。他坦言:「我們(廣達)開始研究量子計算這回事,但我其實很怕。」他指的是量子電腦帶來的世界變局,科技業者需隨之應變。
「目前量子電腦運算機器都很不穩定,因為這是超導體、超低溫的挑戰,這也不是我們(廣達)可以做的,」林百里說,十年後他都80歲了,應該退休了,所以他現在先找幾個工程師研究(量子電腦),他判斷自己退休前應該都還不會真正生產量子電腦。
不過儘管林百里預言,他退休前廣達還不會生產量子電腦,但他看好十年後,量子時代將對廣達有百利,帶來的是雲端跟IoT裝置商機。
「量子時代對廣達更有利,因為運算能量更大,(處理器)裡面很快,需要接收資料量更大,因為接收太快了,一奈米或兩奈米能不能做到都還不知道,也因為運算很快,對外傳輸要用光網路。」林百里描繪量子運算的架構,顯然已經做過一番研究。
林百里說,廣達不會做引擎(量子晶片),但其他東西數量太大,比方應用,比方量子時代的雲端商機(量子電腦伺服器),這些廣達都非常樂觀,因為「世界上能做好的伺服器的公司不多,越來越難做,對我們(廣達)就會越來越有興趣。」
廣達副董事長梁次震也補充,「當運算越來越快,一定會想出應用之處,或許有一天人不用手跟嘴巴,頭上擺兩個量子就可以。」他打趣的說。
林百里舉當年雲端時代來臨,市場開始擔心NB會完蛋,但後來不僅不會,反而延伸出很多IOT應用,「量子是更大的應用,台積電也碰到量子問題,屆時若半導體需求更多,晶片需求更多,廣達做箱子(伺服器)的需求也就更多。」
遵循台積電的腳步,林百里對量子電腦轉為開放擁抱,他直言「台積電是上游,我們是下游,上游水多,我們就水多,供應鏈一致!我們兩家觀點一致!十年後量子電腦時代出來,預估是一奈米以下製程,我們箱子(伺服器)也要做(量子伺服器),箱子越來越難搞,有新的東西當然好玩。
十年內,飛機理論看好四大技術三大商機
至於十年之內,林百里看好的是AI、雲端、5G、IoT四者整合機會,13日他再度拿出一張「飛機理論」圖片描繪四者間的關係,「AI是機師,5G是噴射引擎,延伸出雲端技術,結果是要靠IoT賺錢,這是大整合機會,要四種都會做,提供解決方案才能賺錢!」
在這大技術融合趨勢中,廣達將有幾個佈局方向,首先是智慧醫療,其次是智慧工廠,第三是5G時代的雲端升級需求。
智慧醫療是最難做的事情,因為關乎人命,商業模式要考慮很多,涉及醫療倫理、品質、效率,林百里透露,廣達正在做研發病房及醫護平台,希望2020~2021年產品能落地試營運,但最大考驗仍然是行銷跟商業模式。
林百里解釋,因為若不做ODM,作品牌必須有行銷力,光做台灣市場太小,難以規模化,但若做全世界生意,醫療的品牌是最困難的,不僅要取得醫療認證及醫療機構認可,生意模式怎麼做?銷售人才如何培養?都是問題。
不過,林百里反而最看好AI與5G在三至五年內帶來的機會,他指出,5G背後的雲端機房跟4G不同,當各國發展5G將帶來非常大的需求,而當AI服務上路,Google等雲端客戶業務也將大成長,5G需要大量雲端,靠數量眾多的IoT收集資料,所以雲端跟IoT也將大成長。
在智慧製造方面,廣達林口總部又將興建新大樓,預計2020年第四季完工,這也成為智慧工廠實驗場域,林百里表示,廣達導入自動化機器人,加上AI檢驗,若未來導入5G,把感測器跟機器人都串連起來,能做大數據即時分析,即時決策,生產效率將提升20%,是非常大的商機。
「機器人也有聰明跟笨的!」林百里笑說,未來連線AI的是聰明機器人,而笨的機器人只會說「你好!你好!」毫無用途。
林百里早在20年前就看好行動聯網商機,率先投入筆電開發,現在則大力倡議AI戰略,梁次震補充:其實林百里早在31年前就已經想到廣達(Quanta)會做量子電腦(Quantum),所以創業取名廣達,林百里則笑著回應「廣達」就是取意產品既廣又能無線通達,兩位創辦人一番對談,讓外界多少能一窺科技前瞻領袖的先見之明。
時間回到今年4月,林百里出席員工家庭日活動時,談到量子電腦仍保守以對,認為演算法及材料使用都具不確定性,談量子電腦言之過早,那是15-20年後的事。
不過,半年後他的態度丕變,似乎跟台積電董事長劉德音透露台積電不會缺席量子電腦機會有關,也與重要客戶Google與IBM近期在量子電腦頻頻較勁上難脫關係。
「假如有一天,每一個人的口袋都有一個量子電腦,我想TSMC(台積電)一定不會缺席。」台積電董事長劉德音9月在一場論壇中表示,市場預期這將是一奈米以下製程的產品。
林百里:台積電是上游,我們下游
「我其實很怕十年後量子計算出來,這世界又將天翻地覆。」林百里說。他坦言:「我們(廣達)開始研究量子計算這回事,但我其實很怕。」他指的是量子電腦帶來的世界變局,科技業者需隨之應變。
「目前量子電腦運算機器都很不穩定,因為這是超導體、超低溫的挑戰,這也不是我們(廣達)可以做的,」林百里說,十年後他都80歲了,應該退休了,所以他現在先找幾個工程師研究(量子電腦),他判斷自己退休前應該都還不會真正生產量子電腦。
不過儘管林百里預言,他退休前廣達還不會生產量子電腦,但他看好十年後,量子時代將對廣達有百利,帶來的是雲端跟IoT裝置商機。
「量子時代對廣達更有利,因為運算能量更大,(處理器)裡面很快,需要接收資料量更大,因為接收太快了,一奈米或兩奈米能不能做到都還不知道,也因為運算很快,對外傳輸要用光網路。」林百里描繪量子運算的架構,顯然已經做過一番研究。
林百里說,廣達不會做引擎(量子晶片),但其他東西數量太大,比方應用,比方量子時代的雲端商機(量子電腦伺服器),這些廣達都非常樂觀,因為「世界上能做好的伺服器的公司不多,越來越難做,對我們(廣達)就會越來越有興趣。」
廣達副董事長梁次震也補充,「當運算越來越快,一定會想出應用之處,或許有一天人不用手跟嘴巴,頭上擺兩個量子就可以。」他打趣的說。
林百里舉當年雲端時代來臨,市場開始擔心NB會完蛋,但後來不僅不會,反而延伸出很多IOT應用,「量子是更大的應用,台積電也碰到量子問題,屆時若半導體需求更多,晶片需求更多,廣達做箱子(伺服器)的需求也就更多。」
遵循台積電的腳步,林百里對量子電腦轉為開放擁抱,他直言「台積電是上游,我們是下游,上游水多,我們就水多,供應鏈一致!我們兩家觀點一致!十年後量子電腦時代出來,預估是一奈米以下製程,我們箱子(伺服器)也要做(量子伺服器),箱子越來越難搞,有新的東西當然好玩。
十年內,飛機理論看好四大技術三大商機
至於十年之內,林百里看好的是AI、雲端、5G、IoT四者整合機會,13日他再度拿出一張「飛機理論」圖片描繪四者間的關係,「AI是機師,5G是噴射引擎,延伸出雲端技術,結果是要靠IoT賺錢,這是大整合機會,要四種都會做,提供解決方案才能賺錢!」
在這大技術融合趨勢中,廣達將有幾個佈局方向,首先是智慧醫療,其次是智慧工廠,第三是5G時代的雲端升級需求。
智慧醫療是最難做的事情,因為關乎人命,商業模式要考慮很多,涉及醫療倫理、品質、效率,林百里透露,廣達正在做研發病房及醫護平台,希望2020~2021年產品能落地試營運,但最大考驗仍然是行銷跟商業模式。
林百里解釋,因為若不做ODM,作品牌必須有行銷力,光做台灣市場太小,難以規模化,但若做全世界生意,醫療的品牌是最困難的,不僅要取得醫療認證及醫療機構認可,生意模式怎麼做?銷售人才如何培養?都是問題。
不過,林百里反而最看好AI與5G在三至五年內帶來的機會,他指出,5G背後的雲端機房跟4G不同,當各國發展5G將帶來非常大的需求,而當AI服務上路,Google等雲端客戶業務也將大成長,5G需要大量雲端,靠數量眾多的IoT收集資料,所以雲端跟IoT也將大成長。
在智慧製造方面,廣達林口總部又將興建新大樓,預計2020年第四季完工,這也成為智慧工廠實驗場域,林百里表示,廣達導入自動化機器人,加上AI檢驗,若未來導入5G,把感測器跟機器人都串連起來,能做大數據即時分析,即時決策,生產效率將提升20%,是非常大的商機。
「機器人也有聰明跟笨的!」林百里笑說,未來連線AI的是聰明機器人,而笨的機器人只會說「你好!你好!」毫無用途。
林百里早在20年前就看好行動聯網商機,率先投入筆電開發,現在則大力倡議AI戰略,梁次震補充:其實林百里早在31年前就已經想到廣達(Quanta)會做量子電腦(Quantum),所以創業取名廣達,林百里則笑著回應「廣達」就是取意產品既廣又能無線通達,兩位創辦人一番對談,讓外界多少能一窺科技前瞻領袖的先見之明。
醫師:癌症關鍵死因是它!每天1動作戰勝癌細胞
https://www.edh.tw/article/22819
有做有保佑!運動不能少,傳授你超簡單「強體力運動」
江坤俊醫師表示,除了飲食,運動對癌症病患來說也很重要,但正在接受化療的癌症病患,可能因為身體太過虛弱沒辦法做室外運動,或是怕走出室外容易被病菌感染。為此,江醫師教大家幾個簡易的室內運動,給癌症病患參考。江醫師教的運動叫做「拍手運動」,非常簡單:
這麼簡單的運動只要持續進行30分鐘,體力就會得到很大的改善。「千萬不要坐著不動,坐著不動癌細胞就會戰勝你喔!」
江醫師最後再次呼籲運動的重要性:「你只要有做(運動)就一定有保佑,多運動對你的身體一定有幫助。」
長庚醫院副教授級主治醫師江坤俊舉一個例子:曾有一位乳癌病患在化療期間測肝功能時,發現肝功能飆到2000多,本來醫生們以為是化療藥物的副作用,但換了藥物後肝功能還是沒有降下來。後來才發現,該病患有個遠房親戚買了很昂貴的補品給他補身子,正是那個補品傷了肝臟...
飲食對癌症病患來說是非常重要的大事,不僅不能隨便給他們吃補品,以免傷肝、傷腎造成治療的麻煩,營養攝取本身就是癌症治療會否成功的一大關鍵。
癌症很可怕,但為什麼可怕?一般人可能不夠清楚。江坤俊醫師講解癌症的二大致命原因:
大家總覺得吃紅肉、喝豆漿會致癌…醫師說:大錯特錯,反而鼓勵多吃肉、喝豆漿補充蛋白質!
關於癌症病患的飲食,民眾還是有一些迷思,江醫師舉出3個例子:
有做有保佑!運動不能少,傳授你超簡單「強體力運動」
江坤俊醫師表示,除了飲食,運動對癌症病患來說也很重要,但正在接受化療的癌症病患,可能因為身體太過虛弱沒辦法做室外運動,或是怕走出室外容易被病菌感染。為此,江醫師教大家幾個簡易的室內運動,給癌症病患參考。江醫師教的運動叫做「拍手運動」,非常簡單:
- 踏步
- 每踏一步你的手就拍打一下。
這麼簡單的運動只要持續進行30分鐘,體力就會得到很大的改善。「千萬不要坐著不動,坐著不動癌細胞就會戰勝你喔!」
江醫師最後再次呼籲運動的重要性:「你只要有做(運動)就一定有保佑,多運動對你的身體一定有幫助。」
長庚醫院副教授級主治醫師江坤俊舉一個例子:曾有一位乳癌病患在化療期間測肝功能時,發現肝功能飆到2000多,本來醫生們以為是化療藥物的副作用,但換了藥物後肝功能還是沒有降下來。後來才發現,該病患有個遠房親戚買了很昂貴的補品給他補身子,正是那個補品傷了肝臟...
飲食對癌症病患來說是非常重要的大事,不僅不能隨便給他們吃補品,以免傷肝、傷腎造成治療的麻煩,營養攝取本身就是癌症治療會否成功的一大關鍵。
癌症很可怕,但為什麼可怕?一般人可能不夠清楚。江坤俊醫師講解癌症的二大致命原因:
- 癌細胞會轉移、侵害器官如果得到肝癌,癌細胞把肝臟都佔據了,肝功能就沒有了,失去了解毒功能,人當然就會死亡。若是得到肺癌,癌細胞把肺都佔據了,人沒辦法呼吸當然也會死。至於乳癌,雖然乳房不是維持生命的器官,但乳癌細胞會轉移,若轉移到肝、肺等重要器官就可能致人於死。
- 癌細胞會與人體搶營養,使人虛弱癌細胞生長迅速,會與正常細胞搶奪營養,如果癌症病患沒有好好補充營養,常常會產生「癌症的惡體質」,變得非常虛弱。江醫師指出:「其實有很多癌症病患並非死於癌症本身,而是死於癌症的惡體質。」約有20%的癌症病患是死於營養不良。江醫師強調:「飲食對癌症病患來說真的非常重要!」曾有一位乳癌四期的病患,雖然她有求生意志,但當時對她真的是愛莫能助,因為她虛弱到沒辦法下床的程度。在這種狀況下,化療做下去身體也禁受不住。那位病人最後就眼睜睜地死於虛弱,連治療的機會也沒有。
大家總覺得吃紅肉、喝豆漿會致癌…醫師說:大錯特錯,反而鼓勵多吃肉、喝豆漿補充蛋白質!
關於癌症病患的飲食,民眾還是有一些迷思,江醫師舉出3個例子:
- 罹癌期間能不能吃肉、喝豆漿?大家總覺得吃紅肉不太好、認為吃紅肉會致癌;加上肉沒有煮熟的話怕有吃進寄生蟲的疑慮,對於做化療、抵抗力差的病患來說,可能會造成敗血症等疾病。因此,很多癌症病患以為在罹癌期間,特別是在化療時不能吃肉、尤其是紅肉。但這些觀念都是錯的。江醫師表示,化療最大的副作用是病患的白血球數量降低,白血球數量降低病患非常容易受到感染,所以一定要讓白血球數量回復,這時需要大量的蛋白質。因此,醫師非但不會禁止癌症病患吃肉,反而「強烈建議」一定要多吃肉,特別是紅肉。因為紅肉不但有助於生成白血球,而且紅肉裡面有鐵,也有助於生成紅血球。江坤俊醫師特別提到了豆漿。他指出,很多人以為得到乳癌後,含有類似女性荷爾蒙的食品不要去碰;但江醫師指出:「豆漿裡面的荷爾蒙是大豆異黃酮,它跟動物性的荷爾蒙是不一樣的。」所以他會建議病患,特別是乳癌的病患,如果喜歡喝豆漿,在化療的期間攝取豆漿。
- 飢餓療法有效嗎?癌細胞會跟身體搶營養,且搶得比正常細胞還快。有人認為病患吃東西補的是癌細胞而不是自己的身體,所以採用飢餓療法,想餓死癌細胞。江坤俊醫師「飢餓療法對癌細胞有沒有效?有效,只是對不起!你會先死才是癌細胞死!」江醫師強調,癌症病患一定要有充足的營養,有充足的營養才有充足的體力去接受化療。
- 生酮飲食可以抗癌?生酮飲食近年很流行,很多人利用生酮飲食來減肥,有一部分的人則利用生酮飲食來抗癌。為什麼生酮飲食會跟癌症連上關係呢?江坤俊醫師表示,因為國外有些研究團隊確實利用生酮飲食來治療腦部的癌症;但江醫師強調:第一,只有幾個報導證明生酮飲食抗癌有效;第二,現在只證實生酮飲食對腦癌有一定的幫忙,其他的癌症跟生酮飲食都沒有關係。
Golang: for .... range ...
https://play.golang.org/p/xDH3hPfYYwa
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
for k := range kvs {
fmt.Println("key:", k)
}
for i, c := range "go" {
fmt.Println(i, c)
}
}
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
for k := range kvs {
fmt.Println("key:", k)
}
for i, c := range "go" {
fmt.Println(i, c)
}
}
Javascript: 函數如何返回多組變數 Return multiple values in JavaScript?
https://stackoverflow.com/questions/2917175/return-multiple-values-in-javascript
Q. I am trying to return two values in JavaScript. Is that possible?
A.
No, but you could return an array containing your values:
A.
No, but you could return an array containing your values:
function getValues() {
return [getFirstValue(), getSecondValue()];
}
Then you can access them like so:
var values = getValues();
var first = values[0];
var second = values[1];
With the latest ECMAScript 6 syntax*, you can also destructure the return value more intuitively:
const [first, second] = getValues();
If you want to put "labels" on each of the returned values (easier to maintain), you can return an object:
function getValues() {
return {
first: getFirstValue(),
second: getSecondValue(),
};
}
And to access them:
var values = getValues();
var first = values.first;
var second = values.second;
Or with ES6 syntax:
const {first, second} = getValues();
* See this table for browser compatibility. Basically, all modern browsers aside from IE support this syntax, but you can compile ES6 code down to IE-compatible JavaScript at build time with tools like Babel.
JavaScript: Function 前面加上小括弧
https://freedomknight.me/javascript-ni-ming-han-shu/
如果沒有那個小括號,他就像是最一開始的宣告函數的樣子,直譯器期待你宣告一個函數的名稱。
而小括號對 js 來講,算是個強迫運算,像我們平時運算 (2 + 3) * 5 會用小括號先強迫把 2 + 3 算出來。
對這個小括弧而言,則是強迫生出個匿名函數並且呼叫
其實形式上還是一樣,單純只是變形的呼叫。這時使用匿名的最大大大優點就是「船過水無痕」。 任何你在裡頭做的事情,所佔的任何空間,到最後會馬上釋放。
在小型 js 看不出來,但是在大型一點的程式,大家一股腦的宣告了一堆全域函數跟變數,如果瀏覽器還沒離開這個頁面,這些空間就一直被佔著,無法釋放。
如果沒有那個小括號,他就像是最一開始的宣告函數的樣子,直譯器期待你宣告一個函數的名稱。
而小括號對 js 來講,算是個強迫運算,像我們平時運算 (2 + 3) * 5 會用小括號先強迫把 2 + 3 算出來。
對這個小括弧而言,則是強迫生出個匿名函數並且呼叫
具有名稱時我們是如此呼叫:
add(1, 2);
現在是匿名函數:
(匿名函數)(1, 2);
其實形式上還是一樣,單純只是變形的呼叫。這時使用匿名的最大大大優點就是「船過水無痕」。 任何你在裡頭做的事情,所佔的任何空間,到最後會馬上釋放。
在小型 js 看不出來,但是在大型一點的程式,大家一股腦的宣告了一堆全域函數跟變數,如果瀏覽器還沒離開這個頁面,這些空間就一直被佔著,無法釋放。
JavaScript: ECMAScript 相容性
| 腳本引擎 | 參考應用程式 | 相容性[11] | |||
|---|---|---|---|---|---|
| ES5[12] | ES6[13] | ES7[14] | 較新(2016+)[14][15] | ||
| 100% | 96% | 100% | 58% | ||
| 100% | 98% | 100% | 78% | ||
| 100% | 98% | 100% | 100% | ||
| 99% | 99% | 100% | 90% | ||
JavaScript: SHA256, byte array, 16進制字串 轉換等
JavaScript SHA-256 demo: https://geraintluff.github.io/sha256/
https://github.com/emn178/js-sha256
到 https://cdnjs.com/ 搜尋
=> https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js
A simple SHA-256 / SHA-224 hash function for JavaScript supports UTF-8 encoding.
It also supports byte `Array`, `Uint8Array`, `ArrayBuffer` input
https://github.com/brix/crypto-js
https://code.google.com/archive/p/crypto-js/
https://blog.zhengxianjun.com/2015/05/javascript-crypto-js/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/enc-base64.min.js"></script>
var hash = CryptoJS.SHA256("Message").toString();
https://github.com/brix/crypto-js/issues/157
var hash = CryptoJS.HmacSHA256("Message", "key");
// Message 是按照使用者的規則拼接的字串
// key為設置密鑰
https://github.com/digitalbazaar/forge
<script src="https://cdn.jsdelivr.net/npm/node-forge@0.7.0/dist/forge.min.js"></script>
<script type="text/javascript">
"use strict";
var md = forge.md.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
console.log(hexToBytes(md.digest().toHex()));
console.log(bytesToHex(hexToBytes(md.digest().toHex())));
// Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
</script>
//十六进制字符串转字节数组
function StrToBytes(str) {
var pos = 0;
var len = str.length;
if(len %2 != 0) {
return null;
}
len /= 2;
var hexA = new Array();
for(var i=0; i<len; i++) {
var s = str.substr(pos, 2);
var v = parseInt(s, 16);
hexA.push(v);
pos += 2;
}
return hexA;
}
//字节数组转十六进制字符串
function BytesToStr(arr) {
var str = "";
for(var i=0; i<arr.length; i++) {
var tmp = arr[i].toString(16);
if(tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
javascript 使用 toString 10進位轉2或8或16進位.
toString():預設是10進位轉其它進位,同時,配合正則,可匹配字符。
範例:
<script language="javascript">
var x = 100;
var y1 = x.toString(2);
var y2 = x.toString(8);
var y3 = x.toString(16);
var z = "2進位 = " + y1 + "<br>8進位 = " + y2 + "<br>16進位 = " + y3;
document.write(z);
</script>
https://github.com/emn178/js-sha256
到 https://cdnjs.com/ 搜尋
=> https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js
A simple SHA-256 / SHA-224 hash function for JavaScript supports UTF-8 encoding.
It also supports byte `Array`, `Uint8Array`, `ArrayBuffer` input
https://github.com/brix/crypto-js
https://code.google.com/archive/p/crypto-js/
https://blog.zhengxianjun.com/2015/05/javascript-crypto-js/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/enc-base64.min.js"></script>
var hash = CryptoJS.SHA256("Message").toString();
https://github.com/brix/crypto-js/issues/157
var hash = CryptoJS.HmacSHA256("Message", "key");
// Message 是按照使用者的規則拼接的字串
// key為設置密鑰
https://github.com/digitalbazaar/forge
<script src="https://cdn.jsdelivr.net/npm/node-forge@0.7.0/dist/forge.min.js"></script>
<script type="text/javascript">
"use strict";
var md = forge.md.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
console.log(hexToBytes(md.digest().toHex()));
console.log(bytesToHex(hexToBytes(md.digest().toHex())));
// Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
</script>
//十六进制字符串转字节数组
function StrToBytes(str) {
var pos = 0;
var len = str.length;
if(len %2 != 0) {
return null;
}
len /= 2;
var hexA = new Array();
for(var i=0; i<len; i++) {
var s = str.substr(pos, 2);
var v = parseInt(s, 16);
hexA.push(v);
pos += 2;
}
return hexA;
}
//字节数组转十六进制字符串
function BytesToStr(arr) {
var str = "";
for(var i=0; i<arr.length; i++) {
var tmp = arr[i].toString(16);
if(tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
javascript 使用 toString 10進位轉2或8或16進位.
toString():預設是10進位轉其它進位,同時,配合正則,可匹配字符。
範例:
<script language="javascript">
var x = 100;
var y1 = x.toString(2);
var y2 = x.toString(8);
var y3 = x.toString(16);
var z = "2進位 = " + y1 + "<br>8進位 = " + y2 + "<br>16進位 = " + y3;
document.write(z);
</script>
Golang 同時監聽 TCP 與 HTTP 多個端口 提供服務
https://javasgl.github.io/golang-listen-tcp-and-http-port/
项目入口 main.go 中,之前是监听 TCP 连接,代码大致如下:
现在需要在此基础之上监听 http 连接,由于 http.ListenAndServe() 方法是阻塞的,所以需要新开goroutine进行监听,代码示意如下:
Golang 学习笔记:HTTP, TCP/IP, UDP
http://blog.wuxu92.com/go-lang-notebook-2/
應詳閱出處,相當詳細
项目入口 main.go 中,之前是监听 TCP 连接,代码大致如下:
func main(){ server,err:= net.Listen("tcp","host:port") if err!=nil{ return } defer server.Close() for{ conn,err:= server.Accept() if err!=nil{ continue } go handleConn(conn) } } func handleConn(conn net.Conn){ //do somethings }
现在需要在此基础之上监听 http 连接,由于 http.ListenAndServe() 方法是阻塞的,所以需要新开goroutine进行监听,代码示意如下:
func main(){ http.HandleFunc("/hi", Router) //因为会阻塞,所以需要新开goroutine进行监听 go http.ListenAndServe("host:http_port", nil) server,err:= net.Listen("tcp","host:tcp_port") if err!=nil{ return } defer server.Close() for{ conn,err:= server.Accept() if err!=nil{ continue } go handleConn(conn) } } func handleConn(conn net.Conn){ //do somethings }
Golang 学习笔记:HTTP, TCP/IP, UDP
http://blog.wuxu92.com/go-lang-notebook-2/
應詳閱出處,相當詳細
import ( . "fmt" "net/http" ) const ( PORT = ":1024" MSG = "hello, gopher" ) func main() { http.HandleFunc("/hello", Hello) go func() { http.ListenAndServe(PORT, nil) }() go func() { http.ListenAndServeTLS(":443","cert.pem", "key.pem", nil) }() for {} } func Hello(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") Fprintf(w, MSG) }
- http.HandleFunc(string, func(http.ResponseWriter, *http.Request)) 绑定某个请求路径的处理方法
- 使用goroutine执行监听与服务动作。启动了两个goroutine
- 最后使用一个for循环保证主线程不会退出。因为主线程退出的话goroutine也会结束。
- 其中的TLS支持的HTTPS服务的cert和key文件可以使用Go源码中带的文件生成
go run $GOROOT/src/crypto/tls/generate_cert.go -ca=true -host="localhost"
最后一个无限循环可以使用channel代替,可以使用sync.WaitGroup实现, sync.WaitGroup持有一个计数器,其
Add(int)方法增加计数器,其Done()方法减小计数器,其Wait()方法会让线程阻塞等待计数器恢复0。其实现如下。import ( . "fmt" "net/http" "sync" ) const ( PORT = ":1024" MSG = "hello, gopher" ) var servers sync.WaitGroup func main() { http.HandleFunc("/hello", Hello) Launch(func() { http.ListenAndServe(PORT, nil) }) Launch(func() { http.ListenAndServeTLS(":443","cert.pem", "key.pem", nil) }) servers.Wait() } func Launch(f func()) { servers.Add(1) go func() { defer servers.Done() f() }() } func Hello(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") Fprintf(w, MSG) } |
使用上面的方法可以添加任意多的监听端口。可以通过Server做更多的配置。
s := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Fatal(s.ListenAndServe()) |
HTTP与Go程结合
我们对Goroutine和HTTP都比较熟悉了,我们可以设置一个任务队列,让每一个请求都进入这个队列然后使用一定数量的Goroutine去执行逻辑。这样效率比上面的实现要高很多。
发送GET/POST请求
http保重提供了非常方便的发发送get请求和post请求的封装。
resp, err := http.Get("http://example.com/") ... resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) ... resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}}) defer resp.Body.Close() // 不要忘记defer关闭资源 body, err := ioutil.ReadAll(resp.Body) // 读取资源 strCont := string(body) |
环境变量
使用os包中的
Getenv(string)和Setenv(string, string)方法获得和设置环境变量。要注意Setenv的作用域只是当前运行进程,结束后或者重新运行后环境变量会重置为系统设置值。Println(os.Getenv("PATH")) os.Setenv("GOOO", "gofast") os.Setenv("PATH", ".") Println(os.Getenv("PATH")) |
操作系统信号
当程序在命令行中运行的时候,一般使用
Ctrl+c可以终结程序运行。对于操作系统有一定了解的话应该知道,Ctrl+c向运行的进程发送了一个结束进程的signal。这种方式很有用,但是有点时候暴力结束进程执行不是我们想要的逻辑,我们可以捕获这个termination signal,然后对他做一些自己的处理。注意,windows下不能捕获这些信号,只在*nix下有效。
使用os/signal包提供的封装。
package main import ( "fmt" "os" "os/signal" "time" ) var i int func init() { go SignalHandler(make(chan os.Signal, 1)) } func main() { i = 0 for { i++ time.Sleep(time.Second) } } func SignalHandler(c chan os.Signal) { signal.Notify(c, os.Interrupt) for s:= <-c;; s= <-c { switch s { case os.Interrupt: fmt.Println("interrupt received", i) os.Exit(0) // 去掉这一行,那么Ctrl+c的快捷键将不能中断进程运行 default: fmt.Println("default case") } } } |
运行上面的代码,在Ctrl-c时可以看到输出 interrupt received 字符串。
除了接收系统的信号,还可以向其他的进行发送信号。
syscall.Kill(syscall.Getpid(), syscall.SIGABRT)
|
这一部分可能暂时用不上,只要知道有这种用法就可以了。
TCP/IP
使用net提供的TCP/IP封装可以很方便的监听与请求端口。
server(src/wuxu/tcpserver/main.go):
package main import ( "net" "fmt" ) var i int func main() { i = 0 if listener, e := net.Listen("tcp", ":1024"); e == nil { for { if conn, e := listener.Accept(); e==nil { go func(c net.Conn) { defer c.Close() fmt.Fprintln(c, "hihi", i) i++ }(conn) } } } else { fmt.Println("error") } } |
client(src/wuxu/tcpclient/main.go)
package main import ( "fmt" "net" "bufio" ) func main() { if conn, e := net.Dial("tcp", "localhost:1024"); e == nil { defer conn.Close() if text, e := bufio.NewReader(conn).ReadString('\n'); e ==nil { fmt.Println(text) } } else { fmt.Println("error", e) } } |
加密的TCP通信
还可以很方便地简历加密的连接,为了加密连接我们需要两套密钥对,即服务器的公钥与私钥,客户端的公钥与私钥,使用之前的方法再在服务器和客户端的目录下,各生成一对密钥。
修改代码如下:
go run $GOROOT/src/crypto/tls/generate_cert.go -ca=true -host="localhost"修改代码如下:
server:
package main import ( "fmt" "crypto/tls" "crypto/rand" ) var i int func main() { i = 0 var config tls.Config if certificate, e := tls.LoadX509KeyPair("cert.pem", "key.pem"); e ==nil { config = tls.Config{ Certificates: []tls.Certificate{certificate}, Rand: rand.Reader, } } if listener, e := tls.Listen("tcp", ":1024", &config); e == nil { for { if conn, e := listener.Accept(); e==nil { go func(c *tls.Conn) { defer c.Close() fmt.Fprintln(c, "secure hihi", i) i++ }(conn.(*tls.Conn)) } } } else { fmt.Println("error") } } |
可以看出,引入tls包后不需要net包了,tls实现了相关得了协议的加密版本。
client:
package main import ( "fmt" "bufio" "crypto/tls" ) func main() { var config tls.Config if certificate, e := tls.LoadX509KeyPair("cert.pem", "key.pem"); e == nil { config = tls.Config{ Certificates: []tls.Certificate{certificate}, InsecureSkipVerify: true, } } if conn, e := tls.Dial("tcp", "localhost:1024", &config); e == nil { defer conn.Close() if text, e := bufio.NewReader(conn).ReadString('\n'); e ==nil { fmt.Println(text) } } else { fmt.Println("error", e) } } |
和使用net包的整体流程差不多,使用tls包的实现替换net包。
这样我们就实现了一个使用tls密钥加密的传输通道了。
这样我们就实现了一个使用tls密钥加密的传输通道了。
UDP
UDP的使用和TCP类似也包括一种加密的UDP传输。具体UDP和TCP的区别就不详细说了,这部分是考试面试的重点,一般都能说个123出来。
与TCP实现不一样的是,UDP要先使用
net.ResolveUDPAddr(string, string)来获得一个*net.UDPAddr,然后使用net.ListenUDP("udp", addr)监听这个端口的UDP通信。一个简单实现如下:import ( . "fmt" "net" "os" ) var MSG = ([]byte)("hello golong udp\n") func main() { if addr, e := net.ResolveUDPAddr("udp", ":1026"); e == nil { if server, e := net.ListenUDP("udp", addr); e == nil { // infinit loop for buffer := MakeBuffer(); ; buffer = MakeBuffer() { if n, client, e := server.ReadFromUDP(buffer); e == nil { go func( c *net.UDPAddr, packet []byte) { if n, e := server.WriteToUDP(MSG, c); e == nil { Printf("%v bytes written to %v\n", n, c) } }(client, buffer[:n]) } else { Println("error1", e) } } } else { Println("error2", e) } } else { Println("error3", e) } } func MakeBuffer() ([]byte) { return make([]byte, 1024) } |
作为server需要一个无限循环保持监听,每当收到一个客户端请求(
ReadFromUDP),就启动一个Goroutine服务这个请求(WriteToUDP)。
UDP不想TCP服务可以用telnet连接,因为它是一个无连接的通信,我们需要自己写一个请求的客户端。client也比较简单,其实现如下:
import ( . "fmt" "net" "bufio" ) var CRLF = ([]byte)("\n") func main() { if addr, e := net.ResolveUDPAddr("udp", "localhost:1026"); e == nil { if server, e := net.DialUDP("udp", nil, addr); e == nil { defer server.Close() // 随意发送一些内容到server等待回应 // 这里可以多次(for 循环写都可以)写内容到服务器 if _, e := server.Write(CRLF); e == nil { if text, e := bufio.NewReader(server).ReadString('\n'); e == nil { Printf("%v", text) } } } else { Println("error2", e) } } else { Println("error3", e) } } |
这里注意注释里面的话,使用
Write()方法向UDP Server发送一些内容,然后才会受到Server的返回,返回使用bufio读取。一个UDP的Dial实例可以多起发送数据和读取返回。使用加密的UDP
Golang提供了很方便的加密UDP连接,只需要提供一个密钥,就可以对传输的数据进行加密了,当然我们需要对server和client的数据都进行修改。
rsa encrypt server:
rsa encrypt server:
import ( . "fmt" "bytes" "crypto/rand" "crypto/rsa" "crypto/sha1" "encoding/gob" . "net" ) var MSG = []byte("hello go scure udp") var RSA_LABEL = []byte("server") func main() { Serve(":1025", func(conn *UDPConn, c *UDPAddr, packet *bytes.Buffer) (n int) { var key rsa.PublicKey if e := gob.NewDecoder(packet).Decode(&key); e==nil { if resp, e := rsa.EncryptOAEP(sha1.New(), rand.Reader, &key, MSG, RSA_LABEL); e==nil { n, _ = conn.WriteToUDP(resp, c) } } return }) } func Serve(addr string, f func(*UDPConn, *UDPAddr, *bytes.Buffer) int) { Launch(addr, func(conn *UDPConn) { for { buffer := make([]byte, 1024) if n, client, e := conn.ReadFromUDP(buffer); e == nil { go func(c *UDPAddr, b []byte) { if n := f(conn, c, bytes.NewBuffer(b)); n!=0 { Println(n, "write to ", c) } }(client, buffer[:n]) } } }) } func Launch(addr string, f func(*UDPConn)) { if a, e := ResolveUDPAddr("udp", addr); e == nil { if server, e := ListenUDP("udp", a); e == nil { f(server) } } } |
rsa used client
import ( . "fmt" "bytes" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/gob" "encoding/pem" "io/ioutil" . "net" ) var RSA_LABEL = []byte("server") func main() { Connect("localhost:1025", func(server *UDPConn, priKey *rsa.PrivateKey) { cipherText := MakeBuffer() if n, e := server.Read(cipherText); e == nil { Println("cipher text", string(cipherText)) // cipherText是加密的数据,需要解密 if plainText, e := rsa.DecryptOAEP(sha1.New(), rand.Reader, priKey, cipherText[:n], RSA_LABEL); e == nil { Println("receive decrypt string:", string(plainText)) } } }) } func Connect(address string, f func(*UDPConn, *rsa.PrivateKey)) { LoadPrivateKey("key.pem", func(pk *rsa.PrivateKey) { // pk is private key if addr, e := ResolveUDPAddr("udp", address); e ==nil { if server, e := DialUDP("udp", nil, addr); e == nil { defer server.Close() SendKey(server, pk.PublicKey, func() { f(server, pk) }) } else { Println("err1", e) } } else { Println("err2", e) } }) } func LoadPrivateKey(file string, f func(*rsa.PrivateKey)) { if file, e := ioutil.ReadFile(file); e == nil { if block, _ := pem.Decode(file); block != nil { if block.Type == "RSA PRIVATE KEY" { if key, _ := x509.ParsePKCS1PrivateKey(block.Bytes); key !=nil { f(key) } } } } else { Println(e) } return } func SendKey(server *UDPConn, publicKey rsa.PublicKey, f func()) { var encodedKey bytes.Buffer if e := gob.NewEncoder(&encodedKey).Encode(publicKey); e == nil { if _, e = server.Write(encodedKey.Bytes()); e == nil { f() } } } func MakeBuffer() (r []byte) { return make([]byte, 1024) } |
需要注意的几点:
- RSA_LABEL 是rsa加密使用的一个label,需要客户端和服务器端一致
- 请求流程是客户端读取一个密钥,然后获取一个publickey,使用gob序列化后发送到server, server读取出key,使用这个publicKey对返回的数据进行加密
- 客户端读取返回后,使用密钥进行解密,读取明文
- 客户端代码使用了大量的回调,方法字面量,理解起来有点麻烦,可以用javascript的回调的思路去理解
訂閱:
文章 (Atom)
程式語言編年史
程式語言編年史原文 下面這張圖片描繪了整個程式語言的歷史。包括各種程式語言的發明人、程式語言的特點和適用領域、被什麼網站或公司使用等 (檢視 完整高清圖 )。 之所以會有那麼多不同的程式語言是因為設計程式語言的初衷不同、對語言學習曲線的追求不同、不同程式之間的執行成本差異...


