这一切都有效.现在我想添加外部功能,以便在计时器上“重置”此类(强制每次重新连接)以保持连接活动.我的想法是可以根据需要多次调用Init方法来重置套接字.但是,我确实得到了各种例外.
班级代码:
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,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,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,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,state.Buffer.Length,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,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().