微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 调用EndReceive和BeginReceive时的TcpClient异常

我正在尝试实现包装器类,它只需连接到TCP服务器并等待数据.一旦从服务器提交数据 – 我将收到这些数据并将其传递给我班级的订阅者.

这一切都有效.现在我想添加外部功能,以便在计时器上“重置”此类(强制每次重新连接)以保持连接活动.我的想法是可以根据需要多次调用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().

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐