C# 具有连接丢失检测和自动重新连接的TCP异步客户端
这是我多年来多次修改的TCP异步客户端代码。 它能够检测连接丢失(由于使用了保持活动的值) 但现在,我需要在检测到任何连接丢失(任何通信错误或C# 具有连接丢失检测和自动重新连接的TCP异步客户端,c#,asynchronous,sockets,tcp,client,C#,Asynchronous,Sockets,Tcp,Client,这是我多年来多次修改的TCP异步客户端代码。 它能够检测连接丢失(由于使用了保持活动的值) 但现在,我需要在检测到任何连接丢失(任何通信错误或Disconnect()call)后自动将其重新连接到服务器 它适用于一些简单的SW TCP服务器,当我停止它们侦听或断开客户端与它们的连接时。但是,当我连接到一些真实的设备并开始模拟可能的错误时,问题就开始了 例如,我断开客户端PC与网络的连接,然后在“重新连接”一段时间后,应用程序进入连接状态循环并挂起,具体地说,方法OnDataReceived抛出S
Disconnect()
call)后自动将其重新连接到服务器
它适用于一些简单的SW TCP服务器,当我停止它们侦听或断开客户端与它们的连接时。但是,当我连接到一些真实的设备并开始模拟可能的错误时,问题就开始了
例如,我断开客户端PC与网络的连接,然后在“重新连接”一段时间后,应用程序进入连接状态循环并挂起,具体地说,方法OnDataReceived
抛出SocketException:ConnectionReset
oniRx=stateObject.Socket.EndReceive(asyn)代码>呼叫
由于我不擅长异步代码,我相信我做了一些非常糟糕的事情
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace SharpLib.Tcp
{
public enum ConnectionState
{
Connected, Disconnected
};
public delegate void ConnectionStateChangedEventHandler(object sender, ConnectionStateEventArgs args);
public class ConnectionStateEventArgs : EventArgs
{
public ConnectionState ConnectionState { get; set; }
public ConnectionStateEventArgs(ConnectionState state)
{
this.ConnectionState = state;
}
}
/// <summary>
/// Structure of received data
/// </summary>
public class StateObject
{
public byte[] DataBuffer { get; set; }
public Socket Socket { get; set; }
public StateObject(Socket socket)
{
this.DataBuffer = new byte[128];
this.Socket = socket;
}
}
/// <summary>
/// TCP client with asynchronous connecting and data receiving
/// </summary>
public class TcpAsyncClient
{
protected string address;
protected int port;
private Socket socket;
private int keepAliveTime;
private int keepAliveInterval;
private int connectTimeout;
private bool autoReconnect;
private AsyncCallback callback;
private static ManualResetEvent connectDone = new ManualResetEvent(false);
public event MessageEventHandler DataReceived = delegate { };
public event ExceptionEventHandler ExceptionCaught = delegate { };
public event ConnectionStateChangedEventHandler ConnectionStateChanged = delegate { };
public bool Connected
{
get
{
if (socket == null)
return false;
return socket.Connected;
}
}
public TcpAsyncClient(string address, int port, int keepAliveTime = 1000, int keepAliveInterval = 1000, int connectTimeout = 1000, bool autoReconnect = false)
{
this.address = address;
this.port = port;
this.keepAliveInterval = keepAliveInterval;
this.keepAliveTime = keepAliveTime;
this.connectTimeout = connectTimeout;
this.autoReconnect = autoReconnect;
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
}
/// <summary>
/// Connect to tcp server - async
/// </summary>
public void Connect()
{
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEnd = FindIpEndPoint(address, port);
socket.BeginConnect(ipEnd, new AsyncCallback(ConnectCallback), new StateObject(socket));
connectDone.WaitOne(500);
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Connect done callback
/// </summary>
/// <param name="ar"></param>
private void ConnectCallback(IAsyncResult ar)
{
try
{
// Complete the connection.
((StateObject)ar.AsyncState).Socket.EndConnect(ar);
// Signal that the connection has been made.
connectDone.Set();
WaitForData();
SetKeepAlive(true, Convert.ToUInt32(keepAliveTime), Convert.ToUInt32(keepAliveInterval));
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Connected));
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Disconnect from tcp server
/// </summary>
public void Disconnect()
{
try
{
// MSDN recommends to Shutdown() before Disconnect()
socket.Shutdown(SocketShutdown.Both);
socket.Disconnect(true);
}
catch { }
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
if (autoReconnect)
{
Connect();
}
}
/// <summary>
/// Send string message to tcp server
/// </summary>
/// <param name="message"></param>
public void Send(string message)
{
// because of this, we can Send from client imidiately after Connect() call
DateTime start = DateTime.Now;
if (!Connected)
{
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
return;
}
// make return on the end of line
message += "\r";
int sent = 0; // how many bytes is already sent
do
{
try
{
sent += socket.Send(System.Text.Encoding.UTF8.GetBytes(message), sent, message.Length - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
{
OnError(ex);
break;
}
}
}
while (sent < message.Length);
}
/// <summary>
/// Start receiving data from tcp server
/// </summary>
public void WaitForData()
{
try
{
StateObject stateObject = new StateObject(socket);
IAsyncResult result = socket.BeginReceive(stateObject.DataBuffer, 0, 128, SocketFlags.None, new AsyncCallback(OnDataReceived), stateObject);
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Data received callback
/// </summary>
/// <param name="asyn"></param>
public void OnDataReceived(IAsyncResult asyn)
{
try
{
StateObject stateObject = (StateObject)asyn.AsyncState;
if (!stateObject.Socket.Connected)
return;
int iRx = stateObject.Socket.EndReceive(asyn);
// Server probably stopped listening
if (iRx == 0)
{
Disconnect();
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
return;
}
char[] chars = new char[iRx];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(stateObject.DataBuffer, 0, iRx, chars, 0);
string szData = new string(chars);
DataReceived(this, new MessageEventArgs(szData));
WaitForData();
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Socket exception during connecting or communication with server
/// </summary>
/// <param name="ex"></param>
private void OnError(Exception ex)
{
ExceptionCaught(this, new ExceptionEventArgs(ex));
Disconnect();
}
/// <summary>
/// Set KeepAlive timer for socket
/// </summary>
/// <param name="on"></param>
/// <param name="time"></param>
/// <param name="interval"></param>
private void SetKeepAlive(bool on, uint time, uint interval)
{
int size = Marshal.SizeOf(new uint());
var inOptionValues = new byte[size * 3];
BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size);
BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2);
socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}
/// <summary>
/// Create ip address from known host name
/// </summary>
/// <param name="hostName"></param>
/// <param name="port"></param>
/// <returns></returns>
private IPEndPoint FindIpEndPoint(string hostName, int port)
{
var addresses = System.Net.Dns.GetHostAddresses(hostName);
if (addresses.Length == 0)
{
throw new ArgumentException(
"Unable to retrieve address from specified host name.",
"hostName"
);
}
return new IPEndPoint(addresses[0], port);
}
}
}
使用系统;
Net系统;
使用System.Net.Sockets;
使用系统线程;
使用System.Runtime.InteropServices;
使用System.Threading.Tasks;
名称空间SharpLib.Tcp
{
公共枚举连接状态
{
连接的,断开的
};
公共委托无效ConnectionStateChangedEventHandler(对象发送方,ConnectionStateEventArgs args args);
公共类ConnectionStateEventArgs:EventArgs
{
公共连接状态连接状态{get;set;}
公共连接状态事件参数(连接状态状态)
{
this.ConnectionState=状态;
}
}
///
///接收数据的结构
///
公共类状态对象
{
公共字节[]数据缓冲{get;set;}
公共套接字{get;set;}
公共状态对象(套接字)
{
this.DataBuffer=新字节[128];
这个.Socket=Socket;
}
}
///
///具有异步连接和数据接收的TCP客户端
///
公共类TcpAsyncClient
{
受保护的字符串地址;
受保护的int端口;
专用插座;
私有int keepAliveTime;
私密的内部信息;
私有int连接超时;
私人楼宇自动接驳;
私有异步回调;
专用静态手动复位事件连接完成=新手动复位事件(错误);
public event MessageEventHandler DataReceived=委托{};
公共事件例外venthandler例外caught=委托{};
公共事件连接stateChangedEventHandler连接stateChanged=委托{};
公共广播连接
{
得到
{
if(套接字==null)
返回false;
返回插座。已连接;
}
}
公共TcpAsyncClient(字符串地址,int端口,int keepAliveTime=1000,int keepAliveInterval=1000,int connectTimeout=1000,bool autoReconnect=false)
{
this.address=地址;
this.port=端口;
this.keepAliveInterval=keepAliveInterval;
this.keepAliveTime=keepAliveTime;
this.connectTimeout=connectTimeout;
this.autoReconnect=autoReconnect;
ConnectionStateChanged(这是新的ConnectionStateEventArgs(ConnectionState.Disconnected));
}
///
///连接到tcp服务器-异步
///
公共void Connect()
{
尝试
{
套接字=新套接字(AddressFamily.InterNetwork、SocketType.Stream、ProtocolType.Tcp);
IPEndPoint ipEnd=FindIpEndPoint(地址、端口);
BeginConnect(ipEnd,新的异步回调(ConnectCallback),新的StateObject(socket));
韦通(500);
}
捕获(SocketException例外)
{
OnError(ex);
}
}
///
///连接完成回调
///
///
私有无效连接回调(IAsyncResult ar)
{
尝试
{
//完成连接。
((StateObject)ar.AsyncState).Socket.EndConnect(ar);
//表示已建立连接的信号。
connectDone.Set();
WaitForData();
SetKeepAlive(true,Convert.ToUInt32(keepAliveTime),Convert.ToUInt32(keepAliveInterval));
ConnectionStateChanged(这是新的ConnectionStateEventArgs(ConnectionState.Connected));
}
捕获(SocketException例外)
{
OnError(ex);
}
}
///
///断开与tcp服务器的连接
///
公共空间断开连接()
{
尝试
{
//MSDN建议在断开()之前关闭()
socket.Shutdown(SocketShutdown.Both);
插座。断开(正确);
}
捕获{}
ConnectionStateChanged(这是新的ConnectionStateEventArgs(ConnectionState.Disconnected));
如果(自动重新连接)
{
Connect();
}
}
///
///向tcp服务器发送字符串消息
///
///
公共无效发送(字符串消息)
{
//因此,我们可以在调用Connect()后立即从客户端发送
DateTime start=DateTime.Now;
如果(!已连接)
{
ConnectionStateChanged(这是新的ConnectionStateEventArgs(ConnectionState.Disconnected));
返回;
}
//在行尾返回
消息+=“\r”;
int sent=0;//已发送多少字节
做
{
尝试
{
sent+=socket.Send(System.Text.Encoding.UTF8.GetBytes(消息)