kerryjiang / WebSocket4Net

A popular .NET WebSocket Client
752 stars 272 forks source link

Socket not properly clean up, if connection drop #139

Open humblecscoder opened 5 years ago

humblecscoder commented 5 years ago

Hello, kerryjiang. I facced with the situation, when I try to reconnect to server, that temporarily unavailable, with setting LocalEndPoint. I catch exception in WebSocket.OnError event: WSAEADDRINUSE(10048) Only one usage of each socket address (protocol/network address/port) is normally permitted. And each time my reconnect timer elapsed I always catch this exception.

public class Client
{
    private WebSocket _webSocket;
    private readonly string _serverIp;
    private readonly int _serverPort;
    private readonly string _clientIp;
    private readonly int _clientPort;
    private readonly System.Timers.Timer reconnectTimer;

    public Client(string serverIp, int serverPort, string clientIp, int clientPort)
    {
        _serverIp = serverIp;
        _serverPort = serverPort;
        _clientIp = clientIp;
        _clientPort = clientPort;

        _webSocket = new WebSocket($"ws://{_serverIp}:{_serverPort}");
        _webSocket.EnableAutoSendPing = true;

        _webSocket.Opened += Websocket_Opened;
        _webSocket.Error += Websocket_Error;
        _webSocket.Closed += Websocket_Closed;

        reconnectTimer = new System.Timers.Timer
        {
            Interval = 15000
        };
        reconnectTimer.Elapsed += ReconnectTimer_Elapsed;
    }

    private void ReconnectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        if (_webSocket.State != WebSocketState.Connecting && _webSocket.State != WebSocketState.Open)
        {
            Connect();
        }
    }

    private void Websocket_Opened(object sender, EventArgs e)
    {
        Console.WriteLine("Websocket is opened.");
    }

    private void Websocket_Error(object sender, ErrorEventArgs e)
    {
        Console.WriteLine("Error: " + e.Exception.Message);
    }

    private void Websocket_Closed(object sender, EventArgs e)
    {
        Console.WriteLine("Websocket is closed.");
    }

    public void Connect()
    {
        _webSocket.LocalEndPoint = new IPEndPoint(IPAddress.Parse(_clientIp), _clientPort);
        Console.WriteLine($"Connect to serverIP: {_serverIp}, port: {_serverPort}");

        if (_webSocket.State != WebSocketState.Connecting && _webSocket.State != WebSocketState.Open)
        {
            _webSocket.Open();
        }
        reconnectTimer.Start();
    }
}

When I try to reconnect without setting LocalEndPoint. It works well. My suggestion that Socket not properly cleans up. SuperSocket.ClientEngine in TcpClientSession ProcessConnect maybe add this lines.

if (e != null && e.SocketError != SocketError.Success)
{
> if (socket.LocalEndPoint != null) 
>     { 
>         socket.Close(); 
>     }
        m_InConnecting = false;
        OnError(new SocketException((int)e.SocketError));
        e.Dispose();
        return;
}

Maybe I'm doing something wrong and there's a way to reconnect correctly with setting each time LocalEndPoint. I am using: .Net 4.5 Webscoket4net 0.15.2.11 SuperSocket.ProtoBase 1.7.0.17 SuperSocket.ClientEngine 0.10.0.0

PhilippElhaus commented 3 years ago

I can confirm this is an existing Bug and noticed this in one of my applications as well. If i reconnect after a crash/Websocket Error, i noticed that it does not properly update the Websocket State. E.g. after recreating the connection and being connected and receiving data everything is fine BUT but the Websocket State: when i query the Websocket State it says "Connecting" instead of "Open" (what it should logically show).