粘包現象

来源:https://www.cnblogs.com/ipython-201806/archive/2018/11/25/10009011.html
-Advertisement-
Play Games

一、subprocess 註:如果是Windows,那麼res.stdout.read()讀出的是GBK編碼的信息,在接收端需要用GBK解碼且只能從管道里讀一次結果,PIPE稱為管道。 二、粘包現象 1. TCP會粘包,UDP永遠不會粘包 發送端可以是一K一K地發送數據,而接收端的應用程式可以兩K兩 ...


一、subprocess

import subprocess
cmd = input("請輸入指令>>>")
res = subprocess.Popen(
    cmd,                        # 字元串指令:"dir", "ipconfig"等
    shell=True,                 # 使用shell,就相當於使用cmd視窗
    stderr=subprocess.PIPE,     # 標準錯誤輸出,凡是輸入錯誤指令,錯誤指令輸出的報錯信息就會被它拿到
    stdout=subprocess.PIPE,     # 標準輸出,正確指令的輸出結果被它拿到
)
print(res.stdout.read())
print(res.stderr.read())

註:如果是Windows,那麼res.stdout.read()讀出的是GBK編碼的信息,在接收端需要用GBK解碼且只能從管道里讀一次結果,PIPE稱為管道。

二、粘包現象

1. TCP會粘包,UDP永遠不會粘包

發送端可以是一K一K地發送數據,而接收端的應用程式可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個位元組的數據,也就是說,應用程式所看到的數據是一個整體,或說是一個流(stream),一條消息有多少位元組對應用程式是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因。而UDP是面向消息的協議,每個UDP段都是一條消息,應用程式必須以消息為單位提取數據,不能一次提取任意位元組的數據,這一點和TCP是很不同的。怎樣定義消息呢?可以認為對方一次性write/send的數據為一個消息,需要明白的是當對方send一條信息的時候,無論底層怎樣分段分片,TCP協議層會把構成整條消息的數據段排序完成後才呈現在內核緩衝區。

例如基於tcp的套接字客戶端往服務端上傳文件,發送時文件內容是按照一段一段的位元組流發送的,在接收方看了,根本不知道該文件的位元組流從何處開始,在何處結束

所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少位元組的數據所造成的。

此外,發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據後才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化演算法把這些數據合成一個TCP段後一次發送出去,這樣接收方就收到了粘包數據。

    1.TCP(transport control protocol,傳輸控制協議)是面向連接的,面向流的,提供高可靠性服務。收發兩端(客戶端和伺服器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle演算法),將多次間隔較小且數據量小的數據,合併成一個大的數據塊,然後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通信是無消息保護邊界的。
    2.UDP(user datagram protocol,用戶數據報協議)是無連接的,面向消息的,提供高效率服務。不會使用塊的合併優化演算法,, 由於UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩衝區)採用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(消息來源地址,埠等信息),這樣,對於接收端來說,就容易進行區分處理了。 即面向消息的通信是有消息保護邊界的。
    3.tcp是基於數據流的,於是收發的消息不能為空,這就需要在客戶端和服務端都添加空消息的處理機制,防止程式卡住,而udp是基於數據報的,即便是你輸入的是空內容(直接回車),那也不是空消息,udp協議會幫你封裝上消息頭,實驗略
udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個位元組的數據就算完成,若是y>x數據就丟失,這意味著udp根本不會粘包,但是會丟數據,不可靠

tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩衝區內容。數據是可靠的,但是會粘包
解釋原因

2. UDP是面向包的,不存在粘包現象

import socket
import subprocess

server = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ("127.0.0.1", 8001)

server.bind(ip_port)

while 1:
    cmd, addr = server.recvfrom(1024)
    cmd = cmd.decode("utf-8")
    if cmd == "q":
        break
    res = subprocess.Popen(
        cmd,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    server.sendto(res.stdout.read(), addr)
server端

 

import socket

client = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ("127.0.0.1", 8001)

cmd = input(">>>").strip().encode("utf-8")

client.sendto(cmd, ip_port)

msg, addr = client.recvfrom(1024)

print(msg.decode("gbk"))
client端

結果:

     報錯原因為客戶端設置接收的數大小小於消息包的大小,所以報錯。

3. TCP粘包現象

1) 第一種:接收方沒有及時接收緩衝區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再接收的時候還是從緩衝區拿上次遺留的數據,產生粘包)

服務端:

import socket
import subprocess

server = socket.socket()

ip_port = ("127.0.0.1", 8001)

server.bind(ip_port)

server.listen()

conn, addr = server.accept()
while 1:
    cmd = conn.recv(1024).decode("utf-8")

    res = subprocess.Popen(
        cmd,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    conn.send(res.stdout.read())
server端

客戶端:

import socket

client = socket.socket()

ip_port = ("127.0.0.1", 8001)

client.connect(ip_port)

while 1:
    cmd = input(">>>").strip().encode("utf-8")

    client.send(cmd)

    msg = client.recv(1024)

    print(msg.decode("gbk"))
client端

 結果:

>>>ipconfig

Windows IP 配置


無線區域網適配器 本地連接* 2:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開連接
   連接特定的 DNS 尾碼 . . . . . . . : 

乙太網適配器 乙太網:

   連接特定的 DNS 尾碼 . . . . . . . : 
   本地鏈接 IPv6 地址. . . . . . . . : fe80::642e:112d:ce7:7ce4%14
   IPv4 地址 . . . . . . . . . . . . : 192.168.12.55
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   預設網關. . . . . . . . . . . . . : 192.168.12.254

乙太網適配器 VMware Network Adapter VMnet1:

   連接特定的 DNS 尾碼 . . . . . . . : 
   本地鏈接 IPv6 地址. . . . . . . . : fe80::89c5:67be:1c8f:1493%18
   IPv4 地址 . . . . . . . . . . . . : 192.168.75.1
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   預設網關. . . . . . . . . . . . . : 

乙太網適配器 VMware Network Adapter VMnet8:

   連接特定的 DNS 尾碼 . . . . . . . : 
   本地鏈接 IPv6 地址. . . . . . . . : fe80::7d7e:c23:b290:1420%12
   IPv4 地址 . . . . . . . . . . . . : 192.168.174.1
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
 
>>>dir
  預設網關. . . . . . . . . . . . . : 

無線區域網適配器 WLAN:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開連接
   連接特定的 DNS 尾碼 . . . . . . . : 

隧道適配器 本地連接* 11:

   連接特定的 DNS 尾碼 . . . . . . . : 
   IPv6 地址 . . . . . . . . . . . . : 2001:0:9d38:953c:8be:267a:3f57:f3c8
   本地鏈接 IPv6 地址. . . . . . . . : fe80::8be:267a:3f57:f3c8%5
   預設網關. . . . . . . . . . . . . : ::
結果

    由結果可知,在輸入dir命令時,列印的還是上次ipconfig的數據,由此產生粘包

2) 第二種:發送端需要等緩衝區滿才發送出去,造成粘包(發送數據時間間隔很短,數據也很小,會合到一起發送,產生粘包)

 服務端:

import socket

server = socket.socket()

ip_port = ("127.0.0.1", 8001)

server.bind(ip_port)

server.listen()

conn, addr = server.accept()

from_client_msg1 = conn.recv(1024).decode("utf-8")
from_client_msg2 = conn.recv(1024).decode("utf-8")

print("from_client_msg1>>>", from_client_msg1)
print("from_client_msg2>>>", from_client_msg2)

conn.close()
server.close()
server端

 客戶端:

import socket

client = socket.socket()

server_ip_port = ("127.0.0.1", 8001)

client.connect(server_ip_port)


client.send(b"11")
client.send(b"22")

client.close()
client端

結果:

     如果兩次發送有一定的時間間隔,那麼就不會出現這種粘包情況。

三、粘包的解決方案

    產生粘包現象的根源在於接收端不知道發送端要傳送的位元組流長度。

1. 方案一:

    發送端發送數據之前,先將數據長度讓接收端知曉,接收端根據總長度利用迴圈完成消息的接收。

 

服務端:

import socket
import subprocess

server = socket.socket()

ip_port = ("127.0.0.1", 8001)

server.bind(ip_port)

server.listen()

conn, addr = server.accept()
while 1:
    from_client_cmd = conn.recv(1024).decode("utf-8")

    sub_obj = subprocess.Popen(
        from_client_cmd,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    cmd_res = sub_obj.stdout.read()

    conn.send(str(len(cmd_res)).encode("utf-8"))  # 將數據長度發送給接收方

    client_stutas = conn.recv(1024).decode("utf-8")  # 接收接收方確認結果
    if client_stutas == "ok":   # 如果確認結果為"ok",則發送數據
        conn.send(cmd_res)
    else:
        print("客戶端長度信息沒有收到")
server端

客戶端:

import socket

client = socket.socket()

server_ip_port = ("127.0.0.1", 8001)

client.connect(server_ip_port)

while 1:
    client_cmd = input("請輸入系統指令>>>").strip().encode("utf-8")
    client.send(client_cmd)  # 發送指令

    from_server_datalen = client.recv(1024).decode("utf-8")  # 接收數據長度信息
    client.send(b"ok")  # 發送確認信息

    from_server_result = client.recv(int(from_server_datalen))
    print(from_server_result.decode("gbk"))
client端

結果:

請輸入系統指令>>>ipconfig -all

Windows IP 配置

   主機名  . . . . . . . . . . . . . : DESKTOP-1E4JOLI
   主 DNS 尾碼 . . . . . . . . . . . : 
   節點類型  . . . . . . . . . . . . : 混合
   IP 路由已啟用 . . . . . . . . . . : 否
   WINS 代理已啟用 . . . . . . . . . : 否

無線區域網適配器 本地連接* 2:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開連接
   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
   物理地址. . . . . . . . . . . . . : 16-6D-57-0C-4D-7E
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是

乙太網適配器 乙太網:

   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller
   物理地址. . . . . . . . . . . . . : F0-DE-F1-DF-A4-FB
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是
   本地鏈接 IPv6 地址. . . . . . . . : fe80::642e:112d:ce7:7ce4%14(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.12.55(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   獲得租約的時間  . . . . . . . . . : 2018年11月22日 18:43:32
   租約過期的時間  . . . . . . . . . : 2018年11月24日 14:41:01
   預設網關. . . . . . . . . . . . . : 192.168.12.254
   DHCP 伺服器 . . . . . . . . . . . : 192.168.12.254
   DHCPv6 IAID . . . . . . . . . . . : 217112305
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-23-50-F6-63-F0-DE-F1-DF-A4-FB
   DNS 伺服器  . . . . . . . . . . . : 202.96.134.33
                                       202.96.128.86
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

乙太網適配器 VMware Network Adapter VMnet1:

   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
   物理地址. . . . . . . . . . . . . : 00-50-56-C0-00-01
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是
   本地鏈接 IPv6 地址. . . . . . . . : fe80::89c5:67be:1c8f:1493%18(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.75.1(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   獲得租約的時間  . . . . . . . . . : 2018年11月23日 14:40:54
   租約過期的時間  . . . . . . . . . : 2018年11月23日 18:10:54
   預設網關. . . . . . . . . . . . . : 
   DHCP 伺服器 . . . . . . . . . . . : 192.168.75.254
   DHCPv6 IAID . . . . . . . . . . . : 587223126
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-23-50-F6-63-F0-DE-F1-DF-A4-FB
   DNS 伺服器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                       fec0:0:0:ffff::2%1
                                       fec0:0:0:ffff::3%1
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

乙太網適配器 VMware Network Adapter VMnet8:

   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
   物理地址. . . . . . . . . . . . . : 00-50-56-C0-00-08
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是
   本地鏈接 IPv6 地址. . . . . . . . : fe80::7d7e:c23:b290:1420%12(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.174.1(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   獲得租約的時間  . . . . . . . . . : 2018年11月23日 14:40:53
   租約過期的時間  . . . . . . . . . : 2018年11月23日 18:10:54
   預設網關. . . . . . . . . . . . . : 
   DHCP 伺服器 . . . . . . . . . . . : 192.168.174.254
   DHCPv6 IAID . . . . . . . . . . . : 604000342
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-23-50-F6-63-F0-DE-F1-DF-A4-FB
   DNS 伺服器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                       fec0:0:0:ffff::2%1
                                       fec0:0:0:ffff::3%1
   主 WINS 伺服器  . . . . . . . . . : 192.168.174.2
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

無線區域網適配器 WLAN:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開連接
   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Qualcomm Atheros AR9285 Wireless Network Adapter
   物理地址. . . . . . . . . . . . . : 44-6D-57-0C-4D-7E
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是

隧道適配器 本地連接* 11:

   連接特定的 DNS 尾碼 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft Teredo Tunneling Adapter
   物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是
   IPv6 地址 . . . . . . . . . . . . : 2001:0:9d38:953c:8be:267a:3f57:f3c8(首選) 
   本地鏈接 IPv6 地址. . . . . . . . : fe80::8be:267a:3f57:f3c8%5(首選) 
   預設網關. . . . . . . . . . . . . : ::
   DHCPv6 IAID . . . . . . . . . . . : 83886080
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-23-50-F6-63-F0-DE-F1-DF-A4-FB
   TCPIP 上的 NetBIOS  . . . . . . . : 已禁用

請輸入系統指令>>>dir
 驅動器 E 中的捲沒有標簽。
 捲的序列號是 A473-0ACA

 E:\python_個人\day 028 粘包現象\第二種 的目錄

2018/11/23  17:51    <DIR>          .
2018/11/23  17:51    <DIR>          ..
2018/11/23  17:51               500 客戶端.py
2018/11/23  17:47               810 服務端.py
               2 個文件          1,310 位元組
               2 個目錄 123,297,423,360 可用位元組

請輸入系統指令>>>
結果

    此時,數據之間就不存在粘包現象了。

2. 方案二:

    通過struck模塊將需要發送的內容的長度進行打包成一個4位元組長度的數據發送給接收端,接收端只要取出前4個位元組,然後對這個位元組的數據進行解包,拿到你要發送的內容的長度,然後通過這個長度來繼續接收我們實際要發送的內容。

1. struct模塊

    對python基本類型值與用python字元串格式表示的c struct類型間的轉化。

Format C Type Python type Standard size Notes
x  pad type no value     
 char string of length 1  1  
 char integer  1  (3)
 unsigned char integer   1  (3)
?  _Bool  bool  1  (1)
h  short  integer  2  (3)
 unsigned short  integer  2  (3)
 int  integer  4  (3)
 unsigned int  integer  4  (3)
l  long  integer  4  (3)
L  unsigned long  integer  4  (3)
q  long long  integer  8  (2),(3)
Q  unsigned long long  integer  8  (2),(3)
f  float  float  4  (4)
d  double  float  8  (4)
s  char[]  string    
P  char[]  string    
p  void *  integer    (5),(3)
  • pack(format, value):打包,對於int類型,只能打包[-2147483648,2147483647]範圍內數據,否則報錯struct.error錯誤。對於int類型,打包後得到是4位元組的bytes類型
  • unpack(format, string):解包,將bytes類型轉化為format對應的類型,返回的結果是元組
import struct

a = 10
print(struct.pack("i", a))  # b'\n\x00\x00\x00'
b = b'\n\x00\x00\x00'
print(struct.unpack("i", b))  # (10,)

服務端:

import socket
import subprocess
import struct


server = socket.socket()

ip_port = ("127.0.0.1", 8001)
server.bind(ip_port)  # 綁定埠

server.listen()  # 監聽

conn, addr = server.accept()  # 等待連接

while 1:
    from_client_cmd = conn.recv(1024).decode("utf-8")  # 接收消息

    sub_obj = subprocess.Popen(
        from_client_cmd,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    cmd_res = sub_obj.stdout.read()  # 得到的是bytes類型
    data_len = len(cmd_res)  # 數據長度
    print(data_len)  # 列印長度
    # 將真實數據長度打包成4個位元組的數據
    struct_data_len = struct.pack("i", data_len)
    # 將長度信息和數據內容發送給客戶端
    conn.send(struct_data_len + cmd_res)
server端

 客戶端:

import socket
import struct

client = socket.socket()
server_ip_port = ("127.0.0.1", 8001)

client.connect(server_ip_port)  # 連接

while 1:
    client_cmd = input("請輸入系統指令>>>").strip()
    client.send(client_cmd.encode("utf-8"))  # 發送消息

    # 先接收4個位元組,得到數據內容長度
    recv_data_len = client.recv(4) 
    # 將4個位元組長度的數據,解包成後面真實數據的長度
    real_data_len = struct.unpack("i", recv_data_len)[0]
    print(real_data_len)  # 列印長度

    server_result = client.recv(real_data_len)  # 接收數據內容

    print(server_result.decode("gbk"))
client端

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.while 迴圈 : 2.for 迴圈: ...
  • 前言 爬蟲要爬取的信息主要來自於網頁載入的內容,有必要瞭解一些網頁的知識。 當我們在瀏覽器網址欄輸入一個網址——URL,經過TCP/IP協議簇的處理,這個網址請求的信息就被髮送到URL對應的伺服器,接著伺服器處理這個請求,並將請求的內容返回給瀏覽器,瀏覽器便顯示或者下載URL請求相應的資源。這是前一 ...
  • bat處理文件 (一)定義 bat處理文件就是可以一次性執行多個命令的文件。 (二)編寫步驟 只需要打開一個文本文件,將所要執行的命令寫入其中,然後將文件的尾碼改為.bat即可 (三)bat處理文件的常用命令 pause 讓當前控制台停留 echo 向控制台輸出指定內容 echo off 隱藏ech ...
  • Java開發學習心得(一):SSM環境搭建 有一點.NET的開發基礎,在學校學過基礎語法,對JAVA有點興趣,就簡單學習了一下,記錄一下從哪些方面入手的,暫時不打算深入到原理方面,先簡單搭下環境看看,所以有些地方可能講得不慎準確。 1 SSM框架 從網上的討論來看,SSM框架似乎正在慢慢被Sprin ...
  • 1. 帶著問題去閱讀 為什麼說ConcurrentHashMap是線程安全的?或者說 ConcurrentHashMap是如何防止併發的? 2. 欄位和常量 首先,來看一下ConcurrentHashMap中的一些欄位和常量,這些在接下來的操作中會用得到 2.1. 常量 從中,我們可以獲得以下信息: ...
  • a) 一個整型數(An integer) b) 一個指向整型數的指針(A pointer to an integer) c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer) d) 一個有10個整型數的數組(An arra ...
  • 整理了下阿裡近幾年的java面試題目,大家參考下吧,希望對大家有幫助,可以幫大家查漏補缺。 答對以下這些面試題,可以淘汰掉 80 % 的求職競爭者。 1.hashcode相等兩個類一定相等嗎?equals呢?相反呢? 2.介紹一下集合框架? 3.hashmap hastable 底層實現什麼區別?h ...
  • 1.安裝第三方庫(matplotlib,jieba,wordcloud,numpy) 1.1安裝方法:pip命令線上安裝(python3.x預設安裝了pip,pip下載地址:https://pypi.python.org/pypi/pip#downloads) 已經配置好環境變數前提下,在cmd視窗 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...