我有一个客户机服务器的基础架构,目前他们使用一个TcpClient和TcpListener在所有客户机和服务器之间发送接收数据。目前他们使用一个TcpClient和TcpListener在所有客户端和服务器之间发送和接收数据。
目前我所做的是当数据被接收时(在它自己的线程上),它被放在一个队列中供另一个线程处理,以便释放套接字,使它准备好并开放接收新数据。
// 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个客户端,他们都想在同一时间向服务器发送数据,其中一个会通过,而其他所有的客户端都会失败.或者,如果一个客户端的连接速度很慢,并且占用了socket,所有其他的通信都会停止。
有没有什么办法能够同时接收所有客户端的数据,并在下载完成后将接收到的数据添加到队列中进行处理?
因此,这里有一个答案,将让你开始 - 这是更多的初学者水平比我的。博文.
.Net有一个围绕Begin*和End*调用的异步模式。例如 BeginReceive
和 EndReceive
. 它们几乎总是有它们的非异步对应物(在本例中是 Receive
);并达到完全相同的目标。
最重要的是要记住,socket的不仅仅是让调用async--它们暴露了一种叫做IOCP(IO完成端口,LinuxMono有这两个,但我忘了名字)的东西,这在服务器上使用极为重要;IOCP的核心是你的应用程序在等待数据时不会消耗一个线程。
如何使用BeginEnd模式
每一个Begin*方法都会比它的非async方法多2个参数。第一个是AsyncCallback,第二个是一个对象。这两个参数的意思是,"这里有一个方法,当你完成后可以调用 "和 "这里有一些我在这个方法里面需要的数据"。被调用的方法总是有相同的签名,在这个方法里面,你调用End*对应的方法,得到如果你同步做的结果是什么。所以举个例子。
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
它会给你返回接收到的数据的长度(如果失败了,则抛出一个异常)。
更好的套接字模式
这个表单将为你提供集中的错误处理--它可以用在async模式包装一个类似流的 "东西 "的任何地方(例如,TCP按照发送顺序到达,所以它可以被看作是一个 "东西")。Stream
对象)。)
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.
}
}
接受多个连接
一般来说,你要做的是写一个包含你的socket等的类(以及你的async循环),并为每个客户端创建一个类。(以及你的异步循环),然后为每个客户端创建一个类。因此,比如说。
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() ...
}
每一个客户端连接都应该由你的服务器类来跟踪 (这样你就可以在服务器关闭的时候干净利落地断开连接, 也可以搜索查找它们).
你应该使用异步套接字编程来实现这一点。请看一下 例子 由MSDN提供。
你应该使用异步方法来读取数据,一个例子是。
// Enter the listening loop.
while (true)
{
Debug.WriteLine("Waiting for a connection... ");
client = server.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleTcp), client);
}
private void HandleTcp(object tcpClientObject)
{
TcpClient client = (TcpClient)tcpClientObject;
// Perform a blocking call to accept requests.
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);
}
你也应该考虑使用 AutoResetEvent
或 ManualResetEvent
以在新数据被添加到集合中时得到通知,这样处理数据的线程就会知道何时收到数据,如果你使用了 4.0
你最好不要再使用 BlockingCollection
而不是 Queue
.
我通常做的是使用一个有几个线程的线程池.在每一个新的连接,我运行连接处理(在你的情况下 - 你在使用子句中做的一切)在池中的一个线程。
通过这种方式,你可以实现两个性能,因为你允许几个同时接受的连接,你也限制了资源的数量(线程,等等),你分配给处理传入的连接。
你有一个很好的例子 此处
运气好