代码之家  ›  专栏  ›  技术社区  ›  TimothyP

SL4 AsyncEventArgs在第二个套接字连接后引发InvalidOperationException

  •  1
  • TimothyP  · 技术社区  · 15 年前

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

    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();
    

    此代码发送多个请求,并接收每个请求的答案。 但是,如果您再次运行相同的代码(或在循环中),则会建立连接。 但一旦代码达到这一点:

    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 回复  |  直到 15 年前
        1
  •  1
  •   Jaroslav Jandek    15 年前

    决定把我的意见作为答案。

    伊莫尔 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");
    }
    
        2
  •  0
  •   TimothyP    15 年前

    按照Jaroslav Jandek的建议,使用autoreseteevent似乎解决了我的问题。 尽管你们中的任何人对如何改进这个代码有建议,请随时这样做。

    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(),在那里它将阻塞线程,直到实际发送数据为止。连接也是如此。