哪种线程方法最适合主服务器网络?
遵循这个问题,我决定从更具体的方法开始,而不是被抛出我无法整合的理论和概念信息:Socket.*Async 方法是线程化的吗?
重点是在优化服务器的同时保持所有客户端的流动性。这意味着以某种方式异步,以免阻塞主要操作。
以下是我想出的一些方法。 “处理”是一种假设的方法,用于处理从客户端接收到的数据。考虑到这通常需要 1-5 毫秒,对于罕见数据库调用可能需要 500-2000 毫秒。
使用 Socket.*异步和循环
static void Main()
{
Socket listener = new Socket(...);
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
List<Socket> clients = new List<Socket>();
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
while (true)
{
if (listener.AcceptAsync(e))
{
clients.Add(e.AcceptSocket);
}
foreach (Socket client in clients)
{
if (client.ReceiveAsync(e))
{
Process(e.Buffer);
}
}
}
}
优点:
- 只有一个线程!
- 管理起来相当简单。
缺点:
- while(true):CPU 消耗太大?
- 由于所有操作都相互跟随,因此它们会减慢所有连接的客户端的速度。
我认为这在某种程度上是一个好的开始,也许是我最好的解决方案。如果我可以混合在一个线程池中,将接受、接收和进程拆分到不同的线程中,我们可能会去某个地方。
使用 Socket.Begin*/End* 和 ManualResetEvent
static class Server
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static ManualResetEvent acceptDone = new ManualResetEvent(false);
static void Main()
{
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
while (true)
{
acceptDone.Reset();
listener.BeginAccept(OnAccept, null);
acceptDone.WaitOne();
}
}
private static void OnAccept(IAsyncResult ar)
{
acceptDone.Set();
new Receiver(listener.EndAccept(ar));
}
}
class Receiver
{
Socket socket;
byte[] buffer = new byte[1024];
static ManualResetEvent receiveDone = new ManualResetEvent(false);
public Receiver(Socket socket)
{
this.socket = socket;
new Thread
(
delegate()
{
while (true)
{
receiveDone.Reset();
socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, OnReceive, null);
receiveDone.WaitOne();
}
}
).Start();
}
private void OnReceive(IAsyncResult ar)
{
receiveDone.Set();
int received = socket.EndReceive(ar);
byte[] toProcess = new byte[received];
Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
Process(toProcess);
}
}
优点:
- 完全异步,客户端不会因其他操作而减慢速度。
- 使用 ManualResetEvents 可以很好地停止服务器。
缺点:
- 线程太多,每个客户端 1 个!
- 由于所有线程都被重置事件阻塞,因此浪费了处理时间?
最后是一种简化此解决方案的方法,无需手动重置事件。
使用阻塞调用和手动线程
static class Server
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main()
{
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
while (true)
{
new Receiver(listener.Accept());
}
}
}
class Receiver
{
Socket socket;
public Receiver(Socket socket)
{
this.socket = socket;
new Thread
(
delegate()
{
while (true)
{
byte[] buffer = new byte[1024];
int received = socket.Receive(buffer);
byte[] toProcess = new byte[received];
Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
Process(toProcess);
}
}
).Start();
}
}
使用线程池
我实际上不知道如何使用它,有人能给我一个例子吗?
建议
解决方案可能不是本文中的任何一个。你会怎么处理?
正如您所看到的,我使用了 .*Async 方法、Begin*/End* 方法和阻塞方法,但都有相对较大的缺点。
预先感谢:) 我迫不及待地想看到 S/O 的代码示例。
Following this question, I decided to start with a more concrete approach, rather than being thrown theoretical and conceptual information I cannot integrate: Are Socket.*Async methods threaded?
The point is to keep fluidity for all the clients, while optimizing the server. This means asynchrony in a way or another, in order not to block the main operation.
Here are some methods I came up with. "Process" is an hypothetical method that handles the data received from the client. Consider this could take between 1-5ms usually, perhaps 500-2000ms for rare database calls.
Using Socket.*Async and looping
static void Main()
{
Socket listener = new Socket(...);
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
List<Socket> clients = new List<Socket>();
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
while (true)
{
if (listener.AcceptAsync(e))
{
clients.Add(e.AcceptSocket);
}
foreach (Socket client in clients)
{
if (client.ReceiveAsync(e))
{
Process(e.Buffer);
}
}
}
}
Pros:
- One thread only!
- Rather simple to manage.
Cons:
- While(true): Too CPU intensive?
- Since all ops are following each other, they slow down all clients connected.
I assume this is somehow a good start, perhaps the best of my solutions. If I could mix in a thread pool, split the accept, the receive, and the process in different threads, we might be going somewhere.
Using Socket.Begin*/End* and ManualResetEvent
static class Server
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static ManualResetEvent acceptDone = new ManualResetEvent(false);
static void Main()
{
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
while (true)
{
acceptDone.Reset();
listener.BeginAccept(OnAccept, null);
acceptDone.WaitOne();
}
}
private static void OnAccept(IAsyncResult ar)
{
acceptDone.Set();
new Receiver(listener.EndAccept(ar));
}
}
class Receiver
{
Socket socket;
byte[] buffer = new byte[1024];
static ManualResetEvent receiveDone = new ManualResetEvent(false);
public Receiver(Socket socket)
{
this.socket = socket;
new Thread
(
delegate()
{
while (true)
{
receiveDone.Reset();
socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, OnReceive, null);
receiveDone.WaitOne();
}
}
).Start();
}
private void OnReceive(IAsyncResult ar)
{
receiveDone.Set();
int received = socket.EndReceive(ar);
byte[] toProcess = new byte[received];
Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
Process(toProcess);
}
}
Pros:
- Fully asynchronous, clients never slowed down by other operations.
- Using ManualResetEvents allows to stop the server nicely.
Cons:
- Way too many threads, 1 per client!
- Wasted processing time since all threads are blocked with reset events?
And finally a way of simplifying this solution, without manual reset events.
Using Blocking Calls and manual threads
static class Server
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main()
{
listener.Bind(new IPEndPoint(IPAddress.Any, 555));
while (true)
{
new Receiver(listener.Accept());
}
}
}
class Receiver
{
Socket socket;
public Receiver(Socket socket)
{
this.socket = socket;
new Thread
(
delegate()
{
while (true)
{
byte[] buffer = new byte[1024];
int received = socket.Receive(buffer);
byte[] toProcess = new byte[received];
Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
Process(toProcess);
}
}
).Start();
}
}
Using a Thread Pool
I actually have no idea how to use one, could someone give me an example for it?
Suggestions
Probably that the solution isn't any of the ones in this post. How would you handle it?
As you can see, I used .*Async methods, Begin*/End* methods, and blocking methods, but all have relatively major cons.
Thanks in advance :) I can't wait to see S/O's code examples.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您没有正确使用
Begin/End
。无需等待事件,让框架来处理。请注意,在 MSDN 示例中,接收循环不使用显式事件,尽管接受循环是为了便于控制和说明。You are not using
Begin/End
correctly. There is no need to wait on the events, let the framework handle this. Note that in the MSDN example the receive loop doesn't use an explicit event, although the accept loop does for ease of control and exposition.