接受多个 tcp 客户端的最佳方式? [英] Best way to accept multiple tcp clients?

查看:18
本文介绍了接受多个 tcp 客户端的最佳方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个客户端/服务器基础设施.目前他们使用 TcpClient 和 TcpListener 在所有客户端和服务器之间发送接收数据.

I have a client/server infrastructure. At present they use a TcpClient and TcpListener to send a receive data between all the clients and server.

我目前所做的是当接收到数据时(在它自己的线程上),它被放入一个队列以供另一个线程处理以释放套接字,以便它准备好并打开以接收新数据.

What I currently do is when data is received (on it's own thread), it is put in a queue for another thread to process in order to free the socket so it is ready and open to receive new data.

                // Enter the listening loop.
                while (true)
                {
                    Debug.WriteLine("Waiting for a connection... ");

                    // Perform a blocking call to accept requests.
                    using (client = server.AcceptTcpClient())
                    {
                        data = new List<byte>();

                        // Get a stream object for reading and writing
                        using (NetworkStream stream = client.GetStream())
                        {
                            // Loop to receive all the data sent by the client.
                            int length;

                            while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                var copy = new byte[length];
                                Array.Copy(bytes, 0, copy, 0, length);
                                data.AddRange(copy);
                            }
                        }
                    }

                    receivedQueue.Add(data);
                }

但是我想知道是否有更好的方法来做到这一点.例如,如果有 10 个客户端,并且他们都想同时向服务器发送数据,则其中一个会通过,而所有其他客户端都将失败.或者如果一个客户端的连接速度很慢并占用了套接字,则所有其他通信都将停止.

However I wanted to find out if there is a better way to do this. For example if there are 10 clients and they all want to send data to the server at the same time, one will get through while all the others will fail.Or if one client has a slow connection and hogs the socket all other communication will halt.

有没有什么办法可以同时接收所有客户端的数据,并在下载完成后将接收到的数据加入队列进行处理?

Is there not some way to be able to receive data from all clients at the same time and add the received data in the queue for processing when it has finished downloading?

推荐答案

所以这里有一个可以帮助您入门的答案 - 比我的 博文.

So here is an answer that will get you started - which is more beginner level than my blog post.

.Net 有一个围绕 Begin* 和 End* 调用的异步模式.例如 - BeginReceiveEndReceive.他们几乎总是有他们的非异步对应物(在这种情况下Receive);并实现完全相同的目标.

.Net has an async pattern that revolves around a Begin* and End* call. For instance - BeginReceive and EndReceive. They nearly always have their non-async counterpart (in this case Receive); and achieve the exact same goal.

要记住的最重要的事情是套接字的作用不仅仅是使调用异步 - 它们公开了称为 IOCP(IO 完成端口,Linux/Mono 有这两个但我忘记了名称)的东西,这对于在服务器上使用;IOCP 的关键在于您的应用程序在等待数据时不消耗线程.

The most important thing to remember is that the socket ones do more than just make the call async - they expose something called IOCP (IO Completion Ports, Linux/Mono has these two but I forget the name) which is extremely important to use on a server; the crux of what IOCP does is that your application doesn't consume a thread while it waits for data.

如何使用开始/结束模式

每个 Begin* 方法与它的非异步对应方法相比,将有 2 个以上的参数.第一个是 AsyncCallback,第二个是一个对象.这两个的意思是,这是一个完成后调用的方法"和这是我需要在该方法中使用的一些数据".被调用的方法总是具有相同的签名,在这个方法中,你调用 End* 对应的方法来获得如果你同步完成它会得到的结果.例如:

Every Begin* method will have exactly 2 more arguments in comparisson to it's non-async counterpart. The first is an AsyncCallback, the second is an object. What these two mean is, "here is a method to call when you are done" and "here is some data I need inside that method." The method that gets called always has the same signature, inside this method you call the End* counterpart to get what would have been the result if you had done it synchronously. So for example:

private void BeginReceiveBuffer()
{
   _socket.BeginReceive(buffer, 0, buffer.Length, BufferEndReceive, buffer);
}

private void EndReceiveBuffer(IAsyncResult state)
{
   var buffer = (byte[])state.AsyncState; // This is the last parameter.
   var length = _socket.EndReceive(state); // This is the return value of the method call.
   DataReceived(buffer, 0, length); // Do something with the data.
}

这里发生的是 .Net 开始等待来自套接字的数据,一旦它获得数据,它就会调用 EndReceiveBuffer 并传递自定义数据"(在这种情况下为 buffer) 通过 state.AsyncResult 传递给它.当您调用 EndReceive 时,它会返回接收到的数据的长度(如果出现故障,则抛出异常).

What happens here is .Net starts waiting for data from the socket, as soon as it gets data it calls EndReceiveBuffer and passes through the 'custom data' (in this case buffer) to it via state.AsyncResult. When you call EndReceive it will give you back the length of the data that was received (or throw an exception if something failed).

更好的套接字模式

此表单将为您提供中央错误处理 - 它可以在异步模式包装类似流的事物"的任何地方使用(例如 TCP 按照发送的顺序到达,因此可以将其视为 Stream 对象).

This form will give you central error handling - it can be used anywhere where the async pattern wraps a stream-like 'thing' (e.g. TCP arrives in the order it was sent, so it could be seen as a Stream object).

private Socket _socket;
private ArraySegment<byte> _buffer;
public void StartReceive()
{
    ReceiveAsyncLoop(null);
}

// Note that this method is not guaranteed (in fact
// unlikely) to remain on a single thread across
// async invocations.
private void ReceiveAsyncLoop(IAsyncResult result)
{
    try
    {
        // This only gets called once - via StartReceive()
        if (result != null)
        {
            int numberOfBytesRead = _socket.EndReceive(result);
            if(numberOfBytesRead == 0)
            {
                OnDisconnected(null); // 'null' being the exception. The client disconnected normally in this case.
                return;
            }

            var newSegment = new ArraySegment<byte>(_buffer.Array, _buffer.Offset, numberOfBytesRead);
            // This method needs its own error handling. Don't let it throw exceptions unless you
            // want to disconnect the client.
            OnDataReceived(newSegment);
        }

        // Because of this method call, it's as though we are creating a 'while' loop.
        // However this is called an async loop, but you can see it the same way.
        _socket.BeginReceive(_buffer.Array, _buffer.Offset, _buffer.Count, SocketFlags.None, ReceiveAsyncLoop, null);
    }
    catch (Exception ex)
    {
        // Socket error handling here.
    }
}

接受多个连接

您通常做的是编写一个包含您的套接字等(以及您的异步循环)的类,并为每个客户端创建一个.例如:

What you generally do is write a class that contains your socket etc. (as well as your async loop) and create one for each client. So for instance:

public class InboundConnection
{
    private Socket _socket;
    private ArraySegment<byte> _buffer;

    public InboundConnection(Socket clientSocket)
    {
        _socket = clientSocket;
        _buffer = new ArraySegment<byte>(new byte[4096], 0, 4096);
        StartReceive(); // Start the read async loop.
    }

    private void StartReceive() ...
    private void ReceiveAsyncLoop() ...
    private void OnDataReceived() ...
}

您的服务器类应该跟踪每个客户端连接(这样您就可以在服务器关闭时干净地断开它们的连接,以及搜索/查找它们).

Each client connection should be tracked by your server class (so that you can disconnect them cleanly when the server shuts down, as well as search/look them up).

这篇关于接受多个 tcp 客户端的最佳方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆