kerryjiang / SuperSocket.ClientEngine

A .NET library which can make your socket client development easier
366 stars 197 forks source link

System.InvalidOperationException was thrown when sending data with EasyClient #57

Open wmjordan opened 7 years ago

wmjordan commented 7 years ago

I was using the easy client to send some data. The source data was pushed into the application from about 20 incoming TCP connections, processed by a AppServer of SocketEngine, which called an EasyClient to send data to another place. The client was under a pretty low workload, sending about 2 packages a second only. However, it occasionally threw a "System.InvalidOperationException" and the application became unusable afterward.

The following was the stack trace related to the exception. I could not found a line relative to my own code, I deduced that it was in another thread, spawned from the EasyClient.

System.InvalidOperationException: "An asynchronous socket operation is already in progress using this SocketAsyncEventArgs instance.";
SuperSocket.ClientEngine.AsyncTcpSession.SetBuffer(ArraySegment`1 bufferSegment) @ SuperSocket.ClientEngine\Core\AsyncTcpSession.cs[36:13]
SuperSocket.ClientEngine.ClientSession.SuperSocket.ClientEngine.IBufferSetter.SetBuffer(ArraySegment`1 bufferSegment) @ SuperSocket.ClientEngine\Core\ClientSession.cs[207:13]
SuperSocket.ClientEngine.EasyClientBase.OnSessionDataReceived(Object sender, DataEventArgs e) @ SuperSocket.ClientEngine\EasyClientBase.cs[236:13]
SuperSocket.ClientEngine.AsyncTcpSession.ProcessReceive(SocketAsyncEventArgs e) @ SuperSocket.ClientEngine\Core\AsyncTcpSession.cs[89:13]
kerryjiang commented 7 years ago

Do you have a test case I can reproduce this issue?

wmjordan commented 7 years ago

Thank you for replying. It was hard to reproduce in a short time. I was trying to mock up a stress test model to reproduce this issue and I would post it here when it is finished. Meanwhile, I moved the application to another machine and see whether it would occur there as well.

kerryjiang commented 7 years ago

And which version do you use?

wmjordan commented 7 years ago

Sorry to have forgotten to mention the versions. All assemblies were compiled from the newest source code from your repositories. I'd removed the dependency from NuGet and used the compiled assemblies in the ClientEngine project.

wmjordan commented 7 years ago

The projects targeting .NET 4.0 were used to compile the assemblies. And the operating system was Windows Server 2008.

wmjordan commented 7 years ago

I studied the source code of EasyClientBase and AsyncTcpSession.

I found that the AsyncTcpSession.SendInternal, AsyncTcpSession.StartReceive and AsyncTcpSession.SetBuffer can be using the same instance of SocketAsyncEventArgs without locking. However, the SocketAsyncEventArgs internally excludes data-sending, data-receiving and buffer-setting operations mutually. Those three operations can not proceed at the same time. Consequently, the SetBuffer method can fail when the Socket is using the SocketAsyncEventArgs instance to receive or send data and therefore the InvalidOperationException is thrown. Thread synchronization shall be deployed to avoid the above problem.

kerryjiang commented 7 years ago

I think send and receive have different SocketAsyncEventArgs instances: private SocketAsyncEventArgs m_SocketEventArgs; private SocketAsyncEventArgs m_SocketEventArgsSend;

wmjordan commented 7 years ago

The issue happened to my applications when the client was attempting to receive data. Could it be possible that the SetBuffer method was called when a socket was using the same SocketAsyncEventArgs instance to receive data?

kerryjiang commented 7 years ago

Is there a way to produce this issue easily?

wmjordan commented 7 years ago

Not so easy, I am afraid. But, I will still try to make a test case for you, or fix this issue at my side and commit a pull request here if I am lucky.

yusliao commented 6 years ago

重连多次,大概10次左右。 protected override void SetBuffer(ArraySegment bufferSegment) { base.SetBuffer(bufferSegment);

        if (m_SocketEventArgs != null)
        {
            m_SocketEventArgs.SetBuffer(bufferSegment.Array, bufferSegment.Offset, bufferSegment.Count);
        }
    }

m_SocketEventArgs.SetBuffer 这里报这个错误。连接上之后,已经在接收数据时候,报错。