简体   繁体   English

是否可以为多个客户端创建单个套接字,单线程,TCP / IP数据包读取器?

[英]Is it possible to create a single socket, single thread, TCP/IP packet reader for multiple clients?

A typical server application would create a socket for each incoming connection spawn a new thread. 典型的服务器应用程序将为每个传入的连接创建一个套接字,以产生一个新线程。

However is it possible to do the demuxing yourself in a single thread? 但是,是否可以在单个线程中对自己进行脱胶? What I want is that the application keeps a state machine for each end point which deserializes message as data becomes available in a single thread. 我想要的是应用程序为每个端点保留一个状态机,当数据在单个线程中可用时,该状态机反序列化消息。

Threading is the simplest, and I have a working implementation of that. 线程化是最简单的,我有一个可行的实现。 However each thread adds overhead fairly quickly, and lingering connections will require resources. 但是,每个线程都相当快地增加了开销,缠绵的连接将需要资源。 What I want is that instead for each connection a state machine builds up a message (which the threaded code already does anyway) and upon completion dispatches the deserialized message to a worker queue. 我想要的是,对于每个连接,状态机建立一条消息(线程代码无论如何都已执行该消息),并在完成后将反序列化的消息分发给工作人员队列。

Is this possible in .NET? .NET有可能吗? P/Invoke would also be an acceptable solution. P / Invoke也将是一个可接受的解决方案。

A typical server application would create a socket for each incoming connection spawn a new thread. 典型的服务器应用程序将为每个传入的连接创建一个套接字,以产生一个新线程。

That is not a true statement. 那不是一个真实的陈述。 Very few servers — only those which need not scale to large numbers of connected clients, and not even all of those — will dedicate an entire thread to a single connection. 很少有服务器(只有那些不需要扩展到大量已连接客户端的服务器,甚至没有全部都可以)将整个线程专用于单个连接。

I would say only the most rudimentary servers would use a thread-per-connection design. 我会说,只有最基本的服务器才会使用“每个连接线程”设计。 I suppose that since there are a lot more experimental servers (ie hobbyists learning to write network code) than production servers, the sheer numbers might be on the side of thread-per-connection. 我猜想,因为生产服务器比实验服务器多得多(例如,业余爱好者正在学习编写网络代码),所以纯粹的数量可能在每个连接线程的一边。

But IMHO only production servers are really relevant to the question, as they show what good design and implementation would be. 但是,恕我直言,只有生产服务器与该问题确实相关,因为它们显示出良好的设计和实现。 And for those, thread-per-connection is definitely going to be in the minority. 对于这些,每连接线程绝对将占少数。

However is it possible to do the demuxing yourself in a single thread? 但是,是否可以在单个线程中对自己进行脱胶?

Using Socket.Select() one can have a single thread dedicated to the handling of several sockets. 使用Socket.Select()可以使一个线程专用于处理多个套接字。

However, more typical would be to use one of the several asynchronous programming APIs available for use with sockets, which uses I/O completion ports to farm out handling of I/O operations to a pool of threads dedicated for the purpose. 但是,更典型的方法是使用可用于套接字的几种异步编程API之一,该API使用I / O完成端口将I / O操作的处理工作分配给专用的线程池。 This allows concurrency but efficient use of the threads to minimize context-switching. 这允许并发但有效使用线程以最小化上下文切换。

My preferred approach with the current .NET 4.5 and C# 5.0 features is to wrap the socket in an instance of NetworkStream , so that I can use the ReadAsync() and WriteAsync() methods, which in turn allow the use of await in the C# code. 使用当前.NET 4.5和C#5.0当前功能的首选方法是将套接字包装在NetworkStream的实例中,以便可以使用ReadAsync()WriteAsync()方法,从而允许在C#中使用await码。 This makes the asynchronous code a lot easier to read and implement. 这使得异步代码更易于阅读和实现。

But you can use eg Socket.BeginReceive() or Socket.ReceiveAsync() (the latter being useful for servers that require extremely high scalability…even the former is still much more scalable than thread-per-connection though) just as well. 但是,您也可以使用Socket.BeginReceive()Socket.ReceiveAsync() (后者对于需要极高可伸缩性的服务器很有用……尽管前者仍然比每个连接线程具有更高的可伸缩性)。

Regardless of API used, the typical server implementation will include a state object class for each connection. 无论使用哪种API,典型的服务器实现都将为每个连接包括一个状态对象类。 This can be done with thread-per-connection design as easily as with the async I/O designs. 这可以通过每个连接线程设计来完成,就像使用异步I / O设计一样容易。 Your state machine would reside in that state object class, so that the processing of I/O operations on the socket can use the state machine. 您的状态机将驻留在该状态对象类中,以便套接字上的I / O操作处理可以使用状态机。

Use the Socket.Select method. 使用Socket.Select方法。

When you call the method, you pass it three lists of sockets. 调用该方法时,会向其传递三个套接字列表。 One is the list of sockets you're waiting for reads, one is a list you're waiting for writes (if the write buffer is full you will have to wait to write), and one is a list you're waiting for errors. 一个是您正在等待读取的套接字列表,一个是您正在等待写入的列表(如果写缓冲区已满,您将不得不等待写入),另一个是您正在等待错误的列表。 You also tell it the maximum amount of time to wait, and it will block that long until one of the sockets is ready. 您还告诉它等待的最长时间,它会阻塞很长的时间,直到其中一个插槽就绪为止。 When the function returns, it will modify the lists to remove the sockets that aren't ready. 函数返回时,它将修改列表以删除尚未准备好的套接字。

When you create your read array, put the master/mother socket in so that you will know when there's a new connection. 创建读取阵列时,请插入主/母插槽,以便在有新连接时知道。 Also put in any existing connections. 还放置任何现有连接。 Put all of those in the error array also. 也将所有这些都放在错误数组中。 For the write array, you only need to put sockets in when the write buffer is full. 对于写阵列,只需要在写缓冲区已满时放入套接字。 Probably this is something you can ignore in your first iteration, but it will be important when you start putting serious traffic through. 也许这是您在第一次迭代中可以忽略的事情,但是当您开始通过大量流量时,这一点很重要。

When Socket.Select() returns, you need only loop through the sockets remaining in the read list and process the data as your server requires. Socket.Select()返回时,您只需要遍历读取列表中剩余的套接字并按照服务器的要求处理数据。 Loop through the writes and push out any remaining queued data. 循环进行写操作并推出所有剩余的排队数据。 Loop through the errors and close those sockets or handle errors. 遍历错误并关闭那些套接字或处理错误。 Remember to put the idle sockets back in the list before you call select again! 在再次调用select之前,请记住将空闲的套接字放回列表中!

Absolutely, the trick is to use asynchorous IO (ie BeginAccept, EndAccept, BeginRead, EndRead, BeginWrite, and EndWrite). 绝对,诀窍是使用异步IO(即BeginAccept,EndAccept,BeginRead,EndRead,BeginWrite和EndWrite)。 This allows you to handle multiple clients without the need of threads. 这使您无需线程即可处理多个客户端。 This is the way that react works. 这就是反应的方式。

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

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