gpt4 book ai didi

c# - 调用 EndReceive 和 BeginReceive 时出现 TcpClient 异常

转载 作者:行者123 更新时间:2023-11-30 12:38:51 26 4
gpt4 key购买 nike

我正在尝试实现包装器类,它将简单地连接到 TCP 服务器并等待数据。一旦数据从服务器提交 - 我将收到此数据并将其传递给我类(class)的订阅者。

这一切都有效。现在我想添加外部功能以在计时器上“重置”此类(每隔一段时间强制重新连接)以保持连接有效。我的想法是,可以根据需要多次调用 Init 方法来重置套接字。但是,我确实遇到了各种异常(exception)情况。

类代码:

namespace Ditat.GateControl.Service.InputListener
{
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class BaseTCPSocketListener : IInputListener
{
#region Events/Properties

public event EventHandler<Exception> OnError;

public event EventHandler<string> OnDataReceived;

private string host;

private int port;

private int delayToClearBufferSeconds = 5;

private TcpClient client;

private readonly byte[] buffer = new byte[1024];

/// <summary>
/// Will accumulate data as it's received
/// </summary>
private string DataBuffer { get; set; }

/// <summary>
/// Store time of last data receipt. Need this in order to purge data after delay
/// </summary>
private DateTime LastDataReceivedOn { get; set; }

#endregion

public BaseTCPSocketListener()
{
// Preset all entries
this.LastDataReceivedOn = DateTime.UtcNow;
this.DataBuffer = string.Empty;

}

public void Init(string config)
{
// Parse info
var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
this.host = bits[0];
var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
this.port = int.Parse(bits[1]);
this.delayToClearBufferSeconds = int.Parse(bits[2]);

// Close open client
if (this.client?.Client != null)
{
this.client.Client.Disconnect(true);
this.client = null;
}

// Connect to client
this.client = new TcpClient();
if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");

this.EstablishReceiver();
}

protected void DataReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;
try
{
bytesCount = this.client.Client.EndReceive(result);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}

// No data received, establish receiver and return
if (bytesCount == 0)
{
this.EstablishReceiver();
return;
}

// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(this.buffer, 0, bytesCount);

// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);

this.DataBuffer = string.Empty;
this.EstablishReceiver();
}

private void EstablishReceiver()
{
try
{
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, this.DataReceived, this.buffer);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}

private void RaiseOnErrorToClient(Exception ex)
{
if (this.OnError == null) return;

foreach (Delegate d in this.OnError.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, ex);
}
else
{
syncer.BeginInvoke(d, new object[] { this, ex });
}
}
}

private void RaiseOnDataReceivedToClient(string data)
{
if (this.OnDataReceived == null) return;

foreach (Delegate d in this.OnDataReceived.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, data);
}
else
{
syncer.BeginInvoke(d, new object[] { this, data });
}
}
}
}
}

客户端代码(在按钮下点击表单)

private void ListenBaseButton_Click(object sender, EventArgs e)
{
if (this.bsl == null)
{
this.bsl = new BaseTCPSocketListener();
this.bsl.OnDataReceived += delegate (object o, string s)
{
this.DataTextBox.Text += $"Base: {DateTime.Now} - {s}" + Environment.NewLine;
};

this.bsl.OnError += delegate (object o, Exception x)
{
this.DataTextBox.Text += $"Base TCP receiver error: {DateTime.Now} - {x.Message}" + Environment.NewLine;
};
}

try
{
this.bsl.Init("192.168.33.70|10001|10");
this.DataTextBox.Text += "BEGIN RECEIVING BSL data --------------------------" + Environment.NewLine;
}
catch (Exception exception)
{
this.DataTextBox.Text += $"ERROR CONNECTING TO BSL ------------{exception.Message}" + Environment.NewLine;
}
}

我得到的异常。在 DataReceived

中的处理程序中第二次单击按钮时的第一个异常

The IAsyncResult object was not returned from the corresponding asynchronous method on this class.

在点击之后,我从 EstablishReceiver 中的处理程序中得到异常

A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied

如何正确确保套接字关闭和重新打开?

最佳答案

The IAsyncResult object was not returned from the corresponding asynchronous method on this class.

这是一个众所周知的问题,发生在为先前的套接字调用数据回调 (DataReceived()) 时。在这种情况下,您将使用不正确的 IAsyncResult 实例调用 Socket.EndReceive(),这会引发上述异常。

Asynchronous Client Socket Example包含此问题的可能解决方法:将调用 BeginReceive() 的套接字存储在状态对象中,然后将其传递给 DataReceived 回调:

StateObject 类

public class StateObject
{
public Socket Socket { get; set; }

public byte[] Buffer { get; } = new byte[1024];

public StateObject(Socket socket)
{
Socket = socket;
}
}

EstablishReceiver() 方法:

private void EstablishReceiver()
{
try
{
var state = new StateObject(client.Client);
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, this.DataReceived, state);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}

DataReceived() 方法:

protected void DataReceived(IAsyncResult result)
{
var state = (StateObject) result.AsyncState;

// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;

try
{
SocketError errorCode;
bytesCount = state.Socket.EndReceive(result, out errorCode);
if (errorCode != SocketError.Success)
{
bytesCount = 0;
}
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}

if (bytesCount > 0)
{
// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(state.Buffer, 0, bytesCount);

// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);

this.DataBuffer = string.Empty;
this.EstablishReceiver();
}
}

A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied

上面的 DataReceived() 方法还包含对第二个异常的修复。异常是由在断开连接的套接字上调用 BeginReceive()(从 EstablishReceiver())引起的。如果之前的读取带来了 0 个字节,则不应在套接字上调用 BeginReceive()

关于c# - 调用 EndReceive 和 BeginReceive 时出现 TcpClient 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49244274/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com