首页 文章

SL4 AsyncEventArgs在第二个Socket连接之后抛出InvalidOperationException

提问于
浏览
1

我有一个类,它必须使用Silverlight 4中的套接字发送和接收数据 . 它必须实现一个预先存在的接口,所以有些东西可能看起来有些奇怪,但这里是:

public class TcpDataTransportClient : IDataTransportService
{
    private const string TCP_ADDRESS_SETTING = "tcpaddress";
    private const string TCP_PORT_SETTING = "tcpport";

    private static ManualResetEvent clientConnected = new ManualResetEvent(false);
    private static ManualResetEvent clientDataReceived = new ManualResetEvent(false);
    private static ManualResetEvent clientDataSent = new ManualResetEvent(false);

    private Dictionary<string, object> settings = new Dictionary<string, object>();
    private IDataEncapsulator dataEncapsulator;
    private IDataCollector dataCollector;

    private Socket client;
    private SocketAsyncEventArgs clientArgs;

    public event DataReceivedHandler OnDataReceived;
    public event DataSentHandler OnDataSent;

    public TcpDataTransportClient()
    {

    }

    public Dictionary<string, object> Settings
    {
        get
        {
            return this.settings;
        }
        set
        {
            this.settings = value;
        }
    }

    public IDataEncapsulator DataEncapsulator
    {
        get
        {
            return this.dataEncapsulator;
        }
        set
        {
            this.dataEncapsulator = value;
        }
    }

    public void Start(IDataCollector dataCollector)
    {
        this.dataCollector = dataCollector;
        clientArgs = new SocketAsyncEventArgs();

        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        clientArgs.Completed += clientArgs_Completed;
        clientArgs.UserToken = client;            
        clientArgs.RemoteEndPoint = GetIPEndPoint();

        client.ConnectAsync(clientArgs);
        clientConnected.WaitOne();          
    }

    private IPEndPoint GetIPEndPoint()
    {
        IPAddress ipAddress;
        int tcpPort;

        if (!IPAddress.TryParse(settings[TCP_ADDRESS_SETTING].ToString(), out ipAddress))
            throw new ArgumentException(String.Format("Invalid setting for IP Address: '{0}'", TCP_ADDRESS_SETTING));

        if (!int.TryParse(settings[TCP_PORT_SETTING].ToString(), out tcpPort))
            throw new ArgumentException(String.Format("Invalid setting for TCP Port: '{0}'", TCP_PORT_SETTING));

        return new IPEndPoint(ipAddress, tcpPort);
    }

    void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Connect:
                ProcessConnect(e);
                break;
            case SocketAsyncOperation.Receive:
                ProcessReceive(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSend(e);
                break;
            default:
                throw new Exception("Invalid operation completed");
        }
    }

    private void ProcessConnect(SocketAsyncEventArgs e)
    {
        if (e.SocketError != SocketError.Success)
        {
            throw new SocketException((int)e.SocketError);
        }
        else
        {
            clientConnected.Set();
        }
    }

    private void ProcessReceive(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            var socket = e.UserToken as Socket;

            var response = dataCollector.Collect(e.Buffer);

            if (response != null)
            {
                if (this.OnDataReceived != null)
                    this.OnDataReceived(response);

                clientDataReceived.Set();
            }
            else
            {
                bool willRaiseEvent = socket.ReceiveAsync(clientArgs);
                if (!willRaiseEvent)
                    ProcessReceive(e);
            }
        }
        else
        {
            throw new SocketException((int)e.SocketError);
        }
    }

    private void ProcessSend(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {                
            var socket = e.UserToken as Socket;

            if (OnDataSent != null)
                OnDataSent(clientArgs.Buffer);

            clientDataSent.Set();
            clientDataReceived.Reset();

            bool willRaiseEvent = socket.ReceiveAsync(e);
            if (!willRaiseEvent)
                ProcessReceive(e);

            clientDataReceived.WaitOne();
        }
        else
        {
            throw new SocketException((int)e.SocketError);
        }
    }


    public void Stop()
    {            
        client.Shutdown(SocketShutdown.Send);
        client.Close();
        client.Dispose();
        clientArgs.Dispose();           
    }

    public void Write(byte[] data)
    {
        clientDataSent.Reset();

        clientArgs.SetBuffer(data, 0, data.Length);

        bool willRaiseEvent = client.SendAsync(clientArgs);
        if (!willRaiseEvent)
            ProcessSend(clientArgs);

        clientDataSent.WaitOne();
    }
}

这里的想法是每个请求(发送数据)总是由响应(接收数据)应答,只要您不断开连接并创建新连接,它就可以正常工作 .

例如:

client.Connect();
client.ClearConfiguration(1);
var status = client.RequestStatusDetails(1);
client.Disconnect();

此代码发送多个请求并接收每个请求的答案 . 但是,如果再次运行相同的代码(或循环),则 Build 连接但代码到达此点时:

public void Write(byte[] data)
{
    clientDataSent.Reset();

    clientArgs.SetBuffer(data, 0, data.Length);

    bool willRaiseEvent = client.SendAsync(clientArgs);
    if (!willRaiseEvent)
        ProcessSend(clientArgs);

    clientDataSent.WaitOne();
}

将抛出异常client.SendAsync(clientArgs);

这是例外:

使用此SocketAsyncEventArgs实例正在进行异步套接字操作

但是如果你在这个语句之前放了一个断点,让VS2010打破它,然后继续调试它工作正常 .

我真的无法弄清楚是什么导致了这个问题,而且没有其他信息 .

有什么建议?

2 回答

  • 0

    决定把我的评论作为答案 .

    恕我直言AutoResetEvent Class更适合您的需求 .

    AutoResetEvent clientDataSent = new AutoResetEvent(true);
    
    public void Write(byte[] data)
    {
        // Wait till the Write operation gets a green light to proceed. Consider using a timeout.
        clientDataSent.WaitOne();
    
        clientArgs.SetBuffer(data, 0, data.Length);
    
        bool willRaiseEvent = client.SendAsync(clientArgs);
    
        // Write operation will get a signal either from ProcessSend (sync) or clientArgs_Completed (async),
        if (!willRaiseEvent) ProcessSend(clientArgs);
    }
    
    void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        bool throwInvalidOperationException = false;
    
        switch (e.LastOperation)
        {
            ...
            default:
                throwInvalidOperationException = true;
        }
    
        //Signal a waiting Write operation that it can proceed.
        clientDataSent.Set();
    
        if (throwInvalidOperationException) throw new Exception("Invalid operation completed");
    }
    
  • 1

    使用AutoResetEvent作为Jaroslav Jandek建议似乎解决了我的问题 . 虽然如果您有任何关于如何改进此代码的建议,请随时这样做 .

    public class TcpDataTransportClient : IDataTransportService
    {
        private const string TCP_ADDRESS_SETTING = "tcpaddress";
        private const string TCP_PORT_SETTING = "tcpport";
    
        private Dictionary<string, object> settings = new Dictionary<string, object>();
        private IDataEncapsulator dataEncapsulator;
        private IDataCollector dataCollector;
    
        private Socket client;
        private SocketAsyncEventArgs clientArgs;
    
        public event DataReceivedHandler OnDataReceived;
        public event DataSentHandler OnDataSent;
    
        AutoResetEvent clientDataSent = new AutoResetEvent(false);
        AutoResetEvent clientConnected = new AutoResetEvent(false);
    
        public TcpDataTransportClient()
        {
    
        }
    
        public Dictionary<string, object> Settings
        {
            get
            {
                return this.settings;
            }
            set
            {
                this.settings = value;
            }
        }
    
        public IDataEncapsulator DataEncapsulator
        {
            get
            {
                return this.dataEncapsulator;
            }
            set
            {
                this.dataEncapsulator = value;
            }
        }
    
        public void Start(IDataCollector dataCollector)
        {
            this.dataCollector = dataCollector;
            clientArgs = new SocketAsyncEventArgs();
    
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientArgs.Completed += clientArgs_Completed;
            clientArgs.UserToken = client;            
            clientArgs.RemoteEndPoint = GetIPEndPoint();
    
            client.ConnectAsync(clientArgs);
            clientConnected.WaitOne();            
        }
    
        private IPEndPoint GetIPEndPoint()
        {
            IPAddress ipAddress;
            int tcpPort;
    
            if (!IPAddress.TryParse(settings[TCP_ADDRESS_SETTING].ToString(), out ipAddress))
                throw new ArgumentException(String.Format("Invalid setting for IP Address: '{0}'", TCP_ADDRESS_SETTING));
    
            if (!int.TryParse(settings[TCP_PORT_SETTING].ToString(), out tcpPort))
                throw new ArgumentException(String.Format("Invalid setting for TCP Port: '{0}'", TCP_PORT_SETTING));
    
            return new IPEndPoint(ipAddress, tcpPort);
        }
    
        void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    ProcessConnect(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    throw new Exception("Invalid operation completed");
            }
        }
    
        private void ProcessConnect(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                throw new SocketException((int)e.SocketError);
            }
            else
            {
                clientConnected.Set();
            }
        }
    
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                var socket = e.UserToken as Socket;
    
                var response = dataCollector.Collect(e.Buffer);
    
                if (response != null)
                {
                    if (this.OnDataReceived != null)
                        this.OnDataReceived(response);
                }
                else
                {
                    bool willRaiseEvent = socket.ReceiveAsync(clientArgs);
                    if (!willRaiseEvent)
                        ProcessReceive(e);
                }
            }
            else
            {
                throw new SocketException((int)e.SocketError);
            }
        }
    
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {                
                var socket = e.UserToken as Socket;
    
                if (OnDataSent != null)
                    OnDataSent(clientArgs.Buffer);
    
                bool willRaiseEvent = socket.ReceiveAsync(e);
                if (!willRaiseEvent)
                    ProcessReceive(e);
    
                clientDataSent.Set();
            }
            else
            {
                throw new SocketException((int)e.SocketError);
            }
        }
    
    
        public void Stop()
        {            
            client.Shutdown(SocketShutdown.Send);
            client.Close();
            client.Dispose();
            clientArgs.Dispose();           
        }
    
        public void Write(byte[] data)
        {          
            clientArgs.SetBuffer(data, 0, data.Length);
    
            bool willRaiseEvent = client.SendAsync(clientArgs);
            if (!willRaiseEvent)
                ProcessSend(clientArgs);
    
            clientDataSent.WaitOne();
        }
    }
    

    现在我可以断开连接并连接多次 . 但首先我调用SendAsync(),根据我的理解,它将在后台发送数据(大多数时间),然后下一个调用是.WaitOne(),它会阻塞线程直到数据实际发送完毕 . 连接也是如此 .

相关问题