Limit amount and time of connections TcpListener. How correct close\stop TcpListener and TcpClient?

StackOverflow https://stackoverflow.com/questions/21783422

  •  11-10-2022
  •  | 
  •  

Question

I have some code:

    protected TcpListener ClientListener;
    protected TcpClient ClientSocket;
    protected Thread th1;
    public static string server_ip = string.Empty;
    public static string server_port = string.Empty;

    internal void start_potok()
    {
        Protection.Ban.ban_del();
        th1 = new Thread(start_listener);
        th1.IsBackground = true;
        th1.Start();
    }

    internal void stop_potok()
    {
        th1.Abort();
        if (ClientSocket != null)
        {
            ClientSocket.Close();
        }
        ClientListener.Stop();
    }

    private void start_listener() 
    {
        try
        {
            ClientListener = new TcpListener(IPAddress.Parse(server_ip), Convert.ToInt32(server_port));
            ClientListener.Start();
            ClientSocket = default(TcpClient);
            while (true)
            {
                ClientSocket = ClientListener.AcceptTcpClient();
                client_connected(ClientSocket);
            }
        catch (ThreadAbortException ex)
        {
            //not catch this exception
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error: " + ex.Message);
        }
    }

    private void client_connected(TcpClient client)
    {
        //check bans and other
        LoginClientProc lcp = new LoginClientProc(client);
    }

Class of listening:

class LoginClientProc
{
    internal EndPoint address;
    internal TcpClient client;
    internal NetworkStream stream;
    private byte[] buffer;

    internal LoginClientProc(TcpClient Client)
    {
        address = Client.Client.RemoteEndPoint;
        client = Client;
        stream = Client.GetStream();

        new System.Threading.Thread(read).Start();
    }

    void read()
    {
        try
        {
            buffer = new byte[2];
            stream.BeginRead(buffer, 0, 2, new AsyncCallback(OnReceiveCallbackStatic), null);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    private void OnReceiveCallbackStatic(IAsyncResult result)
    {
        int rs = 0;
        try
        {
            rs = stream.EndRead(result);

            if (rs > 0)
            {
                short Length = BitConverter.ToInt16(buffer, 0);
                buffer = new byte[Length - 2];
                stream.BeginRead(buffer, 0, Length - 2, new AsyncCallback(OnReceiveCallback), result.AsyncState);
            }
            else
                //0 = client close connection
        }
        catch (Exception s)
        {
        }
    }

    private void OnReceiveCallback(IAsyncResult result)
    {
        stream.EndRead(result);

        byte[] buff = new byte[buffer.Length];
        buffer.CopyTo(buff, 0);
        if (!verify_packet)
        {
            //Exception
        }
        else
        {
            handle(buff);
            new System.Threading.Thread(read).Start();
        }
    }

    private void handle(byte[] buff)
    {
        byte id = buff[0];
        switch (id)
        {
            //cases for headers packets
            default:
                //unknown packet
                //Here need correct Close connection
                break;
        }
       //response for packet
}

So, I have some questions:

  1. How can I limit amount of client connections? (not more than 10 users, for example)
  2. How can I limit time (life) of client connections? (not more than 30 sec, for example)
  3. How correct stop Listening Thread (TcpListener, TcpClient) as when Application close?
  4. How correct close TcpClient when Server received unknown packet?

thx for your answers!

Était-ce utile?

La solution

You probably need to use a queue. Here is a sketch of a possible solution on the server side:

class Program
{
    Queue<Connection> queue = new Queue<Connection>();

    TcpListener listener;

    ManualResetEvent reset = new ManualResetEvent(true);

    static void Main(string[] args)
    {
        new Program().Start();
    }

    void Start()
    {
        new Thread(new ThreadStart(HandleConnection)).Start();

        while (true)
        {
            while (queue.Any())
            {
                var connection = queue.Dequeue();

                connection.DoStuff();

                if(!connection.Finished)
                    queue.Enqueue(connection);
            }

            reset.WaitOne();
        }
    }

    static readonly byte[] CONNECTION_REFUSED = Encoding.ASCII.GetBytes("Cannot accept a connection right now.");
    static readonly int CONNECTION_REFUSED_LENGTH = CONNECTION_REFUSED.Length;

    void HandleConnection()
    {
        listener.Start();

        while (true)
        {
            var client = listener.AcceptTcpClient();

            if (queue.Count <= 10)
            {
                queue.Enqueue(new Connection(client));
                reset.Set();
            }
            else
            {
                client.GetStream().Write(CONNECTION_REFUSED, 0, CONNECTION_REFUSED_LENGTH);
                client.Close();
            }
        }
    }

}

public sealed class Connection
{
    readonly TcpClient client;
    readonly Stopwatch stopwatch = new Stopwatch();

    public Connection(TcpClient client)
    {
        this.client = client;
        stopwatch.Start();
    }

    public void DoStuff()
    {

    }

    public void Abort()
    {
        //You can write out an abort message to the client if you like.
        client.Close();
        completed = true;
    }

    bool completed;

    public bool Finished
    {
        get
        {
            return stopwatch.ElapsedMilliseconds > 30 * 1000 || completed;
        }
    }
}

The basic idea is to use one thread to add incoming connections to a queue ,then use another thread to iterate through the queue to do stuff with each connection. A connection is removed from the queue (or rather not re-enqueued) when it is Finished.

A robust solution will probably require using either the lock statement or the ConcurrentQueue class. In my sketch, I used the blocking methods on the TCPClient class for the sake off brevity, but, of course you will want to use the non-blocking ones.

Autres conseils

Limit the amount of client connections:

TcpListener.Start Method (Int32)

How to set timeouts:

tcpListener.Server.ReceiveTimeout = 1000;
tcpListener.Server.SendTimeout = 1000;

Proper way to stop listener:

Proper way to stop TcpListener

Proper way to close TCPClient when Server received an unknown packet

This is somewhat complex depending on the protocol you want to implement.

  • Simplest way is to just close the incoming connection.
  • If you want something smarter you need to think how to let the client know there was an unexpected package and to either resend or start over. Basically design an error handling mechanism (there're a lot out there).
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top