文章目錄
- 一、背景
- 二、總結內容
- 2.1 UDP通信服務端創建方式
- **DUP采用的是無連接的套接字**
- 2.2 16進制數據解析
- 2.3 文件創建與數據儲存分析
一、背景
最近在處理公司的一設備,內置的DTU通過UDP向服務器發送16進制的數據報文,由于第一次接觸此類數據解析方式,在這里做總結與反省,避免大家走彎路
二、總結內容
2.1 UDP通信服務端創建方式
步驟
- 創建UDP的socket通信方式。
- 綁定具體的端口。
- 設置端口復用等待(這一步可以省略)
- 獲取數據。
- 向客戶端發送數據。
- 解析儲存數據。
- 關閉UDP的socket鏈接。
下面分別講解并總結,通過MVP的方式完成以上所有步驟,不做過多延展:
1. 創建UDP的socket通信方式。
import
socket
Server
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_DGRAM
)
在任何類型的通信開始之前,網絡應用程序都必須創建套接字。
套接字最初是為同一主機上的應用程序所創建,使得主機上運行的一個程序(又名一個進程)與另一個運行的程序進行通信。這就是所謂的進程間通信(Inter Process Communication,IPC)
嵌套字有兩種類型:
- 基于文件的:AF_UNIX
- 面向網絡的:AF_INET(面向IPv4)、AF_INET6(面向IPv6)
DUP采用的是無連接的套接字
- 特點:不可靠(局網內還是比較可靠的),開銷小。為了創建UDP套接字,必須使用SOCK_DGRAM作為套接字類型。UDP套接字的SOCK_DGRAM名字來自于單詞“datagram”(數據報)。
2. 綁定具體的端口
Server
.
bind
(
""
,
8600
)
bind 表示將創建好的Server綁定到具體的端口,注意:
當作為UDP的服務端時,前面的IP地址是可以省略的,后面是端口號有效的端口號范圍為0~65535(小于1024的端口號預留給了系統)
**3.設置端口復用等待(這一步可以省略) **
Server
.
setsockopt
(
socket
.
IPPROTO_IP
,
socket
.
SO_REUSEADDR
,
1
)
如果端口被使用過,并且利用Socket.close()關閉了端口鏈接,但是端口還沒有釋放,可以用上述函數來進行等待端口的重調用。
4. 獲取數據
Msg
,
ClientAddr
=
Server
.
recvfrom
(
1024
)
在UDP中使用recvform返回的是客戶端發送過來的**字節流(10進制、ASCII碼、16進制等)**與客戶端的IP地址,1024表示緩存數據的大小。
5. 向客戶端發送數據
Server
.
sendto
(
Data
)
一般UDP服務器在接收到數據后會向客戶端發送心跳包確認,可以通過此函數發送數據。
6. 解析數據
Data
=
binascii
.
b2a_hex
(
Msg
)
此函數是將傳送過來的字節流報文數據解析成16進制數據。相關用法見字節流轉換成ASCII碼
7. 關閉UDP的socket鏈接
Server
.
close
(
)
沒什么好講的,一定要有這個就行,不然下一次沒法繼續用這個端口。
2.2 16進制數據解析
1. 報文時間解析
Time
=
datetime
.
datetime
.
strptime
(
Data
[
26
:
38
]
,
"%y%m%d%H%M%S"
)
# 解析時間
由于時間是十進制數據,這里不用做16進制轉換,直接通過datetime的strptime方法進行數據轉換。
2. 16進制轉換成10進制,再轉換成ASCII碼
chr
(
int
(
Data
[
224
+
(
i
-
2
)
:
224
+
i
]
,
16
)
)
int(num, 16) 將16進制轉換成10進制
chr(十進制)解析出對應的ASCII碼
2.3 文件創建與數據儲存分析
不得不說在儲存數據文件的時候遇到了很多的坑,現在總結:
坑1:如何自動創建 年->月->日的文件夾結構?
在自己創建文件夾的時候有想到過使用
makedirs()
函數鏈式創建文件夾,但是在判斷文件夾是否存在的時候一直報錯,一直以為是在makedirs()使用不當造成的,有想過聯合使用chdir
函數與mkdir()
函數來進行逐級的文件夾創建,最后效果也不是特別好。后來仔細查了好久的資料發現:
if
not
os
.
path
.
exists
(
NewPath
)
:
os
.
makedirs
(
NewPath
)
在默認的IDLE中判斷文件夾是否存在時,一直是有問題會報錯的,折騰了好久,后來改為:
if
not
os
.
path
.
isdir
(
FileDir
)
:
os
.
makedirs
(
FileDir
)
才沒有報錯,這里做筆記好好提醒一下自己。
以上是我總結的所有錯誤,希望看到這篇帖子的你能夠避開這些坑。下面附上我完整的UDP報文接收以及解析的代碼:
#!/usr/local/bin/python3
# coding:utf-8
import
socket
import
binascii
import
datetime
import
os
import
csv
'''
作者:Zflyee
Mailto: zflyee@126.com
'''
Server
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_DGRAM
)
# 創建UDP傳輸數據
# Server.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1) # 快速實現端口復用
Server
.
bind
(
(
""
,
8600
)
)
print
(
u
"已綁定本機端口:8600,正在監聽數據..."
)
def
DataParsing
(
Data
)
:
Time
=
datetime
.
datetime
.
strptime
(
Data
[
26
:
38
]
,
"%y%m%d%H%M%S"
)
# 解析時間
Voltage
=
int
(
Data
[
44
:
46
]
,
16
)
/
10.0
# 電壓數據
Temp
=
int
(
Data
[
60
:
64
]
,
16
)
/
10.0
# 實時溫度數據
Temp_1h_max
=
int
(
Data
[
70
:
74
]
,
16
)
/
10.0
# 溫度小時最大值
Temp_1h_min
=
int
(
Data
[
80
:
84
]
,
16
)
/
10.0
# 溫度小時最小值
Hum
=
int
(
Data
[
90
:
94
]
,
16
)
/
10.0
# 濕度數據
Hum_1h_max
=
int
(
Data
[
100
:
104
]
,
16
)
/
10.0
# 濕度小時最大值
Hum_1h_min
=
int
(
Data
[
110
:
114
]
,
16
)
/
10.0
# 濕度小時最小值
Pa
=
int
(
Data
[
120
:
124
]
,
16
)
/
10.0
# 氣壓數據
Pa_1h_max
=
int
(
Data
[
130
:
134
]
,
16
)
/
10.0
# 氣壓小時最大值
Pa_1h_min
=
int
(
Data
[
150
:
154
]
,
16
)
/
10.0
# 氣壓小時最小值
WindSpd
=
int
(
Data
[
140
:
144
]
,
16
)
/
10.0
# 瞬時風速值
WindSpd_10min_avg
=
int
(
Data
[
160
:
164
]
,
16
)
/
10.0
# 10分鐘風速平均值
WindSpd_max
=
int
(
Data
[
170
:
174
]
,
16
)
/
10.0
# 當前風速最大值
WindSpd_min
=
int
(
Data
[
180
:
184
]
,
16
)
/
10.0
# 當前風速最小值
WindDir
=
int
(
Data
[
190
:
194
]
,
16
)
/
10.0
# 當前風向瞬時值
WindDir_10min_avg
=
int
(
Data
[
200
:
204
]
,
16
)
/
10.0
# 10分鐘風向平均值
Rain_1day
=
int
(
Data
[
210
:
214
]
,
16
)
/
10.0
# 當日降雨量
# 推算緯度數據長度
Lati_len
=
int
(
Data
[
220
:
224
]
,
16
)
# 轉換為10進制
Lati
=
""
for
i
in
range
(
2
,
Lati_len
*
2
,
2
)
:
Lati
+=
(
chr
(
int
(
Data
[
224
+
(
i
-
2
)
:
224
+
i
]
,
16
)
)
)
# 推算維度長度與數據
Long_len
=
int
(
Data
[
250
:
252
]
,
16
)
Long
=
""
for
i
in
range
(
2
,
Long_len
*
2
,
2
)
:
Long
+=
(
chr
(
int
(
Data
[
254
+
(
i
-
2
)
:
254
+
i
]
,
16
)
)
)
# 組裝數據
DataList
=
[
Time
,
WindSpd
,
WindSpd_10min_avg
,
WindSpd_max
,
WindSpd_min
,
WindDir
,
WindDir_10min_avg
,
Temp
,
Temp_1h_max
,
Temp_1h_min
,
Hum
,
Hum_1h_max
,
Hum_1h_min
,
Pa
,
Pa_1h_max
,
Pa_1h_min
,
Rain_1day
,
Voltage
]
# 創建文件路徑
Path
=
"E:\\富奧通\\data\\SY_FWS600\\"
Text
=
os
.
path
.
join
(
Path
,
Time
.
strftime
(
"%Y"
)
,
Time
.
strftime
(
"%m"
)
)
+
"\\"
+
Time
.
strftime
(
"%d"
)
+
".csv"
FileDir
=
os
.
path
.
split
(
Text
)
[
0
]
if
not
os
.
path
.
isdir
(
FileDir
)
:
os
.
makedirs
(
FileDir
)
# 寫入數據
with
open
(
Text
,
"a+"
,
newline
=
""
)
as
f
:
Writer
=
csv
.
writer
(
f
)
Writer
.
writerow
(
DataList
)
# 儲存提示
print
(
u
"{}時刻數據已成功儲存。"
.
format
(
Time
)
)
while
True
:
try
:
Msg
,
ClientAddr
=
Server
.
recvfrom
(
1024
)
Data
=
binascii
.
b2a_hex
(
Msg
)
# 將數據轉換成16進制數據
Data
=
str
(
Data
,
encoding
=
"utf-8"
)
print
(
u
"從{0}的{1}端口得到如下數據:\n{2}"
.
format
(
ClientAddr
[
0
]
,
ClientAddr
[
1
]
,
Data
)
)
if
Data
[
0
:
2
]
==
"7b"
:
Content
=
"7B810010"
+
Data
[
8
:
30
]
+
"7B"
Response
=
binascii
.
a2b_hex
(
Content
)
Server
.
sendto
(
Response
,
ClientAddr
)
# 回復客戶端數據
print
(
u
"已回復客戶端"
)
elif
Data
[
0
:
2
]
==
'01'
:
DataParsing
(
Data
)
else
:
print
(
u
"%%%%%%%%%%%%%%%數據報錯,重新獲取%%%%%%%%%%%%%%%"
)
continue
except
:
print
(
u
"%%%%%%%%%%%%%%%數據報錯,重新獲取%%%%%%%%%%%%%%%"
)
continue
Server
.
close
(
)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
