基于C#實現(xiàn)串口監(jiān)聽與TCP轉(zhuǎn)發(fā)功能
前言
在工業(yè)自動化、物聯(lián)網(wǎng)設(shè)備管理、遠(yuǎn)程監(jiān)控等應(yīng)用場景中,經(jīng)常需要將本地串口設(shè)備(如條碼掃描槍、RFID讀卡器、各類傳感器)的數(shù)據(jù)實時傳輸?shù)竭h(yuǎn)程服務(wù)器進(jìn)行處理。本文將詳細(xì)介紹如何使用 C# 創(chuàng)建一個串口監(jiān)聽服務(wù),并將接收到的數(shù)據(jù)通過 TCP 協(xié)議轉(zhuǎn)發(fā)至遠(yuǎn)程服務(wù)器。
本方案實現(xiàn)的功能非常實用且簡潔:當(dāng)串口設(shè)備有數(shù)據(jù)輸入時,程序自動捕獲并將其轉(zhuǎn)發(fā)至指定的 TCP 服務(wù)器。適用于遠(yuǎn)程數(shù)據(jù)采集、設(shè)備狀態(tài)監(jiān)控、嵌入式系統(tǒng)通信等多種場景。
一、實現(xiàn)原理
整個程序基于以下核心步驟構(gòu)建:
1、創(chuàng)建串口監(jiān)聽服務(wù):配置并打開串口,注冊數(shù)據(jù)接收事件;
2、監(jiān)聽串口數(shù)據(jù)變化:通過 DataReceived 事件捕獲新數(shù)據(jù);
3、建立 TCP 連接:將接收到的數(shù)據(jù)通過 TCP 客戶端發(fā)送到遠(yuǎn)程服務(wù)器;
4、實現(xiàn)重連機(jī)制:在網(wǎng)絡(luò)異常中斷后自動嘗試重新連接;
5、資源釋放與異常處理:確保程序退出或異常時能正確關(guān)閉串口和網(wǎng)絡(luò)連接,避免資源泄漏。
二、代碼實現(xiàn)
using System;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace AppComTransTcp
{
internal class Program
{
// TCP客戶端
private static TcpClient _tcpClient;
// 網(wǎng)絡(luò)數(shù)據(jù)流
private static NetworkStream _networkStream;
// 串口對象
private static SerialPort _serialPort;
// TCP服務(wù)器IP地址
private static string _serverIp = "127.0.0.1";
// TCP服務(wù)器端口
private static int _serverPort = 3001;
// 串口名稱
private static string _portName = "COM1";
// 波特率
private static int _baudRate = 9600;
// 數(shù)據(jù)位
private static int _dataBits = 8;
// 停止位
private static StopBits _stopBits = StopBits.One;
// 校驗位
private static Parity _parity = Parity.None;
// 重連間隔(毫秒)
private static int _reconnectInterval = 5000;
// 是否繼續(xù)運行
private static bool _isRunning = true;
static void Main(string[] args)
{
Console.WriteLine("串口TCP轉(zhuǎn)發(fā)服務(wù)啟動中...");
// 初始化并啟動服務(wù)
InitializeService();
Console.WriteLine("服務(wù)已啟動。按 'q' 退出程序。");
// 保持程序運行,直到用戶輸入q退出
while (_isRunning)
{
var key = Console.ReadKey(true);
if (key.KeyChar == 'q' || key.KeyChar == 'Q')
{
_isRunning = false;
}
}
// 關(guān)閉資源
CloseConnections();
Console.WriteLine("程序已退出。");
}
/// <summary>
/// 初始化服務(wù),包括串口和TCP連接
/// </summary>
private static void InitializeService()
{
try
{
// 初始化串口
InitializeSerialPort();
// 初始化TCP連接
ConnectToTcpServer();
// 啟動重連檢查任務(wù)
StartReconnectionTask();
}
catch (Exception ex)
{
Console.WriteLine($"初始化服務(wù)失敗: {ex.Message}");
}
}
/// <summary>
/// 初始化串口設(shè)置
/// </summary>
private static void InitializeSerialPort()
{
try
{
// 創(chuàng)建并配置串口對象
_serialPort = new SerialPort
{
PortName = _portName,
BaudRate = _baudRate,
DataBits = _dataBits,
StopBits = _stopBits,
Parity = _parity,
ReadBufferSize = 4096,
ReadTimeout = 500
};
// 注冊數(shù)據(jù)接收事件
_serialPort.DataReceived += SerialPort_DataReceived;
// 打開串口
_serialPort.Open();
Console.WriteLine($"串口 {_portName} 已成功打開,波特率: {_baudRate}");
}
catch (Exception ex)
{
Console.WriteLine($"初始化串口失敗: {ex.Message}");
throw;
}
}
/// <summary>
/// 建立TCP服務(wù)器連接
/// </summary>
private static void ConnectToTcpServer()
{
try
{
// 創(chuàng)建TCP客戶端并連接服務(wù)器
_tcpClient = new TcpClient();
_tcpClient.Connect(_serverIp, _serverPort);
_networkStream = _tcpClient.GetStream();
Console.WriteLine($"已成功連接到TCP服務(wù)器 {_serverIp}:{_serverPort}");
}
catch (Exception ex)
{
Console.WriteLine($"連接TCP服務(wù)器失敗: {ex.Message}");
// 這里不拋出異常,讓重連機(jī)制處理
}
}
/// <summary>
/// 啟動TCP重連檢查任務(wù)
/// </summary>
private static void StartReconnectionTask()
{
Task.Run(async () =>
{
while (_isRunning)
{
try
{
// 檢查TCP連接狀態(tài)
if (_tcpClient == null || !_tcpClient.Connected)
{
Console.WriteLine("TCP連接已斷開,嘗試重新連接...");
// 關(guān)閉舊連接
if (_tcpClient != null)
{
_tcpClient.Close();
_tcpClient.Dispose();
}
// 創(chuàng)建新連接
ConnectToTcpServer();
}
}
catch (Exception ex)
{
Console.WriteLine($"重連過程發(fā)生錯誤: {ex.Message}");
}
// 等待指定時間后再次檢查
await Task.Delay(_reconnectInterval);
}
});
}
/// <summary>
/// 串口數(shù)據(jù)接收事件處理
/// </summary>
private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// 確保有足夠的數(shù)據(jù)可讀
if (_serialPort.BytesToRead <= 0)
return;
// 讀取串口數(shù)據(jù)
byte[] buffer = new byte[_serialPort.BytesToRead];
_serialPort.Read(buffer, 0, buffer.Length);
// 轉(zhuǎn)換為字符串(可根據(jù)實際需求修改編碼方式)
string data = Encoding.UTF8.GetString(buffer);
Console.WriteLine($"接收到串口數(shù)據(jù): {data}");
// 將數(shù)據(jù)轉(zhuǎn)發(fā)到TCP服務(wù)器
SendDataToTcpServer(buffer);
}
catch (Exception ex)
{
Console.WriteLine($"處理串口數(shù)據(jù)時發(fā)生錯誤: {ex.Message}");
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù)到TCP服務(wù)器
/// </summary>
private static void SendDataToTcpServer(byte[] data)
{
try
{
// 檢查TCP連接是否可用
if (_tcpClient != null && _tcpClient.Connected && _networkStream != null)
{
// 發(fā)送數(shù)據(jù)
_networkStream.Write(data, 0, data.Length);
_networkStream.Flush();
Console.WriteLine($"已發(fā)送 {data.Length} 字節(jié)數(shù)據(jù)到TCP服務(wù)器");
}
else
{
Console.WriteLine("TCP連接不可用,數(shù)據(jù)發(fā)送失敗");
}
}
catch (Exception ex)
{
Console.WriteLine($"發(fā)送數(shù)據(jù)到TCP服務(wù)器失敗: {ex.Message}");
// 標(biāo)記連接為斷開,讓重連機(jī)制處理
if (_tcpClient != null)
{
_tcpClient.Close();
}
}
}
/// <summary>
/// 關(guān)閉所有連接并釋放資源
/// </summary>
private static void CloseConnections()
{
try
{
// 關(guān)閉串口
if (_serialPort != null && _serialPort.IsOpen)
{
_serialPort.DataReceived -= SerialPort_DataReceived;
_serialPort.Close();
_serialPort.Dispose();
Console.WriteLine("串口已關(guān)閉");
}
// 關(guān)閉TCP連接
if (_networkStream != null)
{
_networkStream.Close();
_networkStream.Dispose();
}
if (_tcpClient != null)
{
_tcpClient.Close();
_tcpClient.Dispose();
Console.WriteLine("TCP連接已關(guān)閉");
}
}
catch (Exception ex)
{
Console.WriteLine($"關(guān)閉連接時發(fā)生錯誤: {ex.Message}");
}
}
}
}
三、功能詳解
1、串口監(jiān)聽服務(wù)
使用 SerialPort 類實現(xiàn)串口監(jiān)聽,支持自定義配置如下:
- 串口名稱(如 COM1)
- 波特率(如 9600、115200)
- 數(shù)據(jù)位(通常為 8 位)
- 停止位(通常為 1 位)
- 校驗位(通常為無校驗)
示例配置:
_serialPort = new SerialPort
{
PortName = _portName,
BaudRate = _baudRate,
DataBits = _dataBits,
StopBits = _stopBits,
Parity = _parity,
ReadBufferSize = 4096,
ReadTimeout = 500
};
2、數(shù)據(jù)變化檢測與轉(zhuǎn)發(fā)
通過注冊 DataReceived 事件實現(xiàn)串口數(shù)據(jù)變化檢測:
_serialPort.DataReceived += SerialPort_DataReceived;
當(dāng)串口接收到數(shù)據(jù)后,事件處理程序自動觸發(fā):
1、讀取緩沖區(qū)數(shù)據(jù);
2、將其轉(zhuǎn)換為字符串;
3、調(diào)用 SendDataToTcpServer 方法將數(shù)據(jù)轉(zhuǎn)發(fā)到 TCP 服務(wù)器。
3、TCP 連接管理
使用 TcpClient 和 NetworkStream 建立與服務(wù)器的連接,并實現(xiàn)以下功能:
- 自動重連機(jī)制;
- 定期檢查連接狀態(tài);
- 異常處理與連接恢復(fù)。
4、錯誤處理與資源管理
- 全面捕獲各階段異常;
- 在程序退出時優(yōu)雅關(guān)閉串口與網(wǎng)絡(luò)連接;
- 防止內(nèi)存泄漏,確保資源正確釋放。
四、應(yīng)用場景
該解決方案適用于多種工業(yè)及物聯(lián)網(wǎng)場景,包括但不限于:
1、工業(yè)自動化:PLC、傳感器數(shù)據(jù)遠(yuǎn)程采集;
2、物聯(lián)網(wǎng)設(shè)備管理:邊緣設(shè)備數(shù)據(jù)集中上傳;
3、零售系統(tǒng):條碼掃描槍數(shù)據(jù)實時上傳;
4、醫(yī)療設(shè)備:儀器數(shù)據(jù)遠(yuǎn)程監(jiān)控;
5、倉儲物流:RFID 讀寫器數(shù)據(jù)同步。
五、配置說明
根據(jù)實際部署環(huán)境,需修改以下配置參數(shù):
private static string _serverIp = "192.168.1.100"; // 服務(wù)器IP private static int _serverPort = 8080; // 服務(wù)器端口 private static string _portName = "COM1"; // 串口名稱 private static int _baudRate = 9600; // 波特率
總結(jié)
本文詳細(xì)講解了如何使用 C# 實現(xiàn)串口監(jiān)聽與 TCP 轉(zhuǎn)發(fā)功能。該方案結(jié)構(gòu)清晰、功能完整,具備良好的穩(wěn)定性和可擴(kuò)展性。你可以直接使用本示例作為基礎(chǔ)模塊,也可以根據(jù)具體業(yè)務(wù)需求進(jìn)行定制開發(fā)。
對于需要遠(yuǎn)程采集串口設(shè)備數(shù)據(jù)的應(yīng)用場景,該程序提供了一個輕量但高效的解決方案。無論是工業(yè)現(xiàn)場設(shè)備監(jiān)控,還是物聯(lián)網(wǎng)終端數(shù)據(jù)上傳,都能從中受益。
以上就是基于C#實現(xiàn)串口監(jiān)聽與TCP轉(zhuǎn)發(fā)功能的詳細(xì)內(nèi)容,更多關(guān)于C#串口監(jiān)聽與TCP轉(zhuǎn)發(fā)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#使用GZipStream解壓縮數(shù)據(jù)文件的方法
這篇文章主要介紹了C#使用GZipStream解壓縮數(shù)據(jù)文件的方法,實例分析了C#中GZipStream方法的原理與使用技巧,需要的朋友可以參考下2015-04-04
C#中ftp檢測目錄是否存在和創(chuàng)建文件夾的實現(xiàn)
本文主要介紹了C#中ftp檢測目錄是否存在和創(chuàng)建文件夾的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
C#實現(xiàn)將HTML轉(zhuǎn)換成純文本的方法
這篇文章主要介紹了C#實現(xiàn)將HTML轉(zhuǎn)換成純文本的方法,基于自定義類實現(xiàn)文本轉(zhuǎn)換功能,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07

