让客户端拉取数据

发布于 2024-07-26 21:43:54 字数 678 浏览 5 评论 0原文

我正在用 C# 编写一个服务器,它创建一个(很长,甚至可能是无限的)IEnumerable 来响应客户端请求,然后将这些结果流式传输回客户端。

我可以这样设置吗,如果客户端读取速度很慢(或者一次可能根本不读取几秒钟),服务器不需要一个线程停止等待缓冲区空间清理,以便它可以拉出接下来的几个 Result,序列化它们,然后将它们填充到网络上?

这是如何 NetworkStream.BeginWrite< /code>有效吗? 对于我来说,文档不清楚何时调用回调方法。 它是否基本上立即发生,只是在另一个线程上发生,然后在 EndWrite 上阻塞等待实际写入发生? 当套接字 API 中的某种较低级别的缓冲区下溢时,是否会发生这种情况? 当数据实际写入网络时会发生这种情况吗? 当它被承认时它会发生吗?

我很困惑,所以整个问题可能都是错误的。 如果是这样,您能否让我转身并为我指明正确的方向来解决我所期望的相当普遍的问题?

I'm writing a server in C# which creates a (long, possibly even infinite) IEnumerable<Result> in response to a client request, and then streams those results back to the client.

Can I set it up so that, if the client is reading slowly (or possibly not at all for a couple of seconds at a time), the server won't need a thread stalled waiting for buffer space to clear up so that it can pull the next couple of Results, serialize them, and stuff them onto the network?

Is this how NetworkStream.BeginWrite works? The documentation is unclear (to me) about when the callback method will be called. Does it happen basically immediately, just on another thread which then blocks on EndWrite waiting for the actual writing to happen? Does it happen when some sort of lower-level buffer in the sockets API underflows? Does it happen when the data has been actually written to the network? Does it happen when it has been acknowledged?

I'm confused, so there's a possibility that this whole question is off-base. If so, could you turn me around and point me in the right direction to solve what I'd expect is a fairly common problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

红ご颜醉 2024-08-02 21:43:54

我将更详细地回答你问题的第三部分。

MSDN 文档指出:

当您的应用程序调用 BeginWrite 时,系统使用单独的线程来执行指定的回调方法,并在 EndWrite 上阻塞,直到 NetworkStream 发送请求的字节数或引发异常。

据我了解,调用BeginSend后是否立即调用回调方法取决于底层实现和平台。 例如,如果 IO 完成端口在 Windows 上可用,则它不会。 线程池中的线程在调用之前会阻塞。

事实上,NetworkStream 的 BeginWrite 方法只是在我的 .Net 实现上调用底层套接字的 BeginSend 方法。 我的使用带有完成端口(如果可用)的底层 WSASend Winsock 函数。 这使得它比简单地为每个发送/写入操作创建自己的线程要高效得多,即使您要使用线程池也是如此。

如果 WSASend 的结果是 IOPending,则 Socket.BeginSend 方法将调用 OverlappedAsyncResult.CheckAsyncCallOverlappedResult 方法,该方法又调用本机 RegisterWaitForSingleObject Win32 函数。 这将导致线程池中的线程之一阻塞,直到 WSASend 方法发出信号表明其已完成,之后调用回调方法。

由 NetworkStream.EndSend 调用的 Socket.EndSend 方法将等待发送操作完成。 它必须这样做的原因是,如果 IO 完成端口不可用,则将立即调用回调方法。

我必须再次强调,这些细节特定于我的 .Net 实现和我的平台,但这应该会给您一些见解。

I'll answer the third part of your question in a bit more detail.

The MSDN documentation states that:

When your application calls BeginWrite, the system uses a separate thread to execute the specified callback method, and blocks on EndWrite until the NetworkStream sends the number of bytes requested or throws an exception.

As far as my understanding goes, whether or not the callback method is called immediately after calling BeginSend depends upon the underlying implementation and platform. For example, if IO completion ports are available on Windows, it won't be. A thread from the thread pool will block before calling it.

In fact, the NetworkStream's BeginWrite method simply calls the underlying socket's BeginSend method on my .Net implementation. Mine uses the underlying WSASend Winsock function with completion ports, where available. This makes it far more efficient than simply creating your own thread for each send/write operation, even if you were to use a thread pool.

The Socket.BeginSend method then calls the OverlappedAsyncResult.CheckAsyncCallOverlappedResult method if the result of WSASend was IOPending, which in turn calls the native RegisterWaitForSingleObject Win32 function. This will cause one of the threads in the thread pool to block until the WSASend method signals that it has completed, after which the callback method is called.

The Socket.EndSend method, called by NetworkStream.EndSend, will wait for the send operation to complete. The reason it has to do this is because if IO completion ports are not available then the callback method will be called straight away.

I must stress again that these details are specific to my implementation of .Net and my platform, but that should hopefully give you some insight.

留蓝 2024-08-02 21:43:54

首先,在完成其他工作时,主线程可以继续执行的唯一方法是使用另一个线程。 一个线程不能同时做两件事。

但是,我认为您想要避免的是 Thread 对象的混乱,是的,通过使用 BeginWrite 可以实现这一点。 根据你的问题

文档不清楚(对我来说)
关于回调方法何时会发生
调用。

该调用是在网络驱动程序将数据读入其缓冲区后进行的。

这基本上是立即发生的吗?
只是在另一个线程上,然后
EndWrite 上的块等待
实际的写作会发生吗?

不,直到它进入网络驱动程序处理的缓冲区为止。

当某种情况发生时,它会发生吗?
套接字 API 中的较低级别缓冲区
下溢?

如果你所说的下溢意味着它有空间,那么是的。

当数据已经存在时,是否会发生这种情况?
实际写入网络?

不。

当它发生时它会发生吗?
承认吗?

不。

编辑

我个人会尝试使用线程。 BeginWrite 在幕后做了很多你可能应该认识到的事情......而且我很奇怪,我喜欢控制我的线程。

First, the only way your main thread can keep executing while other work is being done is through the use of another thread. A thread can't do two things at once.

However, I think what you're trying to avoid is mess with the Thread object and yes that is possible through the use of BeginWrite. As per your questions

The documentation is unclear (to me)
about when the callback method will be
called.

The call is made after the network driver reads the data into it's buffers.

Does it happen basically immediately,
just on another thread which then
blocks on EndWrite waiting for the
actual writing to happen?

Nope, just until it's in the buffers handled by the network driver.

Does it happen when some sort of
lower-level buffer in the sockets API
underflows?

If by underflow your mean it has room for it then yes.

Does it happen when the data has been
actually written to the network?

No.

Does it happen when it has been
acknowledged?

No.

EDIT

Personally I would try using a Thread. There's a lot of stuff that BeginWrite is doing behind the scenes that you should probably recognize... plus I'm weird and I like controlling my threads.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文