Files
huabe-sitondao/tongxin/NetWorkHelper/TCP/ITcpClient.cs
2025-11-14 16:12:32 +08:00

625 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using NetWorkHelper.ICommond;
using NetWorkHelper.IModels;
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace NetWorkHelper.TCP
{
public partial class ITcpClient : Component
{
//#region 读取ini文件
//[DllImport("kernel32")]
//private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
///// <summary>
///// 读出INI文件
///// </summary>
///// <param name="Section">项目名称(如 [TypeName] )</param>
///// <param name="Key">键</param>
//public string IniReadValue(string Section, string Key)
//{
// StringBuilder temp = new StringBuilder(500);
// int i = GetPrivateProfileString(Section, Key, "", temp, 500, System.AppDomain.CurrentDomain.BaseDirectory + "MESConfig.ini");
// return temp.ToString();
//}
static int connecttimeout = 4000;
static int receivetimeout = 2000;
//static int sleeptime;
static int timeoutsend = 3;
//public void ReadINI()
//{
// //string dialog;
// //dialog = System.AppDomain.CurrentDomain.BaseDirectory + "MESConfig.ini";
// //ControlINI mesconfig = new ControlINI(dialog);
// //连接设置
// connecttimeout = Convert.ToInt32(IniReadValue("MESConfig", "connectTimeout")); ;
// receivetimeout = Convert.ToInt32(IniReadValue("MESConfig", "receiveTimeout"));
// _reConnectTime = Convert.ToInt32(IniReadValue("MESConfig", "sleepTime"));
// timeoutsend = Convert.ToInt32(IniReadValue("MESConfig", "timeoutSend"))+1;
//}
//#endregion
#region
public ITcpClient()
{
//ReadINI();
InitializeComponent();
}
public ITcpClient(IContainer container)
{
container.Add(this);
InitializeComponent();
}
#endregion
#region
public IClient Client = null;
private Thread _startThread = null;
private int _reConnectCount = 0;//重连计数
private int _maxConnectCount => timeoutsend;
private ConncetType _conncetType = ConncetType.Conncet;
private bool _isReconnect = true;//是否开启断开重连
private int _reConnectTime;//重连间隔时间
private bool _isStart = false;// 是否启动
private System.Timers.Timer _timer = new System.Timers.Timer(); // 连接后两秒未成功重连
//bool timeoutreconnected=true;//连接超时的重连
//bool datareconnected = true;//接收不到心跳包的重连
#endregion
#region
/// <summary>
/// 服务端IP
/// </summary>
private string _serverip = "127.0.0.1";
[Description("服务端IP")]
[Category("TCP客户端")]
public string ServerIp
{
set { _serverip = value; }
get { return _serverip; }
}
/// <summary>
/// 服务端监听端口
/// </summary>
private int _serverport = 5000;
[Description("服务端监听端口")]
[Category("TCP客户端")]
public int ServerPort
{
set { _serverport = value; }
get { return _serverport; }
}
/// <summary>
/// 网络端点
/// </summary>
private IPEndPoint _ipEndPoint = null;
[Description("网络端点,IP+PORT")]
[Category("TCP客户端")]
internal IPEndPoint IpEndPoint
{
get
{
try
{
IPAddress ipAddress = null;
ipAddress = string.IsNullOrEmpty(ServerIp)
? IPAddress.Any
: IPAddress.Parse(CommonMethod.HostnameToIp(ServerIp));
_ipEndPoint = new IPEndPoint(ipAddress, ServerPort);
}
catch
{
}
return _ipEndPoint;
}
}
/// <summary>
/// 是否重连
/// </summary>
[Description("是否重连")]
[Category("TCP客户端")]
public bool IsReconnection
{
set { _isReconnect = value; }
get { return _isReconnect; }
}
/// <summary>
/// 设置断开重连时间间隔单位毫秒默认3000毫秒
/// </summary>
[Description("设置断开重连时间间隔单位毫秒默认3000毫秒")]
[Category("TCP客户端")]
public int ReConnectionTime
{
get { return _reConnectTime; }
set { _reConnectTime = value; }
}
[Description("设置断开重连时间间隔单位毫秒默认3000毫秒")]
[Category("TCP客户端"), Browsable(false)]
public bool IsStart
{
get { return _isStart; }
set { _isStart = value; }
}
#endregion
#region
private readonly object _threadLock = new object();
public void StartConnect()
{
if (IsStart)
return;
lock (_threadLock)
{
if (_startThread == null || !_startThread.IsAlive)
{
_startThread = new Thread(StartThread);
_startThread.IsBackground = true;
_startThread.Start();
}
}
}
/// <summary>
/// 启动客户端基础的一个线程
/// </summary>
private void StartThread()
{
if (_conncetType == ConncetType.ReConncet && IsReconnection && !IsStart) //如果是重连的延迟N秒
{
Thread.Sleep(ReConnectionTime);
if (IsReconnection)
{
TcpClientStateInfo(string.Format("正在重连..."), SocketState.Reconnection);
}
try
{
_timer.Interval = connecttimeout;
_timer.Elapsed += Timer_Elapsed;
_timer.AutoReset = false;
_timer.Start();
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.ReceiveTimeout = receivetimeout;
socket.SendTimeout = 1000;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
socket.BeginConnect(IpEndPoint, new AsyncCallback(AcceptCallback), socket);
//timeoutreconnected = true;//连接超时的重连
//datareconnected = true;//接收不到心跳包的重连
}
catch (Exception ex)
{
_timer.Stop();
TcpClientErrorMsg(string.Format("连接服务器失败,错误原因:{0},行号{1}", ex.Message, ex.StackTrace));
if (IsReconnection)
{
Reconnect();
}
}
}
else if (!IsStart)
{
if (IsReconnection)
{
TcpClientStateInfo("正在连接服务器... ...", SocketState.Connecting);
try
{
_timer.Interval = connecttimeout;
_timer.Elapsed += Timer_Elapsed;
_timer.AutoReset = false;
_timer.Start();
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.ReceiveTimeout = receivetimeout;
socket.SendTimeout = 1000;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
socket.BeginConnect(IpEndPoint, new AsyncCallback(AcceptCallback), socket);
//timeoutreconnected = true;//连接超时的重连
//datareconnected = true;//接收不到心跳包的重连
}
catch (Exception ex)
{
_timer.Stop();
TcpClientErrorMsg(string.Format("连接服务器失败,错误原因:{0},行号{1}", ex.Message, ex.StackTrace));
if (IsReconnection)
{
Reconnect();
}
}
}
}
}
//连接超时则重新连接
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (IsReconnection)
{
//datareconnected = false;//接收不到心跳包的重连
TcpClientErrorMsg(string.Format("连接服务器失败"));
//if (Client != null)
//{
// ShutdownClient(Client);
// Client.WorkSocket.Close();
//}
Reconnect();
//reconnected = false;
}
}
/// <summary>
/// 当连接服务器之后的回调函数
/// </summary>
/// <param name="ar">TcpClient</param>
private void AcceptCallback(IAsyncResult ar)
{
_timer.Stop();
try
{
IsStart = true;
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
Client = new IClient(socket);
Client.WorkSocket.BeginReceive(Client.BufferInfo.ReceivedBuffer, 0, Client.BufferInfo.ReceivedBuffer.Length, 0, new AsyncCallback(ReadCallback), Client);
_conncetType = ConncetType.Conncet;
TcpClientStateInfo(string.Format("已连接服务器"), SocketState.Connected);
_reConnectCount = 0;
//timeoutreconnected = false;//连接超时的重连
//datareconnected = false;//接收不到心跳包的重连
}
catch (SocketException ex)
{
IsStart = false;
string msg = ex.Message;
if (ex.NativeErrorCode.Equals(10060))
{
//无法连接目标主机
msg = string.Format("{0} 无法连接: error code {1}!", "", ex.NativeErrorCode);
}
else if (ex.NativeErrorCode.Equals(10061))
{
msg = string.Format("{0} 主动拒绝正在重连: error code {1}!", "", ex.NativeErrorCode);
}
else if (ex.NativeErrorCode.Equals(10053))
{
//读写时主机断开
msg = string.Format("{0} 主动断开连接: error code {1}! ", "", ex.NativeErrorCode);
}
else
{
//其他错误
msg = string.Format("Disconnected: error code {0}!", ex.NativeErrorCode);
}
if (IsReconnection)
{
TcpClientErrorMsg(string.Format("连接服务器失败,错误原因:{0}", msg));
Reconnect();
}
}
catch (Exception ex)
{
}
}
#endregion
#region
/// <summary>
/// 重连模块
/// </summary>
private void Reconnect()
{
if (Client != null)
{
ShutdownClient(Client);
Client?.WorkSocket?.Close();
Client = null;
}
if (_conncetType == ConncetType.Conncet)
{
TcpClientStateInfo(string.Format("已断开服务器{0}", IsReconnection ? ",准备重连" : ""), SocketState.Disconnect);
}
if (!IsReconnection)
{
return;
}
_reConnectCount++;//每重连一次重连的次数加1
if (_conncetType == ConncetType.Conncet)
{
_conncetType = ConncetType.ReConncet;
//CommonMethod.EventInvoket(() => { ReconnectionStart(); });
}
_isStart = false;
if (_startThread != null)
{
try
{
// 检查线程状态,避免重复启动
if (_startThread.IsAlive)
{
// 给线程一个机会正常结束
if (!_startThread.Join(500)) // 等待500毫秒
{
try
{
_startThread.Abort(); // 强制终止
}
catch (ThreadStateException)
{
// 线程可能已经终止,忽略此异常
}
}
}
}
catch (Exception ex)
{
// 记录异常但不阻止重连
Console.WriteLine($"清理线程时异常: {ex.Message}");
}
finally
{
_startThread = null; // 确保设置为null
}
}
if (_reConnectCount < _maxConnectCount && IsReconnection)
{
StartConnect();
}
else
{
_timer.Stop();
StopConnect();
this.IsReconnection = false;
_reConnectCount = 0;
TcpClientStateInfo(string.Format("超过最大重连数,已断开服务器连接"), SocketState.Disconnect);
}
}
#endregion
#region
public void SendData(byte[] data)
{
try
{
if (data == null || data.Length == 0)
{
return;
}
if (Client != null && Client.WorkSocket != null && Client.WorkSocket.Connected)
{
//异步发送数据
//cModel.ClientSocket.Send(data);
Client.WorkSocket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), Client);
//timeoutreconnected = false;//连接超时的重连
//datareconnected = true;//接收不到心跳包的重连
}
}
catch (SocketException ex)
{
TcpClientErrorMsg(string.Format("向服务端发送数据时发生错误,错误原因:{0},行号{1}", ex.Message, ex.StackTrace));
}
}
public bool IsConnected
{
get
{
try
{
return Client != null &&
Client.WorkSocket != null &&
Client.WorkSocket.Connected;
}
catch
{
return false;
}
}
}
/// <summary>
/// 发送完数据之后的回调函数
/// </summary>
/// <param name="ar">Clicent</param>
private void SendCallback(IAsyncResult ar)
{
IClient iClient = (IClient)ar.AsyncState;
if (iClient == null)
return;
Socket handler = iClient.WorkSocket;
try
{
int bytesSent = handler.EndSend(ar);
}
catch (Exception ex)
{
TcpClientErrorMsg(string.Format("发送数据后回调时发生错误,错误原因:{0},行号{1}", ex.Message, ex.StackTrace));
}
}
#endregion
#region
/// <summary>
/// 当接收到数据之后的回调函数
/// </summary>
/// <param name="ar"></param>
private void ReadCallback(IAsyncResult ar)
{
if (Client == null || !_isStart)
return;
Socket handler = Client.WorkSocket;
try
{
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0 && bytesRead <= Client.BufferInfo.ReceivedBuffer.Length)
{
byte[] bytes = new byte[bytesRead];
Array.Copy(Client.BufferInfo.ReceivedBuffer, 0, bytes, 0, bytesRead);
TcpClientRecevice(bytes);
handler.BeginReceive(Client.BufferInfo.ReceivedBuffer, 0, Client.BufferInfo.ReceivedBuffer.Length,
0, new AsyncCallback(ReadCallback), Client);
}
else
{
if (IsReconnection)
{
Reconnect();
}
}
}
catch (Exception ex)
{
//if (datareconnected)
//{
// timeoutreconnected = false;//连接超时的重连
////IsStart = false;
StopConnect();
TcpClientErrorMsg(string.Format("接收数据失败,错误原因:{0},行号{1}", ex.Message, ex.StackTrace));
Reconnect();
//}
}
}
#endregion
#region
/// <summary>
/// 关闭相连的scoket以及关联的StateObject,释放所有的资源
/// </summary>
public void StopConnect()
{
IsStart = false;
if (Client != null)
{
var workSocket = Client.WorkSocket; // 提前保存引用
ShutdownClient(Client);
workSocket?.Close();
//if (Client.WorkSocket != null)
//{
// Client.WorkSocket.Close();
// //Client.WorkSocket?.Dispose();
//}
}
_conncetType = ConncetType.Conncet;
_reConnectCount = 0;//前面三个初始化
}
public void ShutdownClient(IClient iClient)
{
try
{
iClient.WorkSocket.Shutdown(SocketShutdown.Both);
}
catch
{
}
}
#endregion
#region
#region OnRecevice接收数据事件
[Description("接收数据事件")]
[Category("TcpClient事件")]
public event EventHandler<TcpClientReceviceEventArgs> OnRecevice;
protected virtual void TcpClientRecevice(byte[] data)
{
if (OnRecevice != null)
CommonMethod.EventInvoket(() => { OnRecevice(this, new TcpClientReceviceEventArgs(data)); });
}
#endregion
#region OnErrorMsg返回错误消息事件
[Description("返回错误消息事件")]
[Category("TcpClient事件")]
public event EventHandler<TcpClientErrorEventArgs> OnErrorMsg;
protected virtual void TcpClientErrorMsg(string msg)
{
if (OnErrorMsg != null)
CommonMethod.EventInvoket(() => { OnErrorMsg(this, new TcpClientErrorEventArgs(msg)); });
}
#endregion
#region OnStateInfo连接状态改变时返回连接状态事件
[Description("连接状态改变时返回连接状态事件")]
[Category("TcpClient事件")]
public event EventHandler<TcpClientStateEventArgs> OnStateInfo;
protected virtual void TcpClientStateInfo(string msg, SocketState state)
{
if (OnStateInfo != null)
CommonMethod.EventInvoket(() => { OnStateInfo(this, new TcpClientStateEventArgs(msg, state)); });
}
#endregion
#endregion
}
public enum ConncetType
{
Conncet,
ReConncet,
DisConncet
}
}