当前位置: 首页 > news >正文

一个基于TCP/IP接收数据并通过API推送数据的小工具

一、前言:基于.NET  C#的「数据打通」小帮手

**

做物联网设备联调、服务端数据采集时,是不是总遇到这些需求:

✅ 用 C# 快速搭建 TCP服务器端、客户端,和设备 / 服务器建立稳定连接

✅ 实时接收 TCP 数据流,自动推送到业务 API 接口

✅ 断连自动重试、推送失败重发,还得有清晰日志方便排查

作为.NET 开发者,之前用 Python 写过类似工具,但项目需要 C# 版本,索性重新造个轮子~ 这个工具轻量无依赖,兼顾「连接稳定性测试」和「数据自动转发」,今天把完整思路 + 代码拆解分享,新手也能直接复用!

当然如果您是需要找一个现成的工具放在windows10/11下直接使用,也可以略过源码研究,直接拿走发行版本使用即可。

功能预览:

二、核心功能 & 技术栈

先明确工具定位,避免功能冗余:

  1. TCP 客户端:支持配置 IP / 端口,断连自动重试(无需手动重启)
  1. 数据接收:实时监听 TCP 流,处理粘包 / 半包(自定义解析规则)
  1. API 推送:异步推送数据到目标接口,支持超时设置、失败重试
  1. 日志记录:记录连接状态、数据收发、API 推送结果(基于.NET 自带日志)

技术栈(纯.NET 原生,无需第三方依赖):

  • 开发语言:C# (兼容.NET Core 3.1/.>NET4+/NET 5+/.NET 6+)
  • 核心类库:
    • System.Net.Sockets(TCP 连接与数据收发)
    • System.Net.HttpWebRequest(API 请求)

三、核心代码拆解(带详细注释)

下面分模块讲解,完整代码可直接拉到文末获取,复制就能运行~

1. 核心服务器端工具类-TcpServerTool

负责执行TCP数据接收,并调用API接口地址将收到的数据进行推送。持续接收数据,处理断连重试和粘包问题。

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>/// 执行操作消息变更/// </summary>public event EventHandler<string> ExecuteMessageChanged;/// <summary>/// 客户端个数变更/// </summary>public event EventHandler<string> ClientCountChanged;#endregion#region 字段、属性private string _ip = "127.0.0.1";           //ipprivate 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;              //网络基础流private Socket _serverSocket;               //服务端套接字对象private Thread listenThread = null;         //侦听线程private List<string> _clientIpPortList = new List<string>();   //客户端Ip端口集合private List<Socket> _clientSocketList = new List<Socket>();    //客户端套接字对象集合    private List<Thread> _clientSocketThreadList = new List<Thread>(); //接收线程:接收客户端对象集合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 构造方法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("关闭。。。");try{//状态IsListened = false;IsConnected = false;//Console.WriteLine("关闭2。。。");foreach (Thread item in ClientSocketThreadList){item.Abort();}//关闭对象集合,清除集合项foreach (Socket item in ClientSocketList){item.Close();}//Console.WriteLine("关闭3。。。");//关闭流Stream?.Close();Stream = null;ServerSocket?.Close();ServerSocket = null;ClientSocketThreadList?.Clear();ClientSocketList?.Clear();ClientIpPortList?.Clear();//关闭线程listenThread?.Abort();listenThread = null;//Console.WriteLine("关闭4。。。");}catch (Exception ex){ExecuteMessageChanged?.Invoke(this, $"失败....");ExecuteMessageChanged?.Invoke(this, $"{ex.Message}");}}/// <summary>/// 服务端打开/// </summary>public void Open(){try{IPAddress ipAddress = IPAddress.Parse(IP);  //IP地址// 创建一个新的 Socket 对象,指定为 IPv4、面向流的(TCP)协议ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//允许套接字复用ServerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);// 服务器绑定指定终端(IP,Port)IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port);//创建终端ServerSocket.Bind(localEndPoint);   //绑定终端ExecuteMessageChanged?.Invoke(this, $"开始侦听准备...");ServerSocket.Listen(10);//创建并使用线程侦听listenThread = new Thread(OnListenClient);listenThread.IsBackground = true;listenThread.Start();IsListened = true;ExecuteMessageChanged?.Invoke(this, $"本地终端:{ServerSocket.LocalEndPoint}");ExecuteMessageChanged?.Invoke(this, $"协议:{ServerSocket.LocalEndPoint.AddressFamily}");ExecuteMessageChanged?.Invoke(this, $"准备完成,开始侦听客户端连接...");}catch (Exception ex){ExecuteMessageChanged?.Invoke(this, $"创建连接失败....");ExecuteMessageChanged?.Invoke(this, $"{ex.Message}");}}/// <summary>/// 侦听客户端/// </summary>public void OnListenClient(){try{while (true){//接受一个客户端的连接请求Socket socket = ServerSocket.Accept();ExecuteMessageChanged?.Invoke(this, $"收到来自【{socket.LocalEndPoint}】远程终端的连接请求...");// 发送消息给客户端string sendTestData = "JoTCP Connect...";ExecuteMessageChanged?.Invoke(this, $"尝试发送数据:{sendTestData}");Send(socket, sendTestData);//创建接收数据线程Thread thread = new Thread(Received);thread.Name = (ClientSocketThreadList.Count + 1) + "";thread.IsBackground = true;thread.Start(socket);//添加对象到集合ClientIpPortList.Add(socket.RemoteEndPoint.ToString());  //添加远程终端到集合ClientSocketList.Add(socket);                                   //添加Socket对现象到集合ClientSocketThreadList.Add(thread);                             //创建对应的客户端Socket线程对象并添加到集合//触发客户端个数变更事件ClientCountChanged?.Invoke("Add", socket.RemoteEndPoint.ToString());}}catch (Exception ex){ExecuteMessageChanged?.Invoke(this, $"侦听异常:{ex.Message}");}}/// <summary>/// 接收数据方法/// </summary>public void Received(object socketClientPara){Socket socketServer = socketClientPara as Socket;string remoteEndPoint = socketServer.RemoteEndPoint.ToString(); ;while (true){try{// 读取客户端发送的数据byte[] buffer = new byte[1024 * 1024];if (socketServer == null) break;// 接收客户端发来的数据int dataLength = socketServer.Receive(buffer);// 将接收的数据转换为字符串并输出//string dataReceived = Encoding.ASCII.GetString(buffer, 0, dataLength);string dataReceived = Encoding.UTF8.GetString(buffer, 0, dataLength).Trim();ExecuteMessageChanged.Invoke(this, "接收数据:");ExecuteMessageChanged.Invoke(this, $"{socketServer.RemoteEndPoint}->{dataReceived}");log("接收数据:" + dataReceived);// 推送数据至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>/// 发送数据:根据指定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, $"发送数据长度:{dataBytes.Length}");}else{ExecuteMessageChanged?.Invoke(this, $"发送失败!socket = null");}}}catch (Exception ex){ExecuteMessageChanged?.Invoke(this, $"发送异常:{ex.Message}");}}/// <summary>/// 发送数据:根据指定Socket对象/// </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, $"发送数据长度:{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, $"发送数据长度:{dataBytes.Length}");}}}catch (Exception ex){ExecuteMessageChanged?.Invoke(this, $"发送异常:{ex.Message}");}}/// <summary>/// 群发数据:发送数据到所有在连接客户端。/// </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, $"发送到终端:{socket.RemoteEndPoint}");ExecuteMessageChanged.Invoke(this, $"协议版本:{socket.RemoteEndPoint.AddressFamily}");ExecuteMessageChanged.Invoke(this, $"发送数据长度:{dataBytes.Length}");}}}catch (Exception ex){ExecuteMessageChanged.Invoke(this, $"发送异常:{ex.Message}");}}/// <summary>/// 推送数据到接口/// </summary>/// <param name="data"></param>public void SendApiData(string apiUrl, string data){try{//调用接口String url = apiUrl + "?data=" + Uri.EscapeDataString(data);log("推送至:" + url);HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);request.Method = "GET"; // 设置请求方法为GETrequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; // 设置接受的内容类型,可选request.ContentType = "application/x-www-form-urlencoded"; // 如果需要发送数据,设置ContentType,此处为GET请求通常不需要设置ContentType,除非有特殊需求request.Headers.Add("Accept-Charset", "UTF-8"); // 指定请求内容应该使用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("推送结果:"+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"; // 日志内容File.AppendAllText(logFilePath, logMessage);} }
}
 
2. 模拟TCP/IP客户端工具类-TcpClient

用于实现TCP客户端模拟测试:

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";           //ipprivate 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>/// 执行(操作)消息变更/// </summary>public event EventHandler<string> ExecuteMessageChanged;#endregion/// <summary>/// 构造函数/// </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{//创建套字节对象(IP4寻址协议,流式连接,TCP协议)ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//设置终端IpPortIPAddress 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>/// 发送/// </summary>public void Send(string data){try{byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(data);//调用客户端套接字发送字节数组// 发送消息到服务器ClientSocket.Send(arrClientSendMsg);ExecuteMessageChanged.Invoke(this, data);}catch (Exception ex){ExecuteMessageChanged.Invoke(this, $"发送失败:{ex.Message}");}}/// <summary>/// 接收/// </summary>public void Received(){try{while (true){//定义一个1M的内存缓冲区 用于临时性存储接收到的信息byte[] arrRecMsg = new byte[1024 * 1024];//将客户端套接字接收到的数据存入内存缓冲区, 并获取其长度int length = ClientSocket.Receive(arrRecMsg);//将套接字获取到的字节数组转换为人可以看懂的字符串string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);//将发送的信息追加到聊天内容文本框中ExecuteMessageChanged.Invoke(this, $"{ClientSocket.RemoteEndPoint}->:{strRecMsg}");}}catch (Exception ex){ExecuteMessageChanged.Invoke(this, $"远程服务器已中断连接...{ex.Message}");}}}
}
3. INI简易配置管理工具类-JoTcp

负责执行config.ini配置文件编辑:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;namespace JoTcp
{// 单层简单ini读取internal class JoIniConfig{private IniFile iniFile;public JoIniConfig(string filename="config.ini"){ //string basePath = System.IO.Directory.GetCurrentDirectory(); // 当前工作目录//string fullPath = System.IO.Path.Combine(basePath, filename); // 构建完整路径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#实现的单层INI配置文件读写工具(无节[Section],仅键值对)/// </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(); // 加载文件内容}/// <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;// 分割键值对(取第一个=作为分隔符)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);}// 写入所有键值对var lines = _keyValues.Select(kv => $"{kv.Key}={kv.Value}").ToList();File.WriteAllLines(_filePath, lines);}/// <summary>/// 读取指定键的值/// </summary>/// <param name="key">键名</param>/// <param name="defaultValue">默认值(键不存在时返回)</param>/// <returns>键对应的值</returns>public string GetValue(string key, string defaultValue = ""){return _keyValues.TryGetValue(key, out string value) ? value : defaultValue;}/// <summary>/// 写入键值对(若键已存在则覆盖)/// </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>/// 获取所有键值对/// </summary>/// <returns>键值对字典</returns>public Dictionary<string, string> GetAllKeys(){return new Dictionary<string, string>(_keyValues); // 返回副本,避免外部修改}}
}
4. 推送服务端窗口模块-Frm_TcpServer
程序服务执行窗口界面,服务核心程序运行界面:
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 窗体:初始化、加载、关闭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 鼠标单击事件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();}//设置显示灯状态if (tcpServerTool.IsListened) picBox_ConnectStatu.BackColor = Color.LimeGreen;else picBox_ConnectStatu.BackColor = Color.Gray;btn_Open.Text = tcpServerTool.IsListened ? "关闭" : "打开";}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>/// 执行(操作)消息/// </summary>private void TcpTool_ExecuteMessageChangedCallback(object sender, string message){MessageShow(message);}/// <summary>/// 客户端数量变更事件:///     参数1:执行模式(Add,Remove)///     参数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("当前设置已保存为默认,下次打开直接可用。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);}// 获取appsettingpublic string configGet(string keyName){//返回配置文件中键为keyName的项的值return ConfigurationManager.AppSettings[keyName];}// 修改、新增、设置 appsettingpublic void configSet(string keyName, string newKeyValue){//修改配置文件中键为keyName的项的值Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);config.AppSettings.Settings[keyName].Value = newKeyValue;config.Save(ConfigurationSaveMode.Modified);ConfigurationManager.RefreshSection("appSettings");}}
}
4. 模拟客户端窗口模块-Frm_TcpClient
模拟客户端执行窗口界面:
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 窗体:初始化、加载、关闭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 鼠标单击事件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>/// 点击服务端按钮,显示服务端/// </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>/// 点击客户端按钮,显示客户端/// </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>/// 窗体加载时,显示服务端/// </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();// 日志目录创建String directoryPath = "log";if (!Directory.Exists(directoryPath)){Directory.CreateDirectory(directoryPath);//Console.WriteLine("目录已创建: " + directoryPath);}else{//Console.WriteLine("目录已存在: " + directoryPath);}}/// <summary>/// 窗体关闭,关闭服务端、客户端/// </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 = "接推服务端";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()方法显示对话框,这会阻止代码继续执行直到对话框关闭}}
}

6. 入库主程序-Program

using System;
using System.Windows.Forms;namespace JoTcp
{internal static class Program{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new MainForm());}}
}

四、关于配置文件

        本程序采用了自定义的INI单层配置方案,在config.ini中直接配置以下参数:

## 配置
ip=127.0.0.1
port=9001
apiurl=https://api.2om.cn/ceshi

    五、使用场景 & 实测效果

    • 物联网设备测试:和 PLC、传感器建立 TCP 连接,实时接收设备数据并推送到云平台,验证设备稳定性。
    • 服务端日志转发:接收 Windows/Linux 服务器的 TCP 日志流,推送到 ELK 等日志分析平台,无需手动采集。
    • .NET 项目数据上报:本地.NET 程序生成的数据通过 TCP 发送,工具自动转发到业务 API,简化项目开发。

            实测效果(.NET 4.7 + Windows 10):

    • 数据处理能力:每秒 150 + 条 TCP 数据,API 推送无阻塞
    • 重连稳定性:断网后 5 秒自动重连,恢复后无缝续传
    • 推送可靠性:API 临时不可用时,重试 3 次后仍失败会记录日志,避免数据丢失

    六、完整代码 & 环境配置

    1. 环境要求
    • .NET Core 3.1 或 .NET 4+ / .NET 5+ /.NET 6+ /.NET 7+
    • 开发工具:Visual Studio 2019+ 或 VS Code
    2. 完整代码获取

    完整代码已上传至 Gitee(包含已编译的发行版exe文件、程序源代码、配置文件等),任何人都可以免费下载使用,请根据需要自行选择编译后的发行版exe文件,或者下载源代码修改编译。

    链接如下:

    JoTcp: 一套用于通过TCP/IP协议获取数据,并通过API接口推送数据的小工具,非常适用于各种物联网设备联调、服务端数据采集转发使用。基于C#编写,免费给所有人合法使用。

    http://www.dtcms.com/a/601240.html

    相关文章:

  1. 经典网站建设案例wordpress讨论区插件
  2. 具身记忆大展拳脚
  3. Java语言编译器 | 深入了解Java编译过程与优化技巧
  4. 实时开发平台(Streampark)--Flink SQL功能演示
  5. 最近做网站开发有前途没wordpress主题特色功能
  6. 深度学习_神经网络初始化参数方法
  7. 程序设计语言编译原理 | 深入解析编译原理与语言设计的关系
  8. MATLAB基于遗传算法的债券投资组合优化
  9. 网站建设背景和目标建设部网站资质查询
  10. Flask 和 Django 的详细对比
  11. 给网站做网络安全的报价全国大学生平面设计大赛
  12. 35搜索插入位置
  13. [AI tradingOS] trader_manager.go | API集中控制_handleStartTrader
  14. LeetCode算法学习之前K个高频元素
  15. 主流 LLM 推理/部署框架指标对比
  16. 大模型上下文窗口详解与 RNN/Transformer 架构比较
  17. C语言编译过程五个步骤 | 深入了解C语言编译的每个环节
  18. seo建站需求海洋cms做电影网站好做吗
  19. 个人网站建设素材广告制作合同范本
  20. 新版 reese84 vmp 补环境逆向分析
  21. 恢复数字序列 od
  22. linux正则匹配
  23. 【1.9】基于FPGA的costas环开发3——解调端低通滤波器模块
  24. C语言编译器及其优化技术
  25. 外国网站建站中国招商网
  26. 自己建的网站也要注册域名吗wordpress云主机
  27. 用 ssldump 跟踪 tls 问题
  28. 《网络安全法》完成修改,AI安全正式“入法”
  29. 算法笔记17 - 贪心算法介绍与思路 | 路灯摆放问题 | 活动安排问题 | 最低字典序拼接 | 金条分割问题 | 项目投资问题
  30. CentOS系统一键安装Memcached教程