简体   繁体   English

在完成端口中调用WSASend()?

[英]Calling WSASend() in completion port?

Many of you know the original "send()" will not write to the wire the amount of bytes you ask it to. 你们中的许多人都知道原来的“send()”不会写入你要求它的字节数。 Easily you can use a pointer and a loop to make sure your data is all sent. 您可以轻松地使用指针和循环来确保所有数据都已发送。

However, I don't see how in WSASend() and completion ports work in this case. 但是,我没有看到WSA​​Send()和完成端口在这种情况下是如何工作的。 It returns immediately and you have no control over how much was sent (except in a lpLength which you have access in the routine). 它会立即返回,您无法控制发送的数量(除了您在例程中有权访问的lpLength)。 How does this get solved? 这是如何解决的?

Do you have to call WSASend() in the routine multiple times in order the get all the data out? 您是否需要多次在例程中调用WSASend()以获取所有数据? Doesn't this seem like a great disadvantage, especially if you want your data out in a particular order and multiple threads access the routines? 这看起来不是一个很大的缺点,特别是如果您希望以特定顺序输出数据并且多个线程访问例程?

When you call WSASend with a socket that is associated with an IOCP and an OVERLAPPED structure you effectively pass off your data to the network stack to send. 当您使用与IOCPOVERLAPPED结构关联的套接字调用WSASend ,您实际上会将数据传递到网络堆栈以进行发送。 The network stack will give you a "completion" once the data buffer that you used is no longer required by the network stack. 一旦您使用的数据缓冲区不再需要网络堆栈,网络堆栈将为您提供“完成”。 At that point you are free to reuse or release the memory used for your data buffer. 此时,您可以自由地重用或释放用于数据缓冲区的内存。

Note that the data is unlikely to have reached the peer at the point the completion is generated and the generation of the completion means nothing more than the network stack has taken ownership of the contents of the buffer. 请注意,在生成完成时,数据不太可能已到达对等体,并且完成的生成仅意味着网络堆栈已取得缓冲区内容的所有权。

This is different to how send operates. 这与send操作的方式不同。 With send in blocking mode the call to send will block until the network stack has used all of the data that you have supplied. send阻塞模式下,对send的调用将阻塞,直到网络堆栈使用了您提供的所有数据。 For calls to send in non-blocking mode the network stack takes as much data as it can from your buffer and then returns to you with details of how much it used; 对于以非阻塞模式send调用,网络堆栈从缓冲区中获取尽可能多的数据,然后返回给您,详细说明它使用了多少; this means that some of your data has been used. 这意味着您的一些数据已被使用。 With WSASend , generally, all of your data is used before you are notified. 通常,在通知WSASend之前,您会使用所有数据。

It's possible for an overlapped WSASend to fail due to resource limits or network errors. 由于资源限制或网络错误,重叠的WSASend可能会失败。 It's unusual to get a failure which indicates that some data has been send but not all. 获得失败表示某些数据已发送但并非全部发送,这是不寻常的。 Usually it's all sent OK or none sent at all. 通常它都发送好或根本没发送。 However it IS possible to get a completion with an error which indicates that some data has been used but not all. 但是,可能会出现错误,表示已使用某些数据,但并非全部。 How you proceed from this point depends on the error (temporary resource limit or hard network fault) and how many other WSASend s you have pending on that socket (zero or non-zero). 从这一点开始如何进行取决于错误(临时资源限制或硬网络故障)以及在该套接字上有多少其他WSASend待处理(零或非零)。 You can only try and send the rest of the data if you have a temporary resource error and no other outstanding WSASend calls for this socket; 如果您有临时资源错误而没有其他未完成的WSASend调用此套接字,则只能尝试发送其余数据; and this is made more complicated by the fact that you don't know when the temporary resource limit situation will pass... If you ever have a temporary resource limit induced partial send and you DO have other WSASend calls pending then you should probably abort the connection as you may have garbled your data stream by sending part of the buffer from this WSASend call and then all (or part) of a subsequent WSASend call. 由于您不知道临时资源限制情况何时会通过,这会变得更加复杂...如果您有一个临时资源限制导致部分发送而您还有其他WSASend呼叫待处理,那么您应该中止连接,因为您可能通过从此WSASend调用发送部分缓冲区然后发送后续WSASend调用的所有(或部分)来使您的数据流乱码。

Note that it's a) useful and b) efficient to have multiple WSASend calls outstanding on a socket. 请注意,a)有用且b)在套接字上具有多个未完成的WSASend调用是有效的。 It's the only way to keep the connection fully utilised. 这是保持连接充分利用的唯一方法。 You should, however, be aware of the memory and resource usage implications of having multiple overlapped WSASend calls pending at one time (see here ) as effectively you are handing control of the lifetime of your buffers (and thus the amount of memory and resources that your code uses) to the peer due to TCP flow control issues). 但是,您应该知道一次有多个重叠的WSASend调用挂起的内存和资源使用含义(请参阅此处 ),因为您正在处理缓冲区生命周期的控制(以及因此内存和资源的数量)由于TCP流控制问题,您的代码使用)到对等方。 See SIO_IDEAL_SEND_BACKLOG_QUERY and SIO_IDEAL_SEND_BACKLOG_CHANGE if you want to get really clever... 如果你想变得非常聪明,请参阅SIO_IDEAL_SEND_BACKLOG_QUERYSIO_IDEAL_SEND_BACKLOG_CHANGE ......

WSASend() on a completion port does not notify you until all of the requested data has been accepted by the socket, or until an error occurs, whichever happens first. 完成端口上的WSASend()不会通知您,直到套接字接受了所有请求的数据,或者直到发生错误为止,以先发生者为准。 It keeps working in the background until all of the data has been accepted (or errored). 它在后台继续工作,直到所有数据都被接受(或错误)。 Until it notifies you, that buffer has to remain active in memory, but your code is free to move on to do other things while WSASend() is busy. 在它通知您之前,该缓冲区必须在内存中保持活动状态,但是当WSASend()忙时,您的代码可以继续执行其他操作。 There is no notification when the data is actually transmitted to the peer. 数据实际传输到对等方时没有通知。 IF you need that, then you have to implement an ACK in your data protocol so the peer can notify you when it receives the data. 如果您需要,那么您必须在数据协议中实现ACK,以便对等方在收到数据时通知您。

First regarding send . 首先关于send Actually there may happen 2 different things, depending on how the socket is configured. 实际上可能会发生两种不同的事情,具体取决于套接字的配置方式。

If socket is in so-called blocking mode (the default) - the call to send will block the calling thread, until all the input buffer is consumed by the underlying network driver. 如果socket处于所谓的阻塞模式(默认) - 对send的调用将阻塞调用线程,直到底层网络驱动程序使用所有输入缓冲区。 (Note that this doesn't mean that the data has already arrived at the peer). (请注意,这并不意味着数据已经到达对等方)。

If the socket is transferred to a non-blocking mode - the call to send will fail if the underlying driver may not consume all the input immediately. 如果套接字转移到非阻塞模式 - 如果底层驱动程序可能不立即消耗所有输入,则调用send失败 The GetLastError returns WSAEWOULDBLOCK in such a case. 在这种情况下, GetLastError返回WSAEWOULDBLOCK The application should wait until it may retry to send. 应用程序应该等到它可能重试发送。 Instead of calling send in a loop the application should get the notification from the system about the socket state change. 应用程序应该从系统获得有关套接字状态更改的通知,而不是在循环中调用send Functions such as WSAEventSelect or WSAAsyncSelect may be used for this (as well as legacy select ). 诸如WSAEventSelectWSAAsyncSelect函数可用于此(以及遗留select )。

Now, with I/O completion ports and WSASend the story is somewhat different. 现在,有了I / O完成端口和WSASend ,故事有些不同。 When the socket is associated with the completion port - it's automatically transferred to a non-blocking mode. 当套接字与完成端口关联时 - 它会自动转移到非阻塞模式。

If the call to WSASend can't be completed immediately (ie the network driver can't consume all the input) - the WSASend returns an error and GetLastError returns STATUS_PENDING . 如果无法立即完成对WSASend的调用(即网络驱动程序无法使用所有输入) - WSASend返回错误, GetLastError返回STATUS_PENDING This actually means that an asynchronous operation has started but not finished yet**. 这实际上意味着异步操作已经开始但尚未完成**。

That is, you should not call WSASend repeatedly, because the send operation is already in the progress. 也就是说,您不应该重复调用WSASend ,因为发送操作已经在进行中。 When it's finished (either successfully or not) you'll get the notification on the I/O completion port, but meanwhile the calling thread is free to do other things. 当它完成(成功与否)时,您将在I / O完成端口上获得通知,但同时调用线程可以自由地执行其他操作。

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

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