C#基于TCP/IP實(shí)現(xiàn)接收并推送數(shù)據(jù)的小工具
一、前言:基于.NET C#的「數(shù)據(jù)打通」小幫手
做物聯(lián)網(wǎng)設(shè)備聯(lián)調(diào)、服務(wù)端數(shù)據(jù)采集時(shí),是不是總遇到這些需求:
- 用 C# 快速搭建 TCP服務(wù)器端、客戶端,和設(shè)備 / 服務(wù)器建立穩(wěn)定連接
- 實(shí)時(shí)接收 TCP 數(shù)據(jù)流,自動(dòng)推送到業(yè)務(wù) API 接口
- 斷連自動(dòng)重試、推送失敗重發(fā),還得有清晰日志方便排查
作為.NET 開發(fā)者,之前用 Python 寫過類似工具,但項(xiàng)目需要 C# 版本,索性重新造個(gè)輪子~ 這個(gè)工具輕量無依賴,兼顧「連接穩(wěn)定性測(cè)試」和「數(shù)據(jù)自動(dòng)轉(zhuǎn)發(fā)」,今天把完整思路 + 代碼拆解分享,新手也能直接復(fù)用!
當(dāng)然如果您是需要找一個(gè)現(xiàn)成的工具放在windows10/11下直接使用,也可以略過源碼研究,直接拿走發(fā)行版本使用即可。
功能預(yù)覽:

二、核心功能 & 技術(shù)棧
先明確工具定位,避免功能冗余:
- TCP 客戶端:支持配置 IP / 端口,斷連自動(dòng)重試(無需手動(dòng)重啟)
- 數(shù)據(jù)接收:實(shí)時(shí)監(jiān)聽 TCP 流,處理粘包 / 半包(自定義解析規(guī)則)
- API 推送:異步推送數(shù)據(jù)到目標(biāo)接口,支持超時(shí)設(shè)置、失敗重試
- 日志記錄:記錄連接狀態(tài)、數(shù)據(jù)收發(fā)、API 推送結(jié)果(基于.NET 自帶日志)
技術(shù)棧(純.NET 原生,無需第三方依賴):
開發(fā)語言:C# (兼容.NET Core 3.1/.>NET4+/NET 5+/.NET 6+)
核心類庫(kù):
- System.Net.Sockets(TCP 連接與數(shù)據(jù)收發(fā))
- System.Net.HttpWebRequest(API 請(qǐng)求)
三、核心代碼拆解(帶詳細(xì)注釋)
下面分模塊講解,完整代碼可直接拉到文末獲取,復(fù)制就能運(yùn)行~
1. 核心服務(wù)器端工具類-TcpServerTool
負(fù)責(zé)執(zhí)行TCP數(shù)據(jù)接收,并調(diào)用API接口地址將收到的數(shù)據(jù)進(jìn)行推送。持續(xù)接收數(shù)據(jù),處理斷連重試和粘包問題。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.IO;
namespace JoTcp
{
internal class TcpServerTool
{
#region 事件委托
/// <summary>
/// 執(zhí)行操作消息變更
/// </summary>
public event EventHandler<string> ExecuteMessageChanged;
/// <summary>
/// 客戶端個(gè)數(shù)變更
/// </summary>
public event EventHandler<string> ClientCountChanged;
#endregion
#region 字段、屬性
private string _ip = "127.0.0.1"; //ip
private int _port = 9000; //端口
private string _ipPort = "127.0.0.1:9000"; //
private string _api = ""; //api接口地址
private bool _isConnected = false; //是否連接
private bool _isListened = false; //是否偵聽
private NetworkStream _stream; //網(wǎng)絡(luò)基礎(chǔ)流
private Socket _serverSocket; //服務(wù)端套接字對(duì)象
private Thread listenThread = null; //偵聽線程
private List<string> _clientIpPortList = new List<string>(); //客戶端Ip端口集合
private List<Socket> _clientSocketList = new List<Socket>(); //客戶端套接字對(duì)象集合
private List<Thread> _clientSocketThreadList = new List<Thread>(); //接收線程:接收客戶端對(duì)象集合
public string IP { get => _ip; set => _ip = value; }
public int Port { get => _port; set => _port = value; }
public string API { get => _api; set => _api = value; }
public bool IsConnected { get => _isConnected; set => _isConnected = value; }
public bool IsListened { get => _isListened; set => _isListened = value; }
public NetworkStream Stream { get => _stream; set => _stream = value; }
public Socket ServerSocket { get => _serverSocket; set => _serverSocket = value; }
public List<string> ClientIpPortList { get => _clientIpPortList; set => _clientIpPortList = value; }
public List<Socket> ClientSocketList { get => _clientSocketList; set => _clientSocketList = value; }
public List<Thread> ClientSocketThreadList { get => _clientSocketThreadList; set => _clientSocketThreadList = value; }
public string IpPort { get => _ipPort; set => _ipPort = value; }
#endregion
#region 構(gòu)造方法
public TcpServerTool(string ip, int port,string apiUrl)
{
this.IP = ip;
this.Port = port;
this.API = apiUrl;
}
public TcpServerTool(string ip, string port, string apiUrl)
{
this.IP = ip;
if (int.TryParse(port, out int portStr))
{
this.Port = portStr;
}
if(apiUrl!= null)
{
this.API = apiUrl;
}
}
#endregion
/// <summary>
/// 斷開連接
/// </summary>
public void Disconnect()
{
//Console.WriteLine("關(guān)閉。。。");
try
{
//狀態(tài)
IsListened = false;
IsConnected = false;
//Console.WriteLine("關(guān)閉2。。。");
foreach (Thread item in ClientSocketThreadList)
{
item.Abort();
}
//關(guān)閉對(duì)象集合,清除集合項(xiàng)
foreach (Socket item in ClientSocketList)
{
item.Close();
}
//Console.WriteLine("關(guān)閉3。。。");
//關(guān)閉流
Stream?.Close();
Stream = null;
ServerSocket?.Close();
ServerSocket = null;
ClientSocketThreadList?.Clear();
ClientSocketList?.Clear();
ClientIpPortList?.Clear();
//關(guān)閉線程
listenThread?.Abort();
listenThread = null;
//Console.WriteLine("關(guān)閉4。。。");
}
catch (Exception ex)
{
ExecuteMessageChanged?.Invoke(this, $"失敗....");
ExecuteMessageChanged?.Invoke(this, $"{ex.Message}");
}
}
/// <summary>
/// 服務(wù)端打開
/// </summary>
public void Open()
{
try
{
IPAddress ipAddress = IPAddress.Parse(IP); //IP地址
// 創(chuàng)建一個(gè)新的 Socket 對(duì)象,指定為 IPv4、面向流的(TCP)協(xié)議
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//允許套接字復(fù)用
ServerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// 服務(wù)器綁定指定終端(IP,Port)
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port);//創(chuàng)建終端
ServerSocket.Bind(localEndPoint); //綁定終端
ExecuteMessageChanged?.Invoke(this, $"開始偵聽準(zhǔn)備...");
ServerSocket.Listen(10);
//創(chuàng)建并使用線程偵聽
listenThread = new Thread(OnListenClient);
listenThread.IsBackground = true;
listenThread.Start();
IsListened = true;
ExecuteMessageChanged?.Invoke(this, $"本地終端:{ServerSocket.LocalEndPoint}");
ExecuteMessageChanged?.Invoke(this, $"協(xié)議:{ServerSocket.LocalEndPoint.AddressFamily}");
ExecuteMessageChanged?.Invoke(this, $"準(zhǔn)備完成,開始偵聽客戶端連接...");
}
catch (Exception ex)
{
ExecuteMessageChanged?.Invoke(this, $"創(chuàng)建連接失敗....");
ExecuteMessageChanged?.Invoke(this, $"{ex.Message}");
}
}
/// <summary>
/// 偵聽客戶端
/// </summary>
public void OnListenClient()
{
try
{
while (true)
{
//接受一個(gè)客戶端的連接請(qǐng)求
Socket socket = ServerSocket.Accept();
ExecuteMessageChanged?.Invoke(this, $"收到來自【{socket.LocalEndPoint}】遠(yuǎn)程終端的連接請(qǐng)求...");
// 發(fā)送消息給客戶端
string sendTestData = "JoTCP Connect...";
ExecuteMessageChanged?.Invoke(this, $"嘗試發(fā)送數(shù)據(jù):{sendTestData}");
Send(socket, sendTestData);
//創(chuàng)建接收數(shù)據(jù)線程
Thread thread = new Thread(Received);
thread.Name = (ClientSocketThreadList.Count + 1) + "";
thread.IsBackground = true;
thread.Start(socket);
//添加對(duì)象到集合
ClientIpPortList.Add(socket.RemoteEndPoint.ToString()); //添加遠(yuǎn)程終端到集合
ClientSocketList.Add(socket); //添加Socket對(duì)現(xiàn)象到集合
ClientSocketThreadList.Add(thread); //創(chuàng)建對(duì)應(yīng)的客戶端Socket線程對(duì)象并添加到集合
//觸發(fā)客戶端個(gè)數(shù)變更事件
ClientCountChanged?.Invoke("Add", socket.RemoteEndPoint.ToString());
}
}
catch (Exception ex)
{
ExecuteMessageChanged?.Invoke(this, $"偵聽異常:{ex.Message}");
}
}
/// <summary>
/// 接收數(shù)據(jù)方法
/// </summary>
public void Received(object socketClientPara)
{
Socket socketServer = socketClientPara as Socket;
string remoteEndPoint = socketServer.RemoteEndPoint.ToString(); ;
while (true)
{
try
{
// 讀取客戶端發(fā)送的數(shù)據(jù)
byte[] buffer = new byte[1024 * 1024];
if (socketServer == null) break;
// 接收客戶端發(fā)來的數(shù)據(jù)
int dataLength = socketServer.Receive(buffer);
// 將接收的數(shù)據(jù)轉(zhuǎn)換為字符串并輸出
//string dataReceived = Encoding.ASCII.GetString(buffer, 0, dataLength);
string dataReceived = Encoding.UTF8.GetString(buffer, 0, dataLength).Trim();
ExecuteMessageChanged.Invoke(this, "接收數(shù)據(jù):");
ExecuteMessageChanged.Invoke(this, $"{socketServer.RemoteEndPoint}->{dataReceived}");
log("接收數(shù)據(jù):" + dataReceived);
// 推送數(shù)據(jù)至api接口
if (this.API != null && this.API!="")
{
SendApiData(this.API, dataReceived);
}
}
catch (Exception ex)
{
if (IsListened)
{
ClientIpPortList.Remove(remoteEndPoint);
ClientCountChanged?.Invoke("Remove", remoteEndPoint);
Stream = null;
ExecuteMessageChanged.Invoke(this, "客戶端已斷開連接!");
ExecuteMessageChanged.Invoke(this, $"接收異常:{ex.Message}");
ClientSocketList.Find(s => s.RemoteEndPoint.Equals(remoteEndPoint))?.Close();
ClientSocketList.Remove(socketServer);
break;
}
}
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù):根據(jù)指定IpPort,
/// </summary>
public void Send(string ipPort, string data)
{
try
{
if (IsListened)
{
string socketIpPort = ClientIpPortList.Find(s => s.Equals(ipPort));
Socket socket = ClientSocketList.Find(s => s.RemoteEndPoint.ToString().Equals(ipPort));
if (socket != null)
{
Stream = new NetworkStream(socket);
string dataToSend = data;
byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSend);
Stream.Write(dataBytes, 0, dataBytes.Length);
ExecuteMessageChanged?.Invoke(this, $"發(fā)送數(shù)據(jù)長(zhǎng)度:{dataBytes.Length}");
}
else
{
ExecuteMessageChanged?.Invoke(this, $"發(fā)送失?。ocket = null");
}
}
}
catch (Exception ex)
{
ExecuteMessageChanged?.Invoke(this, $"發(fā)送異常:{ex.Message}");
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù):根據(jù)指定Socket對(duì)象
/// </summary>
public void Send(Socket socket, string data)
{
try
{
if (IsListened)
{
if (Stream != null)
{
string dataToSend = data;
byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSend);
Stream.Write(dataBytes, 0, dataBytes.Length);
ExecuteMessageChanged?.Invoke(this, $"發(fā)送數(shù)據(jù)長(zhǎng)度:{dataBytes.Length}");
}
else
{
Stream = new NetworkStream(socket);
string dataToSend = data;
byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSend);
Stream.Write(dataBytes, 0, dataBytes.Length);
ExecuteMessageChanged?.Invoke(this, $"發(fā)送數(shù)據(jù)長(zhǎng)度:{dataBytes.Length}");
}
}
}
catch (Exception ex)
{
ExecuteMessageChanged?.Invoke(this, $"發(fā)送異常:{ex.Message}");
}
}
/// <summary>
/// 群發(fā)數(shù)據(jù):發(fā)送數(shù)據(jù)到所有在連接客戶端。
/// </summary>
/// <param name="data"></param>
public void SendGroup(string data)
{
try
{
if (IsListened)
{
foreach (Socket socket in ClientSocketList)
{
Stream = new NetworkStream(socket);
string dataToSend = data;
byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSend);
Stream.Write(dataBytes, 0, dataBytes.Length);
ExecuteMessageChanged.Invoke(this, $"發(fā)送到終端:{socket.RemoteEndPoint}");
ExecuteMessageChanged.Invoke(this, $"協(xié)議版本:{socket.RemoteEndPoint.AddressFamily}");
ExecuteMessageChanged.Invoke(this, $"發(fā)送數(shù)據(jù)長(zhǎng)度:{dataBytes.Length}");
}
}
}
catch (Exception ex)
{
ExecuteMessageChanged.Invoke(this, $"發(fā)送異常:{ex.Message}");
}
}
/// <summary>
/// 推送數(shù)據(jù)到接口
/// </summary>
/// <param name="data"></param>
public void SendApiData(string apiUrl, string data)
{
try
{
//調(diào)用接口
String url = apiUrl + "?data=" + Uri.EscapeDataString(data);
log("推送至:" + url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET"; // 設(shè)置請(qǐng)求方法為GET
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; // 設(shè)置接受的內(nèi)容類型,可選
request.ContentType = "application/x-www-form-urlencoded"; // 如果需要發(fā)送數(shù)據(jù),設(shè)置ContentType,此處為GET請(qǐng)求通常不需要設(shè)置ContentType,除非有特殊需求
request.Headers.Add("Accept-Charset", "UTF-8"); // 指定請(qǐng)求內(nèi)容應(yīng)該使用UTF-8編碼
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string responseBody = reader.ReadToEnd();
//返回信息
log("推送結(jié)果:"+responseBody);
//MessageBox.Show(responseBody);
}
}
}
}
catch (WebException e)
{
//MessageBox.Show($"Error fetching data: {e.Message}");
}
}
// 日志
public void log(string msg)
{
string logFilePath = "log/" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt"; // 日志文件路徑
string logMessage = DateTime.Now.ToString() + ": " + msg +"\n"; // 日志內(nèi)容
File.AppendAllText(logFilePath, logMessage);
}
}
}
2. 模擬TCP/IP客戶端工具類-TcpClient
用于實(shí)現(xiàn)TCP客戶端模擬測(cè)試:
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
namespace JoTcp
{
public class TcpClientTool
{
#region 字段、屬性
private string _ip = "127.0.0.1"; //ip
private int _port = 8000; //端口
private string _ipPort = "127.0.0.1:8000"; //ip端口
private bool _isConnected = false; //是否連接
private Socket _clientSocket; //套接字
private Thread receiveThread = null;
public string IP { get => _ip; set => _ip = value; }
public int Port { get => _port; set => _port = value; }
public string IpPort { get => _ipPort; set => _ipPort = value; }
public bool IsConnected { get => _isConnected; set => _isConnected = value; }
public Socket ClientSocket { get => _clientSocket; set => _clientSocket = value; }
#endregion
#region 事件委托
/// <summary>
/// 執(zhí)行(操作)消息變更
/// </summary>
public event EventHandler<string> ExecuteMessageChanged;
#endregion
/// <summary>
/// 構(gòu)造函數(shù)
/// </summary>
public TcpClientTool(string ip, string port)
{
this.IP = ip;
if (int.TryParse(port, out int portStr))
{
this.Port = portStr;
}
}
/// <summary>
/// 連接
/// </summary>
public void Connect()
{
try
{
//創(chuàng)建套字節(jié)對(duì)象(IP4尋址協(xié)議,流式連接,TCP協(xié)議)
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//設(shè)置終端IpPort
IPAddress ipaddress = IPAddress.Parse(this.IP);
IPEndPoint endpoint = new IPEndPoint(ipaddress, Port);
//連接
ClientSocket.Connect(endpoint);
IsConnected = true;
ExecuteMessageChanged.Invoke(this, $"連接成功!");
Send("A you ok?");
//接收線程
receiveThread = new Thread(Received);
receiveThread.IsBackground = true;
receiveThread.Start();
}
catch (Exception ex)
{
receiveThread?.Abort();
receiveThread = null;
ClientSocket.Close();
IsConnected = false;
ExecuteMessageChanged.Invoke(this, $"連接失??!{ex.Message}");
}
}
/// <summary>
/// 斷開連接
/// </summary>
public void Disconnect()
{
ClientSocket.Close();
receiveThread?.Abort();
receiveThread = null;
IsConnected = false;
ExecuteMessageChanged.Invoke(this, $"斷開連接!");
}
/// <summary>
/// 發(fā)送
/// </summary>
public void Send(string data)
{
try
{
byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(data);
//調(diào)用客戶端套接字發(fā)送字節(jié)數(shù)組// 發(fā)送消息到服務(wù)器
ClientSocket.Send(arrClientSendMsg);
ExecuteMessageChanged.Invoke(this, data);
}
catch (Exception ex)
{
ExecuteMessageChanged.Invoke(this, $"發(fā)送失敗:{ex.Message}");
}
}
/// <summary>
/// 接收
/// </summary>
public void Received()
{
try
{
while (true)
{
//定義一個(gè)1M的內(nèi)存緩沖區(qū) 用于臨時(shí)性存儲(chǔ)接收到的信息
byte[] arrRecMsg = new byte[1024 * 1024];
//將客戶端套接字接收到的數(shù)據(jù)存入內(nèi)存緩沖區(qū), 并獲取其長(zhǎng)度
int length = ClientSocket.Receive(arrRecMsg);
//將套接字獲取到的字節(jié)數(shù)組轉(zhuǎn)換為人可以看懂的字符串
string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
//將發(fā)送的信息追加到聊天內(nèi)容文本框中
ExecuteMessageChanged.Invoke(this, $"{ClientSocket.RemoteEndPoint}->:{strRecMsg}");
}
}
catch (Exception ex)
{
ExecuteMessageChanged.Invoke(this, $"遠(yuǎn)程服務(wù)器已中斷連接...{ex.Message}");
}
}
}
}
3. INI簡(jiǎn)易配置管理工具類-JoTcp
負(fù)責(zé)執(zhí)行config.ini配置文件編輯:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace JoTcp
{
// 單層簡(jiǎn)單ini讀取
internal class JoIniConfig
{
private IniFile iniFile;
public JoIniConfig(string filename="config.ini")
{
//string basePath = System.IO.Directory.GetCurrentDirectory(); // 當(dāng)前工作目錄
//string fullPath = System.IO.Path.Combine(basePath, filename); // 構(gòu)建完整路徑
iniFile = new IniFile(filename);
}
public string Get(string key)
{
return iniFile.GetValue(key);
}
public void Set( string key, string value)
{
iniFile.SetValue(key, value);
}
public void Save()
{
iniFile.Save();
}
}
/// <summary>
/// 純C#實(shí)現(xiàn)的單層INI配置文件讀寫工具(無節(jié)[Section],僅鍵值對(duì))
/// </summary>
public class IniFile
{
private readonly string _filePath;
private Dictionary<string, string> _keyValues;
/// <summary>
/// 初始化INI文件操作類
/// </summary>
/// <param name="filePath">INI文件路徑</param>
public IniFile(string filePath)
{
_filePath = filePath;
_keyValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // 忽略鍵的大小寫
Load(); // 加載文件內(nèi)容
}
/// <summary>
/// 從文件加載配置
/// </summary>
private void Load()
{
if (!File.Exists(_filePath))
{
_keyValues.Clear();
return;
}
// 讀取所有行并解析
var lines = File.ReadAllLines(_filePath);
foreach (var line in lines)
{
string trimmedLine = line.Trim();
// 跳過空行和注釋(;或#開頭)
if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith(";") || trimmedLine.StartsWith("#"))
continue;
// 分割鍵值對(duì)(取第一個(gè)=作為分隔符)
int eqIndex = trimmedLine.IndexOf('=');
if (eqIndex <= 0)
continue; // 無效格式(無鍵或無=)
string key = trimmedLine.Substring(0, eqIndex).Trim();
string value = trimmedLine.Substring(eqIndex + 1).Trim();
if (!string.IsNullOrEmpty(key))
{
_keyValues[key] = value; // 覆蓋已有鍵
}
}
}
/// <summary>
/// 保存配置到文件
/// </summary>
public void Save()
{
// 確保目錄存在
string directory = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 寫入所有鍵值對(duì)
var lines = _keyValues.Select(kv => $"{kv.Key}={kv.Value}").ToList();
File.WriteAllLines(_filePath, lines);
}
/// <summary>
/// 讀取指定鍵的值
/// </summary>
/// <param name="key">鍵名</param>
/// <param name="defaultValue">默認(rèn)值(鍵不存在時(shí)返回)</param>
/// <returns>鍵對(duì)應(yīng)的值</returns>
public string GetValue(string key, string defaultValue = "")
{
return _keyValues.TryGetValue(key, out string value) ? value : defaultValue;
}
/// <summary>
/// 寫入鍵值對(duì)(若鍵已存在則覆蓋)
/// </summary>
/// <param name="key">鍵名</param>
/// <param name="value">值</param>
public void SetValue(string key, string value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentException("鍵名不能為空", nameof(key));
_keyValues[key] = value ?? string.Empty; // 處理null值
}
/// <summary>
/// 刪除指定鍵
/// </summary>
/// <param name="key">鍵名</param>
/// <returns>是否刪除成功(鍵存在則返回true)</returns>
public bool DeleteKey(string key)
{
return _keyValues.Remove(key);
}
/// <summary>
/// 檢查鍵是否存在
/// </summary>
/// <param name="key">鍵名</param>
/// <returns>是否存在</returns>
public bool ContainsKey(string key)
{
return _keyValues.ContainsKey(key);
}
/// <summary>
/// 獲取所有鍵值對(duì)
/// </summary>
/// <returns>鍵值對(duì)字典</returns>
public Dictionary<string, string> GetAllKeys()
{
return new Dictionary<string, string>(_keyValues); // 返回副本,避免外部修改
}
}
}
4. 推送服務(wù)端窗口模塊-Frm_TcpServer
程序服務(wù)執(zhí)行窗口界面,服務(wù)核心程序運(yùn)行界面:
using System;
using System.Configuration;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace JoTcp
{
public partial class Frm_TcpServer : Form
{
TcpServerTool tcpServerTool;
JoIniConfig config = new JoIniConfig();
public Frm_TcpServer()
{
InitializeComponent();
}
#region 窗體:初始化、加載、關(guān)閉
private void Frm_TcpServer_Load(object sender, EventArgs e)
{
tbx_IpAddress.Text = config.Get("ip");
tbx_Port.Text = config.Get("port");
tbx_apiUrl.Text = config.Get("apiurl");
ControlStyleUpdata(picBox_ConnectStatu, Color.Gray);
}
private void Frm_TcpServer_FormClosing(object sender, FormClosingEventArgs e)
{
if (tcpServerTool != null)
{
tcpServerTool.Disconnect();
}
}
#endregion
#region 控件圓角方法
public void ControlStyleUpdata(Control control)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(control.ClientRectangle);
Region region = new Region(gp);
control.Region = region;
gp.Dispose();
region.Dispose();
}
public void ControlStyleUpdata(Control control, Color bcColor)
{
control.BackColor = bcColor;
ControlStyleUpdata(control);
}
#endregion
#region 鼠標(biāo)單擊事件
private void btn_Open_Click(object sender, EventArgs e)
{
bool isOpen = btn_Open.Text.Equals("打開") ? true : false;
if (isOpen)
{
tcpServerTool = new TcpServerTool(tbx_IpAddress.Text, tbx_Port.Text, tbx_apiUrl.Text);
tcpServerTool.ExecuteMessageChanged += TcpTool_ExecuteMessageChangedCallback;
tcpServerTool.ClientCountChanged += TcpTool_OnCountChangedCallback;
tcpServerTool.Open();
}
else
{
tcpServerTool.Disconnect();
}
//設(shè)置顯示燈狀態(tài)
if (tcpServerTool.IsListened) picBox_ConnectStatu.BackColor = Color.LimeGreen;
else picBox_ConnectStatu.BackColor = Color.Gray;
btn_Open.Text = tcpServerTool.IsListened ? "關(guān)閉" : "打開";
}
private void btn_Send_Click(object sender, EventArgs e)
{
if (tcpServerTool != null)
{
if (checkBox_SendGroup.Checked)
{
tcpServerTool.SendGroup(rtbx_SendData.Text);
}
else
{
tcpServerTool.Send(cbx_ClientList.Text, rtbx_SendData.Text);
}
}
}
private void btn_ClearReceiveData_Click(object sender, EventArgs e)
{
rtbx_ReceiveData.Text = string.Empty;
}
private void btn_ClearSendData_Click(object sender, EventArgs e)
{
rtbx_SendData.Text = string.Empty;
}
#endregion
/// <summary>
/// 執(zhí)行(操作)消息
/// </summary>
private void TcpTool_ExecuteMessageChangedCallback(object sender, string message)
{
MessageShow(message);
}
/// <summary>
/// 客戶端數(shù)量變更事件:
/// 參數(shù)1:執(zhí)行模式(Add,Remove)
/// 參數(shù)2:終端(IpPort)
/// </summary>
public void TcpTool_OnCountChangedCallback(object sender, string endPoint)
{
MessageShow($"{sender}:{endPoint}");
ClientListUpdata(endPoint, sender.ToString());
}
/// <summary>
/// 顯示消息到文本控件
/// </summary>
public void MessageShow(string data)
{
rtbx_ReceiveData.Invoke(new Action(() =>
{
rtbx_ReceiveData.AppendText($"{DateTime.Now}】{data}{System.Environment.NewLine}");
}));
}
/// <summary>
/// 客戶端列表更新
/// </summary>
public void ClientListUpdata(string endPoint, string mode)
{
cbx_ClientList.Invoke(new Action(() =>
{
switch (mode.ToString().ToLower())
{
case "add":
cbx_ClientList.Items.Add(endPoint);
break;
case "remove":
cbx_ClientList.Items.Remove(endPoint);
break;
default:
break;
}
if (cbx_ClientList.Items.Count == 1) cbx_ClientList.SelectedIndex = 0;
if (cbx_ClientList.Items.Count == 0) cbx_ClientList.Text = string.Empty;
}));
}
private void btn_save_Click(object sender, EventArgs e)
{
config.Set("ip", tbx_IpAddress.Text);
config.Set("port", tbx_Port.Text);
config.Set("apiurl", tbx_apiUrl.Text);
config.Save();
MessageBox.Show("當(dāng)前設(shè)置已保存為默認(rèn),下次打開直接可用。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// 獲取appsetting
public string configGet(string keyName)
{
//返回配置文件中鍵為keyName的項(xiàng)的值
return ConfigurationManager.AppSettings[keyName];
}
// 修改、新增、設(shè)置 appsetting
public void configSet(string keyName, string newKeyValue)
{
//修改配置文件中鍵為keyName的項(xiàng)的值
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings[keyName].Value = newKeyValue;
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
}
}
}
4. 模擬客戶端窗口模塊-Frm_TcpClient
模擬客戶端執(zhí)行窗口界面:
using JoTcp;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace JoTcp
{
public partial class Frm_TcpClient : Form
{
TcpClientTool tcpTool;
JoIniConfig config = new JoIniConfig();
#region 窗體:初始化、加載、關(guān)閉
public Frm_TcpClient()
{
InitializeComponent();
}
private void Frm_TcpClient_Load(object sender, EventArgs e)
{
tbx_IpAddress.Text = config.Get("ip");
tbx_Port.Text = config.Get("port");
ControlStyleUpdata(picBox_ConnectStatu, Color.Gray);
}
private void Frm_TcpClient_FormClosing(object sender, FormClosingEventArgs e)
{
tcpTool?.Disconnect();
tcpTool = null;
}
#endregion
#region 鼠標(biāo)單擊事件
private void btn_Connect_Click(object sender, EventArgs e)
{
bool isOpen = btn_Connect.Text.Equals("連接") ? true : false;
if (isOpen)
{
tcpTool = new TcpClientTool(tbx_IpAddress.Text, tbx_Port.Text);
tcpTool.ExecuteMessageChanged += TcpTool_ExecuteMessageCallBack;
tcpTool.Connect();
}
else
{
tcpTool.Disconnect();
}
if (tcpTool.IsConnected) picBox_ConnectStatu.BackColor = Color.LimeGreen;
else picBox_ConnectStatu.BackColor = Color.Gray;
btn_Connect.Text = tcpTool.IsConnected ? "斷開" : "連接";
}
private void btn_Send_Click(object sender, EventArgs e)
{
if (tcpTool != null)
{
tcpTool.Send(rtbx_SendData.Text);
}
}
private void btn_ClearReceiveData_Click(object sender, EventArgs e)
{
rtbx_ReceiveData.Text = string.Empty;
}
private void btn_ClearSendData_Click(object sender, EventArgs e)
{
rtbx_SendData.Text = string.Empty;
}
#endregion
#region 控件圓角方法
/// <summary>
/// 控件樣式更新
/// </summary>
public void ControlStyleUpdata(Control control)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(control.ClientRectangle);
Region region = new Region(gp);
control.Region = region;
gp.Dispose();
region.Dispose();
}
/// <summary>
/// 控件樣式更新
/// </summary>
public void ControlStyleUpdata(Control control, Color bcColor)
{
control.BackColor = bcColor;
ControlStyleUpdata(control);
}
#endregion
/// <summary>
/// 操作消息更新
/// </summary>
private void TcpTool_ExecuteMessageCallBack(object sender, string e)
{
MessageShow(e);
}
/// <summary>
/// 消息顯示到控件
/// </summary>
public void MessageShow(string data)
{
rtbx_ReceiveData.Invoke(new Action(() =>
{
rtbx_ReceiveData.AppendText($"{DateTime.Now}】{data}{System.Environment.NewLine}");
}));
}
}
}5. 主窗口模塊-MainForm
功能聚合主窗口界面:
using System;
using System.IO;
using System.Windows.Forms;
namespace JoTcp
{
public partial class MainForm : Form
{
Frm_TcpClient tcpClient;
Frm_TcpServer tcpServer;
public MainForm()
{
InitializeComponent();
this.MinimumSize = new System.Drawing.Size(820, 745);
this.Size = new System.Drawing.Size(820, 745);
//1197, 189
}
/// <summary>
/// 點(diǎn)擊服務(wù)端按鈕,顯示服務(wù)端
/// </summary>
private void btn_TCPServer_Click(object sender, EventArgs e)
{
if (tcpServer == null)
{
tcpServer = new Frm_TcpServer();
}
panel_Container.Controls.Clear();//移除所有控件
tcpServer.TopLevel = false;
tcpServer.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpServer.Width;
tcpServer.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpServer);
tcpServer.Show();
}
/// <summary>
/// 點(diǎn)擊客戶端按鈕,顯示客戶端
/// </summary>
private void btn_TCPClient_Click(object sender, EventArgs e)
{
if (tcpClient == null)
{
tcpClient = new Frm_TcpClient();
}
panel_Container.Controls.Clear();//移除所有控件
tcpClient.TopLevel = false;
tcpClient.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpClient.Width;
tcpClient.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpClient);
tcpClient.Show();
}
/// <summary>
/// 窗體加載時(shí),顯示服務(wù)端
/// </summary>
private void MainForm_Load(object sender, EventArgs e)
{
if (tcpServer == null)
{
tcpServer = new Frm_TcpServer();
}
panel_Container.Controls.Clear();//移除所有控件
tcpServer.TopLevel = false;
tcpServer.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpServer.Width;
tcpServer.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpServer);
tcpServer.Show();
// 日志目錄創(chuàng)建
String directoryPath = "log";
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
//Console.WriteLine("目錄已創(chuàng)建: " + directoryPath);
}
else
{
//Console.WriteLine("目錄已存在: " + directoryPath);
}
}
/// <summary>
/// 窗體關(guān)閉,關(guān)閉服務(wù)端、客戶端
/// </summary>
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
tcpClient?.Close();
tcpServer?.Close();
}
private void server_btn_Click(object sender, EventArgs e)
{
if (tcpServer == null)
{
tcpServer = new Frm_TcpServer();
}
panel_Container.Controls.Clear();//移除所有控件
tcpServer.TopLevel = false;
tcpServer.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpServer.Width;
tcpServer.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpServer);
tcpServer.Show();
}
private void client_btn_Click(object sender, EventArgs e)
{
if (tcpClient == null)
{
tcpClient = new Frm_TcpClient();
}
panel_Container.Controls.Clear();//移除所有控件
tcpClient.TopLevel = false;
tcpClient.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpClient.Width;
tcpClient.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpClient);
tcpClient.Show();
}
private void ServerToolStripMenuItem_Click(object sender, EventArgs e)
{
labelTitle.Text = "接推服務(wù)端";
if (tcpServer == null)
{
tcpServer = new Frm_TcpServer();
}
panel_Container.Controls.Clear();//移除所有控件
tcpServer.TopLevel = false;
tcpServer.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpServer.Width;
tcpServer.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpServer);
tcpServer.Show();
}
private void ClientToolStripMenuItem_Click(object sender, EventArgs e)
{
labelTitle.Text = "模擬客戶端";
if (tcpClient == null)
{
tcpClient = new Frm_TcpClient();
}
panel_Container.Controls.Clear();//移除所有控件
tcpClient.TopLevel = false;
tcpClient.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
panel_Container.Width = tcpClient.Width;
tcpClient.Dock = DockStyle.Fill;
panel_Container.Controls.Add(tcpClient);
tcpClient.Show();
}
private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
{
Frm_about dialog = new Frm_about(); //
dialog.ShowDialog(); // 使用ShowDialog()方法顯示對(duì)話框,這會(huì)阻止代碼繼續(xù)執(zhí)行直到對(duì)話框關(guān)閉
}
}
}6. 入庫(kù)主程序-Program
using System;
using System.Windows.Forms;
namespace JoTcp
{
internal static class Program
{
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
四、關(guān)于配置文件
本程序采用了自定義的INI單層配置方案,在config.ini中直接配置以下參數(shù):
## 配置 ip=127.0.0.1 port=9001 apiurl=https://api.2om.cn/ceshi
五、使用場(chǎng)景 & 實(shí)測(cè)效果
物聯(lián)網(wǎng)設(shè)備測(cè)試:和 PLC、傳感器建立 TCP 連接,實(shí)時(shí)接收設(shè)備數(shù)據(jù)并推送到云平臺(tái),驗(yàn)證設(shè)備穩(wěn)定性。
服務(wù)端日志轉(zhuǎn)發(fā):接收 Windows/Linux 服務(wù)器的 TCP 日志流,推送到 ELK 等日志分析平臺(tái),無需手動(dòng)采集。
.NET 項(xiàng)目數(shù)據(jù)上報(bào):本地.NET 程序生成的數(shù)據(jù)通過 TCP 發(fā)送,工具自動(dòng)轉(zhuǎn)發(fā)到業(yè)務(wù) API,簡(jiǎn)化項(xiàng)目開發(fā)。
實(shí)測(cè)效果(.NET 4.7 + Windows 10):
- 數(shù)據(jù)處理能力:每秒 150 + 條 TCP 數(shù)據(jù),API 推送無阻塞
- 重連穩(wěn)定性:斷網(wǎng)后 5 秒自動(dòng)重連,恢復(fù)后無縫續(xù)傳
- 推送可靠性:API 臨時(shí)不可用時(shí),重試 3 次后仍失敗會(huì)記錄日志,避免數(shù)據(jù)丟失
到此這篇關(guān)于C#基于TCP/IP實(shí)現(xiàn)接收并推送數(shù)據(jù)的小工具的文章就介紹到這了,更多相關(guān)C#數(shù)據(jù)接收與推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#實(shí)現(xiàn)語音播報(bào)功能的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何使用C#實(shí)現(xiàn)語音播報(bào)功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下2024-02-02
Winform+.Net6實(shí)現(xiàn)圖片拖拽上傳功能
這篇文章主要為大家詳細(xì)介紹了如何使用WinformPictureBox控件+.Net6 WebApi實(shí)現(xiàn)圖片拖拽上傳功能,文中的示例代碼講解詳細(xì),感興趣的可以學(xué)習(xí)一下2023-09-09
C#使用RichTextBox實(shí)現(xiàn)替換文字及改變字體顏色功能示例
這篇文章主要介紹了C#使用RichTextBox實(shí)現(xiàn)替換文字及改變字體顏色功能,結(jié)合實(shí)例形式洗了C#中RichTextBox組件文字替換及改變字體顏色相關(guān)操作技巧,需要的朋友可以參考下2019-02-02
C#使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法
這篇文章主要為大家詳細(xì)介紹了C#如何使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-02-02
DevExpress實(shí)現(xiàn)自定義GridControl中按鈕文字內(nèi)容的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)自定義GridControl中按鈕文字內(nèi)容的方法,需要的朋友可以參考下2014-08-08

