在工業控制與物聯網項目中,單片機通過 WiFi 定時上傳傳感器數據,上位機需可靠解析數據幀。本文詳細講解如何在 C# 中使用 Socket.BeginReceive 實現異步接收,並通過幀頭(0xAA, 0x55)校驗提取有效數據,解決粘包、半包問題。
單片機每秒發送一幀:0xAA, 0x55, 數據1, 數據2,共 4 字節。上位機需判斷前兩字節為幀頭,才提取後兩字節作為有效數據。
private byte[] m_receiveBuffer = new byte[1024];
private Socket sock;
public void StartReceive() {
sock.BeginReceive(m_receiveBuffer, 0, m_receiveBuffer.Length, 0, ReceiveCallBack, null);
}
private void ReceiveCallBack(IAsyncResult ar) {
try {
int received = sock.EndReceive(ar);
if (received > 0) {
ProcessData(m_receiveBuffer, received);
}
sock.BeginReceive(m_receiveBuffer, 0, m_receiveBuffer.Length, 0, ReceiveCallBack, null);
} catch (Exception ex) {
// 處理異常,如連接斷開
}
}
由於 TCP 是流式協議,可能出現粘包(多幀合一)或半包(一幀分多次到達),必須維護一個全局緩衝區:
private List _buffer = new List();
private void ProcessData(byte[] data, int length) {
_buffer.AddRange(data.Take(length));
while (_buffer.Count >= 4) {
if (_buffer[0] == 0xAA && _buffer[1] == 0x55) {
// 找到完整幀
byte d1 = _buffer[2];
byte d2 = _buffer[3];
HandleValidFrame(d1, d2);
_buffer.RemoveRange(0, 4);
} else {
// 幀頭錯誤,丟棄第一字節,繼續尋找
_buffer.RemoveAt(0);
}
}
}
private void HandleValidFrame(byte data1, byte data2) {
// 合成數據,如 data1 << 8 | data2
int value = (data1 << 8) | data2;
// 更新 UI(需 Invoke)
this.Invoke((Action)(() => {
label6.Text = $"數據1: {data1:X2}, 數據2: {data2:X2}, 合成: {value}";
}));
}
EndReceive 後必須再次調用 BeginReceive 保持接收List 動態拼接、截取,處理粘包/半包Invoke 或 BeginInvoke作者提到「延時問題」,可考慮:
SocketAsyncEventArgs 替代 APM 模式,提高性能