文:宋瑞文(加州能源特約撰述)
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
歐洲環保署(European Environment Agency)最新統計顯示,歐洲掀起電動車革命,其中又以法國最為熱銷,佔了歐盟電動車市場的四分之一以上。 資料顯示,2014 年已登記的電動車大約有 3 萬 8 千輛,相較於 2013 年上升了 57%。其中,法國的電動車數量居全歐洲之冠,目前國內共有超過 1 萬 700 輛,德國以 8,500 輛位居第二,英國則以 6,700 輛暫居第三。 這項報告不但證明歐洲現今電動車市場方興未艾,也發現 2014 年售出的新車所排放的二氧化碳量,平均較 2013 年售出的車款少了 2.6%。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
![]() |
美國豪華電動車製造商特斯拉 (Tesla) 跨界家用電池,德國汽車碳纖維生產商西格里集團 (SGL Carbon SE) 在投資人期待該公司可望因此受惠的激勵下,股價創下近三個月以來最大單日漲幅。 SGL 主要是為日立、Panasonic 這些日本電子大廠供應石墨陽極材料,而日立、Panasonic 則會將電池元件賣給特斯拉。 Bankhaus Lampe 分析師 Marc Gabriel 說,SGL 是少數幾家能因電池需求增溫而受惠的德國業者。根據報導,SGL 發言人已確認,該公司的確是日立、Panasonic 的石墨陽極材料供應商。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
在本章節中,我們將探討TCP協議基於流式傳輸的最大一個問題,即粘包問題。本章主要介紹TCP粘包的原理與其三種解決粘包的方案。並且還會介紹為什麼UDP協議不會產生粘包。
我們準備做一個可以在Client端遠程執行Server端shell命令並拿到其執行結果的程序,而涉及到網絡通信就必然會出現socket模塊,關於如何抉擇傳輸層協議的選擇?我們選擇使用TCP協議,因為它是可靠傳輸協議且數據量支持比UDP協議要大。好了廢話不多說直接上代碼了。
Server端代碼如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Server ==== import subprocess from socket import * server = socket(AF_INET, SOCK_STREAM) server.bind(("0.0.0.0",6666)) # 放在遠程填入0.0.0.0,放在本地填入127.0.0.1 server.listen(5) while 1: # 鏈接循環 conn,client_addr = server.accept() while 1: # 通信循環 try: # 防止Windows平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 cmd = conn.recv(1024) if not cmd: # 防止類Unix平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) stdout_res = res.stdout.read() # 正確結果 stderr_res = res.stderr.read() # 錯誤結果 # subprocess模塊拿到的是bytes類型,所以直接發送即可 cmd_res = stdout_res if stdout_res else stderr_res # 因為兩個結果只有一個有信息,所以我們只拿到有結果的那個 conn.send(cmd_res) except Exception: break conn.close() # 由於client端鏈接異常,故關閉鏈接循環
Client端代碼如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Client ==== from socket import * client = socket(AF_INET,SOCK_STREAM) client.connect(("xxx.xxx.xxx.xxx",6666)) # 填入Server端公網IP while 1: cmd = input("請輸入命令>>>:").strip() if not cmd: continue if cmd == "quit": break client.send(cmd.encode("utf-8")) cmd_res = client.recv(1024) # 本次接收1024字節數據 print(cmd_res.decode("utf-8")) # 如果Server端是Windows則用gbk解碼,類Unix用utf-8解碼 client.close()
測試結果:
上面的測試一切看起來都非常完美,但是是有一個BUG的。當我們如果讀取一條非常長的命令實際上是會出問題的,比如:
這種現象被稱之為粘包,那麼為何會產生這樣的現象呢?
這是由於
recv()沒有一次性讀取完整個內核緩衝區的內容導致的。其實歸根結底還是怪TCP是字節流方式傳輸數據。
我們來解析一下這種現象產生的原因:
由於我們的recv()只是按照固定的1024去讀取數據,那麼一旦整體內核緩衝區中所存儲的整體數據大於1024,就會產生粘包現象。所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。
這裏我還畫了一幅圖,可以方便讀者理解:
那麼我們可以通過不斷的增大recv()中的讀取範圍來解決這個問題嗎?就像對應上圖中的,一次性把快遞櫃包裹全取完,答案是不可以!你再大你也不可能大過內核緩衝區,這個東西都是有一個一定的閾值。一旦超出了這個閾值就會引發異常或者乾脆無效。那麼有什麼好的辦法呢?哈,下面會教給你一些解決辦法的。不過在此之前我們要先看一個TCP協議特有的Nagle算法。
基於TCP協議的socket通信有一個特點,即:一方的
send()與另一方的recv()可以沒有任何關係,即:一方send()三次,另一方recv()一次就可以將數據全部取出來。
TCP協議的發送方有一個特徵。他會進行組包,如果一次發送的數據量很小,比如第一次發送10個字節,第二次發生2個字節,第三次發生3個字節。他可能會將這15個字節湊到一塊發送出去,這是採用了Nagle算法來進行的,這麼做有一個弊端就是接收方想要將這個大的數據包按照發送方的發送次數精確無誤的接收拆分成10 2 3必須要有發送方提供的拆包機制才行。
如下圖組所示
發送方:
from socket import * ip_port = ("127.0.0.1",12306) buffer_size = 1024 back_log = 5 server = socket(AF_INET,SOCK_STREAM) server.bind(ip_port) server.listen(back_log) conn,addr = server.accept() conn.send("hello,".encode("utf-8")) # 第一次發送是6Bytes的數據 conn.send("world,".encode("utf-8")) # 第二次也是6Bytes的數據 conn.send("yunyaGG!!".encode("utf-8")) # 第三次是9Bytes的數據
接收方:
from socket import * ip_port = ("127.0.0.1",12306) buffer_size = 1024 client = socket(AF_INET,SOCK_STREAM) client.connect(ip_port) data_1 = client.recv(buffer_size) # 我們讀取數據時統一用設定的 buffer_size 來讀取 print("這是第一次的數據包:",data_1.decode("utf-8")) data_2 = client.recv(buffer_size) print("這是第二次的數據包:",data_2.decode("utf-8")) data_3 = client.recv(buffer_size) print("這是第三次的數據包:",data_3.decode("utf-8"))
接收結果:
# ==== 執行結果 ==== """ 這是第一次的數據包: hello, 這是第二次的數據包: world,yunyaGG!! 這是第三次的數據包: """
和預想的有點不太一樣哈,居然把第二次和第三次組成了一個大的數據包發送過來了。這就是Nagle算法,這樣的組包策略很容易就會產生粘包。我不知道你是以什麼樣的方式發過來的,所以我recv()就只能按照自己設定的方式去接收。
現在思考一下粘包的思路,我們的發送方需要將切分解包的規則告訴給接收方。
我們嘗試改一下每一次的buffer_size接收大小:
接收方:
from socket import * ip_port = ("127.0.0.1",12306) buffer_size = 1024 client = socket(AF_INET,SOCK_STREAM) client.connect(ip_port) data_1 = client.recv(6) # 我們手動的按照對方發送時的規則來進行拆包 print("這是第一次的數據包:",data_1.decode("utf-8")) data_2 = client.recv(6) print("這是第二次的數據包:",data_2.decode("utf-8")) data_3 = client.recv(9) print("這是第三次的數據包:",data_3.decode("utf-8"))
接收結果:
# ==== 執行結果 ==== """ 這是第一次的數據包: hello, 這是第二次的數據包: world, 這是第三次的數據包: yunyaGG!! """
粘包被我們手動的計算字節數來精確的分割數據接受量的大小給解決了,但是這樣做是不現實的..我們不可能知道對方發送的數據到底是怎麼樣的,更不用說手動計算。所以有沒有更好的解決方案呢?
好了,其實上面關於解決粘包的思路已經出來了。我們需要做的就是讓接收方知道本次發送內容的大小,接收方才能夠精確的將所有數據全部提取出來不產生遺漏。其實實現方式很簡單,可以嘗試以下思路:
1.發送方發送一個此次數據固定的長度
2.接收方接收到該數據長度並且回應
3.發送方收到回應並且發送真正的數據
4.接收方不斷的用默認的
buffer_size值接收新的數據並存儲起來直到超出整個數據的長度,代表此處數據全部接收完畢
Server端:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Server ==== import subprocess from socket import * server = socket(AF_INET, SOCK_STREAM) server.bind(("0.0.0.0", 6666)) # 放在遠程填入0.0.0.0 放在本地測試填入127.0.0.1 server.listen(5) while 1: # 鏈接循環 conn, client_addr = server.accept() while 1: # 通信循環 try: # 防止Windows平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 cmd = conn.recv(1024) if not cmd: # 防止類Unix平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout_res = res.stdout.read() # 正確結果 stderr_res = res.stderr.read() # 錯誤結果 # subprocess模塊拿到的是bytes類型,所以直接發送即可 cmd_res = stdout_res if stdout_res else stderr_res # 因為兩個結果只有一個有信息,所以我們只拿到有結果的那個 msg_length = len(cmd_res) # 本次數據的長度 conn.send(str(msg_length).encode("utf-8")) # 先將要發的整體內容長度發送過去 if conn.recv(1024) == b"ready": # 如果接收方回應了ready則開始發送真正的數據體 conn.send(cmd_res) except Exception: break conn.close() # 由於client端鏈接異常,故關閉鏈接循環
Client端:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Client ==== from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(("xxx.xxx.xxx.xxx", 6666)) # 填入Server端公網IP while 1: cmd = input("請輸入命令>>>:").strip() if not cmd: continue if cmd == "quit": break client.send(cmd.encode("utf-8")) msg_length = int(client.recv(1024).decode("utf-8")) # 接收到此次發送內容的整體長度 recv_length = 0 # 代表已接收的內容長度 cmd_res = b"" client.send(b"ready") # 發送給Server端,代表自己已經接收到此次內容長度,可以發送真正的數據啦 while recv_length < msg_length: cmd_res += client.recv(1024) # 本次接收1024字節數據,可能是一小節數據 recv_length += len(cmd_res) # 添加上本次讀取的長度,當全部讀取完后應該 recv_length == msg_length else: print(cmd_res.decode("utf-8")) # 如果Server端是Windows則用gbk解碼,類Unix用utf-8解碼 client.close()
結果如下:
其實上面的解決方案還是有一些弊端,因為Server端是發送了2次send(),第1次發送數據整體長度,第2次發送數據內容主體,這樣其實是不太好的(Server端可能同時處理多個鏈接,所以send()次數越少越好),而且如果Server端傳的是一個文件的話那麼局限性就太強了。因為我們只能將整體的消息長度發送過去而諸如文件名,文件大小之內的信息就發送不過去。
所以我們需要一個更加完美的解決方案,即Server端發送一次send()就將本次的數據整體長度發送過去(還可以包括文件姓名,文件大小等信息。)
struct模塊使用介紹
struct模塊可以將其某一種數據格式序列化為固定長度的Bytes類型,其中最重要的兩個方法就是pack()、unpack()。
pack(fmt,*args): 根據格式將其轉換為Bytes類型
unpack(fmt,string):根據格式將Bytes類型數據反解為其原本的形式
| 格式 | C語言類型 | Python類型 | 字節數大小 |
|---|---|---|---|
| x | 填充字節 | 沒有值 | |
| c | char | 字節長度為1 | 1 |
| b | signed char | 整數 | 1 |
| B | unsigned char | 整數 | 1 |
| ? | _Bool | bool | 1 |
| h | short | 整數 | 2 |
| H | unsigned short | 整數 | 2 |
| i | int | 整數 | 4 |
| I | unsigned int | 整數 | 4 |
| l | long | 整數 | 4 |
| L | unsigned long | 整數 | 4 |
| q | long long | 整數 | 8 |
| Q | unsigned long long | 整數 | 8 |
| n | ssize_t | 整數 | |
| N | size_t | 整數 | |
| f | float | 浮點數 | 4 |
| d | double | 浮點數 | 8 |
| s | char[] | 字節 | |
| p | char[] | 字節 | |
| P | void * | 整數 |
使用演示:
>>> import struct >>> b1 = struct.pack("i",12) # 嘗試將 int類型的12進行序列化,得到一個4字節的對象 >>> b1 b'\x0c\x00\x00\x00' >>> struct.unpack("i",b1) # 嘗試將12的序列化對象字節進行反解,得出元組,第1位就是需要的數據。 (12,) >>>
好了,了解到這裏我們就可以開始進行改寫了。
Server端代碼如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Server ==== import json import struct import subprocess from socket import * server = socket(AF_INET, SOCK_STREAM) server.bind(("0.0.0.0", 6666)) # 放在遠程填入0.0.0.0 放在本地測試填入127.0.0.1 server.listen(5) while 1: # 鏈接循環 conn, client_addr = server.accept() while 1: # 通信循環 try: # 防止Windows平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 cmd = conn.recv(1024) if not cmd: # 防止類Unix平台下Client端異常關閉導致雙向鏈接崩塌Server端異常的情況發生 break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout_res = res.stdout.read() # 正確結果 stderr_res = res.stderr.read() # 錯誤結果 # subprocess模塊拿到的是bytes類型,所以直接發送即可 cmd_res = stdout_res if stdout_res else stderr_res # 因為兩個結果只有一個有信息,所以我們只拿到有結果的那個 # 解決粘包:構建字典,包含數據主體長度,這個就相當於其頭部信息 head_msg = { "msg_length": len(cmd_res), # 包含數據主體部分的長度 # 如果是文件,還可以添加file_name,file_size等屬性。 } # 序列化成json格式,並且統計其頭部的長度 head_data = json.dumps(head_msg).encode("utf-8") head_length = struct.pack("i", len(head_data)) # 得到4字節的頭部信息,裡面包含頭部的長度 # 發送頭部長度信息,頭部數據,與真實數據部分 conn.send(head_length + head_data + cmd_res) except Exception: break conn.close() # 由於client端鏈接異常,故關閉鏈接循環
Client端代碼如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ==== 基於TCP協議的socket實現遠程命令輸入之Client ==== import json import struct from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(("xxx.xxx.xxx.xxx", 6666)) # 填入Server端公網IP while 1: cmd = input("請輸入命令>>>:").strip() if not cmd: continue if cmd == "quit": break client.send(cmd.encode("utf-8")) # 發送終端命令 # 解決粘包 head_length = struct.unpack("i", client.recv(4))[0] # 接收到頭部的長度信息 head_data = json.loads(client.recv(head_length)) # 接收到真實的頭部信息 msg_length = head_data["msg_length"] # 獲取到數據主體的長度信息 recv_length = 0 # 代表已接收的內容長度 cmd_res = b"" # 開始獲取真正的數據主體信息 while recv_length < msg_length: cmd_res += client.recv(1024) # 本次接收1024字節數據,可能是一小節數據 recv_length += len(cmd_res) # 添加上本次讀取的長度,當全部讀取完后應該 recv_length == msg_length else: print(cmd_res.decode("utf-8")) # 如果Server端是Windows則用gbk解碼,類Unix用utf-8解碼 client.close()
思想如下:
1.Server端構建自身的數據頭部分,其中包含數據體整體長度,如果傳輸的是文件的話還可以包含文件名,文件大小等信息
2.將數據頭部分
json序列化后再轉換為Bytes類型3.使用
struct.pack()模塊獲取數據頭的長度,得到一個長度為4的Bytes類型4.Server端將 數據頭長度 + 數據頭部分 + 數據體部分 全部發送給Client端
5. Client端
recv()接收值改為4,拿到數據頭長度Bytes類型6. Client端使用
struct.unpack(數據頭長度Bytes類型)模塊反解出數據頭真實的長度7. Client端使用
recv()接收值為數據頭真實的長度拿到真正的數據頭8. 通過
json反序列化出真正的數據頭,在到其中取出數據體的長度9. 開始
while循環不斷的讀取真實的數據體數據
上面那麼做看似完美但還是美中不足。因為內存緩衝區本來就是只能取一次值,和迭代器很像,只能迭代一次便不能繼續迭代了。基於這一點我們來做一個終極優化:
還記得iter()方法嗎?iter()方法除開創建迭代器外實際上還有一個參數:
def iter(source, sentinel=None): # known special case of iter """ iter(iterable) -> iterator iter(callable, sentinel) -> iterator Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence. In the second form, the callable is called until it returns the sentinel. """ pass
我們來試試這個參數做什麼用的。
li = [1, 2, 3, 4] def my_iter(): return li.pop() res = iter(my_iter, 2) # 代表這個迭代器沒__next__一下就會執行my_iter函數,並且該函數返回值如果是2則終止迭代 print(res.__next__()) # 4 print(res.__next__()) # 3 print(res.__next__()) # StopIteration
第二個參數看來可以設置迭代的終點。
那麼偏函數是什麼呢?偏函數可以設定一個固定的參數給第一個位置的值
效果如下:
from functools import partial # 導入偏函數 def add(x, y): return x + y func = partial(add, 1) # 設置辨寒暑綁定的第一個參數的值 print(func(1)) # 2 print(func(5)) # 6
現在我們仔細回想,當緩衝區的消息接收完畢後為空的狀態是會變成 b""的形式。那麼這個時候我們可以使用iter()方法設置為不斷的取出緩存中的值直到出現b"",而偏函數可以對recv()函數進行設置讓它始終取一個值,最後通過join來拼接出取出的所有值即可。
可以使用 "".join(iter(partial(tcp_clien.recv,back_log)),b"")
我們嘗試用函數來查看一下效果:
from functools import partial # 導入偏函數 li = [b"","1","2","3","4","5"] # 模擬內核緩衝區 def test(buffer_size): if buffer_size: # 模擬recv的數據大小 return li.pop() print("buffer_size必須為一個int類型的值") res = "".join(iter(partial(test,1024),b"")) print(res) # 54321 # join()方法會不斷的調用iter()下的__next__,每調用一次就執行一次偏函數。知道出現b""停止
最後我們發現,這樣的做法是會產生recv()阻塞的,總體來說還是不能夠成功。因為join()方法會不斷的執行,即使內核緩衝區的數據被recv()讀完了也不會終止迭代而是繼續阻塞下次的recv(),故這種方式宣告失敗。(還是iter()的第二個參數導致的,或許讀取完后內核緩衝區中的數據並不是b"")
測試的Server端代碼如下:
from socket import * import subprocess import struct ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_server=socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn,addr=tcp_server.accept() print('新的Client鏈接',addr) while True: #收 try: cmd=conn.recv(buffer_size) if not cmd:break print('收到Client的命令',cmd) #執行命令,得到命令的運行結果cmd_res res=subprocess.Popen(cmd.decode('utf-8'),shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) err=res.stderr.read() if err: cmd_res=err else: cmd_res=res.stdout.read() #發 if not cmd_res: cmd_res='執行成功'.encode('gbk') length=len(cmd_res) data_length=struct.pack('i',length) conn.send(data_length) conn.send(cmd_res) except Exception as e: print(e) break
測試的Client代碼如下:
from socket import * import struct from functools import partial #偏函數 ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_client=socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd=input('>>: ').strip() if not cmd:continue if cmd == 'quit':break tcp_client.send(cmd.encode('utf-8')) #解決粘包 length_data=tcp_client.recv(4) length=struct.unpack('i',length_data)[0] #第一種方法 recv_size=0 recv_msg=b'' while recv_size < length: #為何recv里是buffer_size,不是length,因為length如果為24G,系統內存沒有那麼大 #所以每次buffer_size,當recv_size < length時,循環接收,直到recv_size =length,退出循環 recv_msg += tcp_client.recv(buffer_size) recv_size=len(recv_msg) #1024 #第二種方法 失敗版本,會引發recv()的阻塞,而不會終止迭代。因為join()方法會不斷的調用其iter()方法產生的迭代器,也就是調用其__next__方法,所以第二次沒消息的recv()會阻塞住。 #recv_msg=''.join(iter(partial(tcp_client.recv, buffer_size), b'')) print('命令的執行結果是 ',recv_msg.decode('gbk')) tcp_client.close()
UDP協議是面向消息的協議,每一次的
sendto()與recvfrom()必須一一對應,否則就會收不到消息。
UDP是面向消息的協議,每個UDP段都是一條消息,每sendto()一次就是發送一次消息,而不管接收方有沒有收到消息發送方只管自己的發送任務,這也是UDP被稱為不可靠傳輸協議的由來。接收端的套接字緩衝區採用了鏈式的結構來記錄每一個到達的UDP包,在每一個UDP包中都有了消息頭,包括端口,消息源等等..於是UDP就能夠去區分出一個明確的消息定義,即面向消息的通信是有消息邊界的,所以UDP的傳輸叫做數據報的形式。
並且每一次recvform()的buffer_size最大值如果不夠獲取完全部的內核緩衝區里的數據的話,那麼只會收夠指定的最大字節數量(即buffer_size的設定值),剩餘的就不要了。所以UDP不會存在粘包,多麼乾脆利落…
我們還是用一個快遞員的那個圖來進行演示:
還有一點需要注意一下。使用UDP協議進行通信的時候不管首先啟動哪一方都不會報錯,因為它只管發,不管有沒有人接收。
所以,這也是我稱UDP協議比較隨便的原因。
那麼隨便有沒有什麼好處呢?有的,速度快。不用建立雙向鏈接通道,但是其代價就是數據可靠性與安全性的問題,效率和安全從來都是相對的,這個也只能在從中做取捨。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
為了加速電動車(electric vehicles)普及率,南韓政府宣佈將主導建設名為「EV-Line」的行動充電站,在首都首爾地區擴增10萬個充電設施;此計畫預計在2018年完工,由當地業者Power Cube和KT南韓電信承包建設業務。充電站將包括停車場、公園、住宅區等各式建築物內。 Power Cube董事表示,首爾當地有8成民眾都住在公寓,而非獨棟房屋,因此家用車都停在公用停車廠。每一個「EV-Line」充電站每小時約可充3.3 KW電量,要充飽整台車約需耗費6至8小時。有些充電站則可提供每小時8KW電量,雖可以縮短一半以上的充電時間,但充電站內的安全考慮成為最大考驗。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
1.事故背景
在APP訪問服務器接口時需要從redis中獲取token進行校驗,服務器上線后發現一開始可以正常訪問,但只要短時間內請求量增長服務則無法響應
2.排查流程
(1)使用top指令查看CPU資源佔用還遠遠達不到瓶頸,排查因為CPU資源不足導致服務不可用的可能
(2)查看tomcat線程池配置,默認最大線程數為200,理論上可以支持目前服務器的訪問量
(3)使用jmap指令保存堆棧信息,jmap -dump:format=b,file=dump.log pid,pid為進程號
(4)使用Java visualVM打開保存的堆棧日誌dump.log,發現絕大部分的線程都阻塞在從redis連接池中獲取連接的代碼,如下圖所示
3.原理分析
根據堆棧日誌所显示得知線程訪問redis前需要從連接池隊列中推出一個連接,當連接池沒有連接時,則會阻塞等待,阻塞等待的時間可以自行設置MAA_WAIT參數,默認是-1表示不限時等待,目前項目使用默認配置,所以所有的線程都會一直阻塞在獲取連接的步驟,如果設置了最大等待時間,當超過最大等待時間會報出Could not get a resource from the pool的異常
(1)在spring配置文件中的stringRedisTemplate對象配置參數中打開了事務支持,而redis的事務支持是用MUTI和EXEC指令來支持,以下事務實例截圖來自菜鳥教程 https://www.runoob.com/redis/redis-transactions.html
(2)如果要保證在事務能正常執行,那麼在一個方法中多次操作redis必須是同一條連接,這樣才能保證事務能正常執行,所以在stringRedisTemplate會將連接綁定在當前線程,當第二次訪問redis時直接從當前線程中獲取連接,綁定連接源碼如下:
(3)按照流程,先綁定連接,最後在finally代碼塊中釋放連接,看起來並沒有問題,但跳進去releaseConnection方法的代碼發現連接需要在事務提交后才能釋放,也就是說service方法上必須使用@Transation註解修飾,但因為業務方法上少寫了@Transation註解導致連接將一直綁定第一次獲取他的線程上,當線程池的線程被獲取完之後,其他線程就會就如阻塞等待狀態,導致服務不可用
(4)如果加上@Transation註解,那麼方法執行完之後將會執行TransactionSynchronizationUtils.invokeAfterCompletion這個方法,mysql事務也是在這個方法執行commit操作,如下圖所示方法的第一個參數是List<TransactionSynchronization> synchronizations,代表可以有多個事務,redis,mysql等,都會此進行事務提交操作,這裏使用多態,根據對象的具體類型執行不同的方法,redis則執行redis的事務提交操作,mysql則執行mysql的事務提交操作
(5)以下為redis事務提交的代碼,也跟我們上面提到的一樣,發送exec指令提交事務
4.如何修改代碼
(1)確認實際需求是否需要事務支持,如果需要則在對應方法上加上@Transaction註解
(2)如果不需要事務支持則將enableTransactionSupport設置為false
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
30日,北京市發佈今年首個霧霾橙色預警,全市PM2.5濃度12個小時內增長近10倍。 環保部稱,北京此次重污染過程主要以本地排放貢獻為主,其中機動車排放貢獻占比較大。在關於北京市大氣污染源排放清單數據中顯示,在氮氧化物和揮發性有機物VOCs中,機動車排放所占的比重分 別高達42%和32%。 面對嚴峻的氣候問題,大力發展新能源汽車成了必然舉措。 2014年中國出臺10餘項扶持政策,地方政府也紛紛制定了新能源汽車推廣規劃,2014年成新能源政策密集年,2015年國家及地方政策頻出,推動新能源汽車突飛猛進發展。伴隨政策的支持,新能源汽車相關產品不斷豐富,如前10月已經累計銷售新能源汽車17.11萬輛。未來新能源汽車有望進入家家戶戶,減少尾氣排放量。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
共享機車平台 GoShare 今日宣布將準備進軍台南,屆時將會以宏佳騰 Ai-1 為主力服務車款,最快今年第二季就要上路。此外,GoShare 也追加定點借還服務,4 月 1 日起在北海岸啟用。
GoShare 今日記者會上首次公開南台灣計畫,除了確定會在古都台南首推營運之外,這次也將會是電動車聯盟聯手出擊,台南服務網的主力將會是宏佳騰旗下 Ai-1 電動車,讓更多人可以體驗不同廠牌電動車的駕駛感受。

GoShare 目前上線車輛已經達到 4,000 輛,即使台南人口不若台北市這麼多,但預計至少會有 300-500 輛的佈署,對於市場能見度相對較低的宏佳騰來說,是一個展示自家車款的好機會。
另一項服務則是 GoShare Dots,也就是定點借還服務,有別於現行的路邊隨租隨還,GoShare Dots 的服務範圍限定在北海岸,以淡水到貢寮為界,途中雖然不能還車,但仍有換電站提供服務。
新北市政府是這次計畫的主要推手,使用定點借還服務,將可以享有前 20 分鐘起跳價免費的優惠。同時為了趕上清明連假觀光人潮,這個服務將會於 4 月 1 日正式啟用。
定點借還看起來是一個相對不方便的服務,但是對於營運商跟地方政府來說,卻能夠減少維護成本,同時也避免車輛亂停的問題。如果使用過租車 App 的朋友應該很有感受,特別是在台北市,如果租用路邊借還服務,最頭痛的依然是停車問題。
對於 GoShare 用戶來說,如果有明確的移動目標,例如觀光景點,那麼定點租還確實更像是大眾運輸的延伸,同時又能省下找車位的麻煩。GoShare 這次試行新模式,不僅是踩點新區域,同時也是收集數據與使用者行為的好機會,未來也有機會看到 GoShare Dots 在更多地方出現吧。
GoShare 新服務上線優惠:
4 月 1 日到 4 月 30 日,GoShare 新註冊用戶只要綁定悠遊卡 LINE 個人化服務並登錄活動,持同一張悠遊卡搭乘台北捷運滿 11 次,除了享有首次騎乘前 30 分鐘免費優惠之外,可再獲得價值 $100 元的 GoShare 騎乘金。
· GoShare 聯手四大銀行,將於四月獻上刷卡回饋活動 ——「GoShare 卡友週」,讓用戶一周七天都能盡享好康:
(合作媒體:。首圖來源:GoShare 提供)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?

日本老牌造船公司 Asahi Tanker 宣布開始建造全球第一的零排放油輪,預計將於 2022 年第一季完工,全船採用 3,500 度鋰離子電池組,載運量可達 1,300 立方公尺。
減少車輛排放廢氣是當前全球共同努力目標,而日本造船公司 Asahi Tanker(旭タンカー)眼光更宏大,他們打算將油輪也電動化,進而大幅減少溫室氣體排放,比起只會抓小不敢抓大的環保政策,這樣的努力值得肯定。
船運是國際貿易最重要的方式,90% 的運輸都依靠船運,而大噸位的船隻運行時,相當於數千輛貨櫃車同時前進,產生的廢氣十分驚人。根據國際海事組織(IMO)研究報告指出,船運每年排放的二氧化碳高達 9.4 億噸,占全球溫室氣體排放量的 2.5%。如果能夠降低船隻的廢氣排放,就可以有效減碳。
IMO 定出 2050 年減碳 50% 的目標,而日本 Asahi 油輪公司率先發表電動油輪計畫,可望搶下全球第一的稱號。
這艘電動油輪暫時名為 e5 tanker,是該公司於 2019 年推出的概念設計,如今正式投入生產,估計將搭載容量達 3,500 kWh 的鋰離子電池組,航行速度可達 11 節。
船身重量約 499 噸,可載運容量為 1,300 立方公尺,計劃預計建造兩艘電動船,將於 2022 年 3 月、2023 年 3 月陸續完工。投入服勤後,將可大幅降低硫化物、氮化物和二氧化碳的排放。
此外電動船上也將搭載更多 IoT 技術與智慧化科技,協助船員上下貨和處理各種雜事,減輕船員負擔,來緩解海上缺工的問題。
Asahi 油輪公司並未公布航行距離,然而根據資料來看,儘管擁有相當於 50 輛特斯拉的電池組,然而要支撐這麼龐大的重量前行,航行距離恐怕還是有限,因此將來首先會使用在日本國內航線,在裝卸貨時搭配直流充電,按照當前充電技術,如果使用特斯拉最新超充規格 250 kW 功率充電,要充滿這部超巨大電動船,也需要 12 個小時以上。
但是目前油輪進港卸貨,幾乎也需要一天左右的時間,即使是補給或修護,也常常需要花費 10 個小時,如此看來這個充電時間不算太久,只要每個港口都有一座大功率的直流充電站就很夠用。
綠能航運的另一個領航科技是燃料電池,在德國漢堡附近的生態湖,當作觀光船使用,但由於技術不夠規模化使用,要用到船運上還無法實現。
電動機車、電動小客車已經出現在我們四周,不久的將來,更多運輸工具將陸續電動化,我們應該問的下一個問題是:鋰離子電池夠用嗎?
(合作媒體:。首圖來源:)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?

據加州新車經銷協會數據指出,2020 年第一季特斯拉 Model 3 成為新車銷售排行榜冠軍,這是第一次有電動車獲得銷量第一頭銜,甚至超越了市場上常勝軍 Honda Civic 和 Toyota Corolla。
在美國加州,Honda Civic 是長期以來的銷售冠軍,而 Toyota Corolla 系列更是全球銷售最好的車(Altis 也是 Corolla 系列),Model 3 能夠超越這兩款大眾車,實在讓人意外。
在第一季 Model 3 在加州總共有 18,856 輛新車領牌,這個成績也勝過同級的大型房車 Honda Accord 和 Toyota Camry。
在這一季,特斯拉比起去年同期成長了 9.3℅,同時全加州的汽車銷量下滑了 4.3℅,使得特斯拉在汽車品牌市占率上升到 4.6℅。
特斯拉 Model 3 從上市以來就是大型豪華車的殺手,在同時期的銷售量始終勝過 BMW 3 系列、賓士 C Class 和 Audi A4。
加州是特斯拉總部所在,也是數千名員工的住處,對於銷量提升自然有加成效果。此外,加州長期以來都是反空污的指標地區,尤其是近年來的野火和霧霾問題,使得加州地區對電動車的購買意願相對其他州來得高。
如果以全美 2019 銷量來看,Model 3 總共售出 18 萬輛,然而 Civic 和 Corolla 各自都賣出 30 萬輛,差距依然很大。武漢肺炎也大幅影響了民眾購車的意願,預估在今年的前 4 個月,加州地區銷量會比去年下滑 24℅,在總量減少的情況下,特斯拉非傳統經銷通路的銷售方式,可能有助於銷售。
隨著 Model Y 在第一季末開賣,它是否會像 Model 3 衝擊大型房車市場一樣,也改變休旅車甚至輕型商用車的生態,是今年最值得關注的話題。此外,Model 3 的銷售量,在這段期間超過了所有車廠「插電式混合動力車」的銷量,換句話說,在油價來到歷史新低的此刻,在燃油車與電動車之間,定位模糊的 Plugin Hybird 的市場嚴重壓縮。如果維持這個走勢,那麼各車廠勢必要加快純電動化的腳步,而不能只靠插電混合動力車來過渡。
(合作媒體:。首圖來源:Tesla)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?