限制连接数量和时间TcpListener。如何正确关闭\停止TcpListener和TcpClient?

时间:2014-02-14 15:36:43

标签: c# tcpclient tcplistener

我有一些代码:

    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 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
}

所以,我有一些问题:

  1. 如何限制客户端连接数量? (不超过10个 用户,例如)
  2. 如何限制客户端连接的时间(生命)? (例如,不超过30秒)
  3. 当应用程序关闭时,如何正确停止侦听线程(TcpListener,TcpClient)?
  4. 当服务器收到未知数​​据包时,如何正确关闭TcpClient?
  5. thx为你的答案!

2 个答案:

答案 0 :(得分:2)

您可能需要使用队列。以下是服务器端可能解决方案的草图:

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;
        }
    }
}

基本思想是使用一个线程将传入连接添加到队列,然后使用另一个线程迭代队列以对每个连接执行操作。当Finishedlock时,连接将从队列中删除(或者不是重新入队)。

强大的解决方案可能需要使用ConcurrentQueue语句或TCPClient类。在我的草图中,为了简洁起见,我在{{1}}类上使用了阻塞方法,但是,当然你会想要使用非阻塞方法。

答案 1 :(得分:0)

限制客户端连接数量:

TcpListener.Start Method (Int32)

如何设置超时:

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

阻止听众的正确方法:

Proper way to stop TcpListener

当服务器收到未知数​​据包时关闭TCPClient的正确方法

这有点复杂,具体取决于您要实施的协议。

  • 最简单的方法是关闭传入连接。
  • 如果你想要更聪明的东西,你需要考虑如何让客户知道有一个意外的包,并重新发送或重新开始。基本上设计一个错误处理机制(那里有很多)。