c# - C# 异步 TCP 客户端在服务器断开连接后进入无限循环
问题描述
我的代码有问题。很容易,我在这里写了一个问题。
我编写了一个新类来拥有一个异步 TCP 客户端。我的问题很简单,在服务器断开连接时,接收回调在无限循环中调用,没有读取任何字节。项目的 RAM 是指数级的。这对我来说太不可思议了,我没有一点线索来解决这个问题..我尝试了很多解决方案来解决这个问题,但没有成功......
我是代码新手..
public class TcpClientAsync : IDisposable
{
////////////////////////////////////////////////////////////////
/// Constant
#region Constant
private const string SOURCE = "TcpClient";
#endregion
/////////////////////////////////////////////////////////////////////
/// Constructor
#region Constructor
public TcpClientAsync()
{
mpr_timeoutConnection.Elapsed += Mpr_timeoutConnection_Elapsed;
mpr_timeoutConnection.AutoReset = false;
}
~TcpClientAsync()
{
StopAndClean();
}
#endregion
/////////////////////////////////////////////////////////////////////
/// Private Member
#region Private Member
private IPAddress mpr_remoteIp = IPAddress.Any;
private IPEndPoint mpr_remoteEndPoint;
private Socket mpr_clientSocket;
private Timer mpr_timeoutConnection = new Timer(5000);
private bool mpr_checkFrameSize = false;
#endregion
/////////////////////////////////////////////////////////////////////
/// Protected Member
#region Protected Member
#endregion
/////////////////////////////////////////////////////////////////////
/// Public Member
#region Public Member
#endregion
/////////////////////////////////////////////////////////////////////
/// EventHandler
#region EventHandler
public event EventHandler<Frame> OnNewDataReceived = delegate { };
public event EventHandler<TcpClientState> OnNewState = delegate { };
public event EventHandler<Event> OnNewEvent = delegate { };
public event EventHandler<EndPoint> OnBeginConnect = delegate { };
public event EventHandler<EndPoint> OnConnect = delegate { };
public event EventHandler OnDisconnect = delegate { };
public event EventHandler<Frame> OnNewDataSent = delegate { };
#endregion
/////////////////////////////////////////////////////////////////////
/// Properties
#region Properties
public double ConnectionTimeout
{
get
{
return mpr_timeoutConnection.Interval;
}
set
{
mpr_timeoutConnection.Interval = value;
}
}
public bool CheckFrameSize
{
get { return mpr_checkFrameSize; }
set
{
mpr_checkFrameSize = value;
}
}
#endregion
////////////////////////////////////////////////////////////////////
/// Event Callback
#region EventCallback
private void Mpr_timeoutConnection_Elapsed(object sender, ElapsedEventArgs e)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Timeout for connection", "Timeout for connect remote tcp server."));
StopAndClean();
mpr_timeoutConnection.Enabled = false;
}
#endregion
////////////////////////////////////////////////////////////////////
// Private Method
#region Private Method
private void StartService(int port = 2401, string remoteAddress = "0.0.0.0")
{
try
{
try
{
if (!IPAddress.TryParse(remoteAddress, out mpr_remoteIp))
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Warning, "Fail to parse ip address", "Try to retrieve by hostname"));
IPHostEntry ipHostInfo = Dns.GetHostEntry(remoteAddress);
if (ipHostInfo == null)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error for retrieve remote end point", ""));
StopAndClean();
return;
}
mpr_remoteIp = ipHostInfo.AddressList[0];
}
mpr_remoteEndPoint = new IPEndPoint(mpr_remoteIp, port);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error for retrieve remote endpoint", ex.Message, ex));
StopAndClean();
return;
}
try
{
// Create a TCP/IP socket.
mpr_clientSocket = new Socket(mpr_remoteIp.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
try
{
// Connect to the remote endpoint.
mpr_clientSocket.BeginConnect(mpr_remoteEndPoint,
new AsyncCallback(ConnectCallback), mpr_clientSocket);
OnBeginConnect?.Invoke(this, mpr_remoteEndPoint);
OnNewState?.Invoke(this, TcpClientState.ConnectionInProgress);
if (mpr_timeoutConnection.Enabled)
mpr_timeoutConnection.Enabled = false;
mpr_timeoutConnection.Enabled = true;
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error to launch connection", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error to create or launch socket connection", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to start tcp client", ex.Message, ex));
StopAndClean();
return;
}
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
try
{
// Set new state
mpr_timeoutConnection.Enabled = false;
OnNewState?.Invoke(this, TcpClientState.Connected);
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Event, "Remote partner connected : " + client.RemoteEndPoint.ToString(), ""));
OnConnect?.Invoke(this, client.RemoteEndPoint);
// Make receive callback
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
mpr_clientSocket.BeginReceive(state.frame.FrameBytes, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to create receive callback", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to update the state", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to stop connection state", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to open the connection with server", ex.Message, ex));
StopAndClean();
return;
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
try
{
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(
state.frame.FrameBytes, 0, bytesRead));
if (bytesRead != StateObject.BufferSize && mpr_checkFrameSize)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Incomplete frame received from component, frame size expected : " + StateObject.BufferSize.ToString(), ""));
OnNewDataReceived?.Invoke(this, state.frame);
StopAndClean();
return;
}
}
else
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Warning, "Empty frame received from component", ""));
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to encode received frame", ex.Message, ex));
StopAndClean();
return;
}
try
{
OnNewDataReceived?.Invoke(this, state.frame);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to share received data", ex.Message, ex));
StopAndClean();
return;
}
try
{
// Create new callback for a new incoming frame
StateObject newState = new StateObject();
newState.workSocket = client;
client.BeginReceive(newState.frame.FrameBytes, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), newState);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to create receive callback", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to close socket for read buffer", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to receive data from server", ex.Message, ex));
StopAndClean();
return;
}
}
private void Send(Socket client, Frame data)
{
if (!mpr_clientSocket.Connected)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Warning, "Fail to send frame, not connected", ""));
return;
}
try
{
// Create state object
StateObject state = new StateObject();
state.workSocket = client;
// Convert the string data to byte data using ASCII encoding.
state.frame = data;
try
{
// Begin sending the data to the remote device.
client.BeginSend(state.frame.FrameBytes, 0, state.frame.Lenght, 0,
new AsyncCallback(SendCallback), state);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error for create the send callback", ex.Message, ex));
StopAndClean();
return;
}
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Fail to create the send callback", ex.Message, ex));
StopAndClean();
return;
}
}
private void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
// Update hex dump
OnNewDataSent?.Invoke(this, state.frame);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error in send callback", ex.Message, ex));
StopAndClean();
return;
}
}
private void StopAndClean()
{
if (mpr_clientSocket != null)
{
if (mpr_clientSocket.Connected)
{
OnDisconnect?.Invoke(this, EventArgs.Empty);
mpr_clientSocket.Shutdown(SocketShutdown.Both);
}
mpr_clientSocket.Close();
mpr_clientSocket.Dispose();
}
OnNewState?.Invoke(this, TcpClientState.Disconnect);
mpr_timeoutConnection.Enabled = false;
}
#endregion
////////////////////////////////////////////////////////////////////
// Protected Method
#region Protected Method
#endregion
////////////////////////////////////////////////////////////////////
// Public Method
#region Public Method
public void Start(int port = 2401, string ipAddress = "0.0.0.0")
{
try
{
StartService(port, ipAddress);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error to start the client", ex.Message, ex));
StopAndClean();
return;
}
}
public void Stop()
{
try
{
StopAndClean();
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error to stop the client", ex.Message, ex));
StopAndClean();
return;
}
}
public void SendFrame(Frame frame)
{
try
{
Send(mpr_clientSocket, frame);
}
catch (Exception ex)
{
OnNewEvent?.Invoke(this, new Event(SOURCE, EventType.Error, "Error to send frame", ex.Message, ex));
StopAndClean();
return;
}
}
public void Dispose()
{
StopAndClean();
}
#endregion
}
解决方案
推荐阅读
- identityserver4 - IdentityServer4:快速入门总是返回未授权的 API 端点,并显示消息“观众 'https://localhost:5001/resources' 无效”
- ruby-on-rails - 带有 STI 的 PaperTrail 似乎存储了错误的 item_type
- python - 当 urllib 无法下载时,QMediaContent 如何能够显示视频?
- r - R - 按2个变量分组并将最高值设置为新列?
- javascript - 从 jquery 加载页面中将数据保存到 MySQL
- php - 如何发送 XML (epp) 请求然后接收数据?(PHP)
- python - 从图像中提取特定文本
- python-3.x - Tkinter 滚动条不会控制入口小部件
- javascript - 在会话存储中使用数组保存表单数据
- c++ - 为什么在使用花括号初始化结构时出现错误?