William
William

Reputation: 3405

Asynchronous Socket Client/Server throwing exception (TcpClient & TcpListener)

I am trying to write an asynchronous socket Server/Client, but I keep getting the following error:

Unable to read data from the transport connection: A blocking operation was interrupted
    by a call to WSACancelBlockingCall.
Inner Exception: System.Net.Sockets.SocketException (0x80004005): A blocking operation
    was interrupted by a call to WSACancelBlockingCall
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size,
    SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)

This exception it thrown in my SockerServer class at the following line:

BytesRead = ClientStream.Read(MessageBuffer, 0, BufferSize);

This only occurs when the Client attempts to disconnect from the server - they appear to disconnect fine and then I get this exception.

Here is the codez:

MessageHandler

There is a single instance of this class in the Client and one for each of the clients in the server. This is used to wrap the NetworkStream for each client so that messages can be written to it.

public class MessageHandler
{
    private readonly ASCIIEncoding ByteEncoder = new ASCIIEncoding();

    private readonly NetworkStream ClientStream;

    public MessageHandler(NetworkStream Stream)
    {
        this.ClientStream = Stream;
    }

    public void Send(byte[] Message)
    {
        ClientStream.Write(Message, 0, Message.Length);
        ClientStream.Flush();
    }

    public void Send(string Message)
    {
        byte[] MessageBytes = ByteEncoder.GetBytes(Message);
        ClientStream.Write(MessageBytes, 0, MessageBytes.Length);
        ClientStream.Flush();
    }
}

MessageHandlerEventArgs

public class MessageHandlerEventArgs : EventArgs
{
    public readonly MessageHandler SocketMessage;

    public MessageHandlerEventArgs(MessageHandler Messages)
    {
        this.SocketMessage = Messages;
    }
}

SocketHandler (Base class for SocketServer & SocketClient)

public abstract class SocketHandler
{
    protected const int DefaultPort = 3000;
    protected const int BufferSize = 4096;
    protected readonly Action<byte[]> MessageAction;

    public SocketHandler(Action<byte[]> MessageAction)
    {
        this.MessageAction = MessageAction;
        ClientConnected += OnClientConnected;
        ClientDisconnected += OnClientDisconnected;
    }

    protected void HandleConnection(IAsyncResult ar)
    {
        TcpClient Client = (TcpClient)ar.AsyncState;
        ThreadPool.QueueUserWorkItem((x) => HandleConnection(Client));
    }

    protected void HandleConnection(TcpClient NetworkClient)
    {
        try
        {
            using (NetworkStream ClientStream = NetworkClient.GetStream())
            {
                MessageHandler Messages = new MessageHandler(ClientStream);
                DoClientConnected(new MessageHandlerEventArgs(Messages));
                byte[] MessageBuffer = new byte[BufferSize];
                int BytesRead;

                while (true)
                {
                    BytesRead = 0;
                    BytesRead = ClientStream.Read(MessageBuffer, 0, BufferSize);

                    if (BytesRead == 0)
                    {
                        DoClientDisconnected(new MessageHandlerEventArgs(Messages));
                        break;
                    }
                    else
                    {
                        MessageAction.Invoke(MessageBuffer.ToArray());
                    }
                }
                NetworkClient.Close();
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketHandler.HandleConnection");
        }
    }

    protected abstract void OnClientConnected(object sender, MessageHandlerEventArgs e);
    protected abstract void OnClientDisconnected(object sender, MessageHandlerEventArgs e);

    public event EventHandler<MessageHandlerEventArgs> ClientConnected;
    protected void DoClientConnected(MessageHandlerEventArgs args)
    {
        if (ClientConnected != null)
        {
            ClientConnected(this, args);
        }
    }

    public event EventHandler<MessageHandlerEventArgs> ClientDisconnected;
    protected void DoClientDisconnected(MessageHandlerEventArgs args)
    {
        if (ClientDisconnected != null)
        {
            OnClientDisconnected(this, args);
        }
    }

    public event EventHandler StatusChanged;
    protected void DoStatusChanged()
    {
        if (StatusChanged != null)
        {
            StatusChanged(this, EventArgs.Empty);
        }
    }
}

Socket Server

public class SocketServer : SocketHandler
{
    private static List<MessageHandler> CurrentClients = new List<MessageHandler>();
    private ServerStatus _Status;
    public ServerStatus Status
    {
        get { return _Status; }
        private set
        {
            _Status = value;
            DoStatusChanged();
        }
    }

    private readonly TcpListener NetworkServer;

    public SocketServer(Action<byte[]> MessageAction)
        : base(MessageAction)
    {
        NetworkServer = new TcpListener(IPAddress.Any, DefaultPort);
    }

    public void Start()
    {
        try
        {
            if (Status == ServerStatus.Stopped)
            {
                ThreadPool.QueueUserWorkItem((x) => ListenForClient());
                Status = ServerStatus.Started;
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketServer.Start");
        }
    }

    public void Stop()
    {
        try
        {
            if (Status != ServerStatus.Stopped)
            {
                Status = ServerStatus.Stopped;
                NetworkServer.Stop();
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketServer.Stop");
        }
    }

    private void ListenForClient()
    {
        try
        {

            Status = ServerStatus.WaitingClient;
            NetworkServer.Start();
            while (Status != ServerStatus.Stopped)
            {
                if (!NetworkServer.Pending())
                {
                    Thread.Sleep(100);
                    continue;
                }
                else
                {
                    TcpClient NetworkClient = NetworkServer.AcceptTcpClient();
                    ThreadPool.QueueUserWorkItem((x) => HandleConnection(NetworkClient));
                    Status = ServerStatus.Connected;
                }
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketServer.ListenForClient");
        }
    }

    protected override void OnClientConnected(object sender, MessageHandlerEventArgs e)
    {
        CurrentClients.Add(e.SocketMessage);
    }

    protected override void OnClientDisconnected(object sender, MessageHandlerEventArgs e)
    {
        CurrentClients.Remove(e.SocketMessage);

        Status = (CurrentClients.Count == 0)
            ? ServerStatus.WaitingClient
            : ServerStatus.Connected;
    }
}

SocketClient

public class SocketClient : SocketHandler
{
    private ClientStatus _Status;
    public ClientStatus Status
    {
        get { return _Status; }
        set
        {
            _Status = value;
            DoStatusChanged();
        }
    }

    private TcpClient NetworkClient;

    public MessageHandler Messages;

    public SocketClient(Action<byte[]> MessageAction)
        : base(MessageAction)
    {
        NetworkClient = new TcpClient();
    }

    public void Connect(IPAddress ServerAddress, int ServerPort)
    {
        try
        {
            if (Status == ClientStatus.Disconnected)
            {
                Status = ClientStatus.Connecting;
                NetworkClient.Connect(ServerAddress, ServerPort);
                ThreadPool.QueueUserWorkItem((x) => HandleConnection(NetworkClient));
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketClient.Connect");
        }
    }

    public void Disconnect()
    {
        try
        {
            if (Status != ClientStatus.Disconnected)
            {
                Status = ClientStatus.Disconnecting;
                NetworkClient.Close();
                Status = ClientStatus.Disconnected;
            }
        }
        catch (Exception ex)
        {
            Logger.ExceptionLog.AddEntry(ex, "SocketClient.Disconnect");
        }

    }

    protected override void OnClientConnected(object sender, MessageHandlerEventArgs e)
    {
        Messages = e.SocketMessage;
        Status = ClientStatus.Connected;
    }

    protected override void OnClientDisconnected(object sender, MessageHandlerEventArgs e)
    {
        Messages = null;
        Status = ClientStatus.Disconnected;
    }
}

Upvotes: 0

Views: 1796

Answers (1)

jgauffin
jgauffin

Reputation: 101192

Do not code like that. Using threads/thread pool is just wrong. The Socket/TcpClient has asynchronous methods which should be used

I've created a framework which you can use. It takes care of all processing for you. all you need to do is to handle the incoming/outgoing data.

http://blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net/

Upvotes: 1

Related Questions