简体   繁体   English

C#异步TCP服务器过度杀手?

[英]C# Async TCP Server overkill?

This is really an implementation question so I feel it's best to start with my specific case. 这实际上是一个实现问题,所以我觉得最好从我的具体案例开始。

I've got a C# server that listens for TCP connections asynchronously from mobile clients. 我有一个C#服务器,它从移动客户端异步侦听TCP连接。 When a mobile client connects a new thread is started, the client sends a little (<100 bytes usually) text message and receives one of similar size back. 当移动客户端连接新线程启动时,客户端发送一些(通常<100字节)文本消息并接收相似大小的文本消息。 After the server responds, it closes the connection and ends the thread. 服务器响应后,它会关闭连接并结束线程。

Current basic usage is a user logs in, checks on stuff for sometimes up to 5 minutes, sending little messages and thus creating new threads on the server in rapid succession , and they disconnect only to reconnect a few hours later. 当前的基本用法是用户登录,有时最多5分钟检查一些内容, 发送少量消息,从而快速连续地在服务器上创建新线程 ,并且几小时后它们仅断开连接。 Also, every user has their own server they run on their PC, and as such most servers will only ever have one client connected at any given time, in RARE cases two. 此外,每个用户都有自己的PC上运行的服务器,因此大多数服务器在任何给定时间只能连接一个客户端,在RARE情况下只有两个。

Right now I'm running into the following error, An existing connection was forcibly closed by the remote host , and it has got me thinking, am I doing this wrong? 现在我遇到了以下错误, 现有连接被远程主机强行关闭 ,它让我思考, 我做错了吗?

So my question(s): 所以我的问题:

  1. Is my current setup appropriate here? 我目前的设置是否合适?
  2. If so, should I be ending the thread after a little message is sent or keeping it alive and closing after a given period of idle? 如果是这样,我应该在发送一条小消息后结束线程还是保持活动并在给定的空闲时间后关闭?
  3. On the off chance that I'm doing everything correctly, highly unlikely , should I be avoiding the error by simply retrying a few times before giving up? 关于我正在做的一切正确, 非常不可能 ,我应该通过简单地在放弃之前重试几次来避免错误吗?
  4. Fourth and finally, that error completely kills the server (the server is spawned by another process and any untrapped exception kills it), if we've made it this far, and my implementation is OK, how can I avoid that? 第四个也是最后一个,那个错误完全杀死了服务器(服务器是由另一个进程生成的,任何未经处理的异常都会杀死它),如果我们已经做到这一点,我的实现是可以的,我怎么能避免呢?

EDIT: 编辑:

In response to some of the questions here: 回答这里的一些问题:

  • The exception is occurring before I receive all the data, but only in instances where a user has sent multiple messages in quick succession. 在收到所有数据之前发生异常,但仅在用户快速连续发送多条消息的情况下发生。
  • From what I recall max backlog is 5 unless a user is running Windows Server, however I have not set mine and I don't know what the default is, I'll try explicitly setting it to 5. 据我所知,除非用户正在运行Windows Server,否则max backlog为5,但是我没有设置我的,我不知道默认是什么,我会尝试将其明确设置为5。

Async Server Code: 异步服务器代码:

    public void StartListening()
    {
        //Data buffer for incoming data.
        byte[] bytes = new Byte[1024];

        //Establish the local endpoint for the socket.
        IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port);

        //Create a TCP/IP socket.
        Socket listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);
        listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontLinger,1);
        listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress,1);

        //Bind the socket to the local endpoint and listen for
        //incoming connections.
        try
        {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (listening)
            {
                //Set the event to nonsignaled state.
                allDone.Reset();    

                //Start an asychronous socket to listen for connections.
                Print("Waiting for a connection...");
                listener.BeginAccept(
                new AsyncCallback(AcceptCallback),
                    listener);

                //Wait until a connection is made before continuing.
                allDone.WaitOne();
            }
        }
        catch (Exception e)
        {
            Print(e.ToString());    
        }

        listener.Close();
    }

    public void AcceptCallback(IAsyncResult arg)
    {
        //Signal the main thread to continue.
        allDone.Set();


        try
        {
            //Get the socket that handles the client request.
            Socket listener = (Socket) arg.AsyncState;
            Socket handler = listener.EndAccept(arg);


            //Create the state object.
            StateObject state = new StateObject();
            state.workSocket = handler;

            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
        }
        catch (ObjectDisposedException ex)
        {
            Print("Server terminated from another thread.");    
        }
    }

    public void ReadCallback(IAsyncResult arg)
    {
        String content = String.Empty;

        //Retrieve the state object and the handler socket
        //from the asynchronous state object.
        StateObject state = (StateObject) arg.AsyncState;
        Socket handler = state.workSocket;

        //Read data from the client socket.
        int bytesRead = 0;
        try 
        {
            bytesRead = handler.EndReceive(arg);
        }
        catch (ObjectDisposedException ex)
        {
            Print("Process was terminated from another thread.");   
        }

        if (bytesRead > 0)
        {
            //There might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(
                state.buffer,0,bytesRead));

            //Check for end-of-file tag. If it is not there, read
            //more data.
            content = state.sb.ToString();
            if (content.IndexOf("<EOF>") > -1)
            {
                content = content.Remove(content.Length-6);
                //All the data has been read from the
                //client. Display it on the console.
                Print("Read " + content.Length + " bytes from socket. \n Data : " + content);
                Respond(handler, content);
            }
            else
            {
                //Not all data received. Get more.
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
            }
        }
    }

    private void Send(Socket handler, String data)
    {
        //Convert the string data to byte data using ASCII encoding.
        byte[] byteData = Encoding.ASCII.GetBytes(data);

        //Begin sending the data to the remote device.
        handler.BeginSend(byteData,0,byteData.Length,0,
            new AsyncCallback(SendCallback),handler);
    }

    private void SendCallback(IAsyncResult arg)
    {
        try
        {
            //Retrieve the socket from the state object.
            Socket handler = (Socket) arg.AsyncState;

            //Complete sending the data to the remote device.
            int bytesSent = handler.EndSend(arg);
            Print("Sent " + bytesSent + " bytes to client.");

            handler.Shutdown(SocketShutdown.Both);
            //need to make this not linger around
            handler.LingerState = new LingerOption(true,1);
            handler.Close();
        }
        catch (Exception e)
        {
            Print(e.ToString());    
        }
    }

Ideally, you'd be using the .NET threadpool, which would be much more efficient than creating a new thread for every connection. 理想情况下,您将使用.NET线程池,这比为每个连接创建新线程更有效。 Can you please share your exact "async" code - if you're using the existing async pattern on TCPListener then you're probably already using the threadpool. 你可以分享你确切的“异步”代码 - 如果你在TCPListener上使用现有的异步模式,那么你可能已经在使用线程池了。

With respect to the exception, that's what you'd expect to see when your clients disconnect from the server. 关于异常,当您的客户端与服务器断开连接时,您会看到这种情况。 Is it occurring before you manage to receive all the data? 是否在您设法接收所有数据之前发生了? Are you flushing your socket on the client side? 你在客户端刷新插座吗?

In terms of completely crashing the server, just keep testing, and log any globally unhandled exceptions. 在完全崩溃服务器方面,只需继续测试,并记录任何全局未处理的异常。 That way you'll learn about everything that can be expected. 这样你就可以了解可以预期的一切。

You might want to have a look at this article , which has a good list of several things to check. 您可能希望看一下这篇文章 ,其中列出了一些需要检查的好东西。 For example, what is your backlog set to when you .Listen() on your socket? 例如,当您在套接字上使用.Listen()时,您的待办事项是什么?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM