Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TcpServer call OnConnected after object disposed #115

Closed
wingyukyuk opened this issue Feb 18, 2021 · 14 comments
Closed

TcpServer call OnConnected after object disposed #115

wingyukyuk opened this issue Feb 18, 2021 · 14 comments

Comments

@wingyukyuk
Copy link

wingyukyuk commented Feb 18, 2021

In some case (if the connect disconnect immediately after connect), the OnConnected will call after OnDisconnected, and the OnConnected function will not able to access the Sockets object due to it already disposed.

I can add a addition checking on this case. May I know it is a bug or any better solution?

Thanks

        protected override void OnConnected()
        {
            Console.WriteLine($"TCP session with Id {Id} connected! RemoteEndPoint : {Socket.RemoteEndPoint} .");
        }
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
   at System.Net.Sockets.Socket.ThrowObjectDisposedException()
   at System.Net.Sockets.Socket.get_RemoteEndPoint()
   at xxxxxx.xxSession.OnConnected()
   at NetCoreServer.TcpSession.Connect(Socket socket)
   at NetCoreServer.TcpServer.ProcessAccept(SocketAsyncEventArgs e)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pNativeOverlapped)
@chronoxor
Copy link
Owner

Fixed in 5.0.8 please try!

@wingyukyuk
Copy link
Author

wingyukyuk commented Feb 22, 2021

I try 5.0.8 and 5.0.9, the OnDisconnected still call before OnConnected. also, still got System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.Socket'. Error on OnConnected function.

The Server will execute
protected override void OnReceived(byte[] buffer, long offset, long size)
then
protected override void OnDisconnected()
finally call
protected override void OnConnected()

I use PHP socket to call the TCP server.

<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, "127.0.0.1", 12345);

print "#socket_connect#";
$cmd = "##TEST##";
$len = strlen($cmd);
$offset = 0;

while ($offset < $len)
{
    $sent = socket_write($socket, substr($cmd, $offset), $len - $offset);
    if ($sent === false)
        break;	//Error
    $offset += $sent;
}

if ($offset < $len)
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
}
socket_close($socket);	
print "#socket_close#";	
?>

@wingyukyuk
Copy link
Author

wingyukyuk commented Feb 22, 2021

Update
I try to using the TcpClient on NetCoreServer to connect into the TcpServer

  protected override void OnConnected()
  {
    SendAsync("##TEST##");
    Disconnect();
  }

will have same result

@chronoxor
Copy link
Owner

Reproduced. I'll look into it!

@chronoxor
Copy link
Owner

Could you please try version 5.0.11?

@wingyukyuk
Copy link
Author

wingyukyuk commented Feb 23, 2021

try both 5.0.11 and 12, still happens
I am sorry about that if I can't explain well.

The exception is on the Server Side in my case, and not case by SendAsync method.
those code I posted is the client will cause this problem (with both PHP socket client and C#)

You may discover another issue in this case.
If not, I just worry if you change the code from Task.Factory.StartNew(TrySend); to TrySend(); may lost the asynchronous characteristics.
But I must to say I am not fully understand your code, you should keep this change if needed.

We may expect, the flow of function call on the Server Side should be
OnConnected() --> (if any data) OnReceived() --> OnDisconnected()
But in my case, if the client disconnect immediately, the server will
(if any data) OnReceived() --> OnDisconnected() --> OnConnected()
and I will get the object disposed exception on the OnConnected() method during access Sockets object

Thanks and sorry if I make you misunderstand.

@chronoxor
Copy link
Owner

TrySend() has call Socket.SendAsync() so it's all ok with async. Task.Factory.StartNew() was an old workaround that put unnecessary thread racing.

On a Server Side I don't understand how you managed to step OnDisconnected() --> OnConnected(). OnConnected() should not be called for the Session after any disconnect. A new session should be created for a new connection...

@wingyukyuk
Copy link
Author

wingyukyuk commented Feb 23, 2021

I don't know and may be my code issue, you may check as below:

I will able to get (able >90% some time)

Server starting...
Client starting... 0
Client starting... 1
Client starting... 2
Client starting... 3
Client starting... 4
Client starting... 5
Client starting... 6
Client starting... 7
Client starting... 8
Client starting... 9
Client starting... 10
Client starting... 11
Client starting... 12
Client starting... 13
Client starting... 14
Client starting... 15
Client starting... 16
Client starting... 17
Client starting... 18
Client starting... 19
Client starting... 20
Client starting... 21
Client starting... 22
Client starting... 23
Client starting... 24
Client starting... 25
Client starting... 26
d051e1b5-9a09-4700-a609-a3bd7945aca7 127.0.0.1:58542 call [##TEST##]
Client starting... 27
Client starting... 28
Client starting... 29
d051e1b5-9a09-4700-a609-a3bd7945aca7 disconnected!
Client starting... 30
Client starting... 31
Client starting... 32
Client starting... 33
Client starting... 34
Client starting... 35
Client starting... 36
Client starting... 37
Client starting... 38
Client starting... 39
Client starting... 40
Client starting... 41
Client starting... 42
Client starting... 43
Client starting... 44
Client starting... 45
Client starting... 46
Client starting... 47
Client starting... 48
Client starting... 49
Client starting... 50
Client starting... 51
Client starting... 52
Client starting... 53
Client starting... 54
Client starting... 55
Client starting... 56
Client starting... 57
Client starting... 58
Client starting... 59
Client starting... 60
Client starting... 61
Client starting... 62
Client starting... 63
Client starting... 64
Client starting... 65
Client starting... 66
Client starting... 67
Client starting... 68
Client starting... 69
Client starting... 70
Client starting... 71
Client starting... 72
Client starting... 73
Client starting... 74
Client starting... 75
Client starting... 76
Client starting... 77
Client starting... 78
Client starting... 79
Client starting... 80
Client starting... 81
Client starting... 82
Client starting... 83
Client starting... 84
Client starting... 85
d051e1b5-9a09-4700-a609-a3bd7945aca7 error :
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
   at System.Net.Sockets.Socket.ThrowObjectDisposedException()
   at System.Net.Sockets.Socket.get_RemoteEndPoint()
   at TcpChatTest.Program.ChatSession.OnConnected() in \TcpChatTest\Program.cs:line 34
Client starting... 86
d051e1b5-9a09-4700-a609-a3bd7945aca7 connected!
error statis True
8538d9fe-aa91-44de-afd0-7f28b9d47b47 127.0.0.1:58543 call [##TEST##]
8538d9fe-aa91-44de-afd0-7f28b9d47b47 disconnected!
8538d9fe-aa91-44de-afd0-7f28b9d47b47 error :
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
   at System.Net.Sockets.Socket.ThrowObjectDisposedException()
   at System.Net.Sockets.Socket.get_RemoteEndPoint()
   at TcpChatTest.Program.ChatSession.OnConnected() in \TcpChatTest\Program.cs:line 34
8538d9fe-aa91-44de-afd0-7f28b9d47b47 connected!
6e437ac3-858f-4f7a-b76f-5121d339ab5a 127.0.0.1:58544 call [##TEST##]
6e437ac3-858f-4f7a-b76f-5121d339ab5a disconnected!

with the code as below

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NetCoreServer;
namespace TcpChatTest
{
    class Program
    {
        public static bool _error = false;
        class ChatClient : NetCoreServer.TcpClient
        {
            public ChatClient(string address, int port) : base(address, port) { }
            protected override void OnConnected()
            {
                SendAsync("##TEST##");
                Disconnect();
            }
        }
        public class ChatServer : NetCoreServer.TcpServer
        {
            public ChatServer(IPAddress address, int port) : base(address, port) { }
            protected override TcpSession CreateSession() { return new ChatSession(this); }

        }
        public class ChatSession : NetCoreServer.TcpSession
        {
            public ChatSession(TcpServer server) : base(server) { }
            protected override void OnConnected()
            {
                try
                {
                    _ = Socket.RemoteEndPoint.ToString();
                }
                catch(Exception e)
                {
                    Console.WriteLine($"{Id} error :\n\r{e}");
                    Program._error = true;
                }
                Console.WriteLine($"{Id} connected! ");
            }
            protected override void OnReceived(byte[] buffer, long offset, long size)
            {
                string message = Encoding.UTF8.GetString(buffer, (int)offset, (int)size);
                Console.WriteLine($"{Id} {Socket.RemoteEndPoint} call [{message}] ");
            }
            protected override void OnDisconnected()
            {
                Console.WriteLine($"{Id} disconnected!");
            }
            protected override void OnError(SocketError error)
            {
                Console.WriteLine($"Session caught an error: {error}");
            }

        }
        static void Main(string[] args)
        {
           
            var port = 12345;
            Console.WriteLine("Server starting...");
            var server = new ChatServer(IPAddress.Any, port);
            server.Start();

            Thread.Sleep(1000);
            for (int i = 0; i < 1000; i++)
            {
                if (_error) break;
                Console.WriteLine($"Client starting... {i}");
                _ = new ChatClient("127.0.0.1", port).ConnectAsync();
                
            }
            Console.WriteLine($"error statis {_error}");
        }
    }
}

You will able to see "d051e1b5-9a09-4700-a609-a3bd7945aca7 " is calling OnReceived -> OnDisconnected -> OnConnected.

Maybe the multithreading with Console.WriteLine will make me misunderstanding the real situation?
Ummm....

@wingyukyuk
Copy link
Author

For the PHP socket I will able to get >99% with just one session

image

@chronoxor
Copy link
Owner

Thanks for samples! I'll look into it.

@chronoxor
Copy link
Owner

Reproduced and fixed in 5.0.13
Please test it on your side!

@wingyukyuk
Copy link
Author

wingyukyuk commented Feb 23, 2021

Very fast response.
The server will not call OnConnected() after OnDisconnected() is call.
On 5.0.13, the server may have some connection never call the OnConnected() function with this method, I will be fine if this is the limitation.

Thanks for your work!

@chronoxor
Copy link
Owner

It's the right behavior. Because the connection establishing (OnConnecting() handler called), but suddenly disconnected by peer, therefore you'll receive OnDisconnected() then instead of OnConnected().

@wingyukyuk
Copy link
Author

Thanks for your explanation~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants