使用完成端口来编写的一个程序,使用WSASend来投递数据,返回值表示已经成功了。
在完成端口的服务线程中,GetQueuedCompletionStatus结果为False,使用WSAGetLastError查到的错误是6,搜了一下,其中有个回答是OVERLAPPED 里的hEvent无效,但我已经用WSACreateEvent赋值了。  str := Edit1.Text + chr($D);  new(PerIoData);
  for k:= 1 to Length(str) do
    PerIoData.Buffer[k-1] := str[k];  ZeroMemory(@(PerIoData.Overlapped), sizeof(OVERLAPPED));
  PerIoData.Overlapped.hEvent := WSACreateEvent();  PerIoData.DataBuf.buf := @PerIoData.Buffer;
  PerIoData.DataBuf.len := DATA_BUFSIZE;//length(str);
  PerIoData.IOType := ioWrite;
  if (WSASend(m_socket, @(PerIoData.DataBuf), 1, @SendBytes, 0,  @(PerIoData.Overlapped), nil) = SOCKET_ERROR) then
  begin
    if (WSAGetLastError() <> ERROR_IO_PENDING) then
    begin
      self.Memo1.Lines.Add('WSASend() failed with error');
    end;
  end;LPPER_IO_OPERATION_DATA = ^ PER_IO_OPERATION_DATA ;
  PER_IO_OPERATION_DATA = packed record
    Overlapped: WSAOVERLAPPED;
    DataBuf: TWSABUF;
    Buffer: array [0..DATA_BUFSIZE] of CHAR;
    BytesSEND: DWORD;
    BytesRECV: DWORD;    IOType :TIOType;
  end;

解决方案 »

  1.   

    使用GetQueuedCompletionStatus时,是不需要hEvent的.
      

  2.   

    另外,错误码实际上存于线程局部存储当中的,当操作成功时,错误码有可能不会被重置,甚至还有可能影响正常的作业,为防止线程内错误码被缓存而影响正常的作业,在线程调用GetQueuedCompletionStatus前最好使用SetLastError(0)
      

  3.   

    把GetQueuedCompletionStatus相关代码贴上。
      

  4.   

    在完成端口的服务线程里边调用GetQueuedCompletionStatus的时候,没有数据的时候,GetQueuedCompletionStatus也不会阻塞,这种情况是不是错的?
    我跟踪了几次,GetQueuedCompletionStatus每次的返回了false,根本就没有成功过,这令我很困惑。
      

  5.   

    while not Terminated do
     begin
        getData := GetQueuedCompletionStatus(m_hCompletionPort, BytesTransferred,
                     DWORD(PerHandleData), POverlapped(PerIoData), INFINITE);    if getData then             
           ....
        else
           ...endgetData永远都返回False,PerIoData值为nil。不知道是不是WSASend的时候,PerIoData内存没有分配到堆上,还是什么别的原因。
      

  6.   

    调试看一下m_hCompletionPort是否正确。
      

  7.   

    谢谢两位,真的是m_hCompletionPort的初始化没有成功,两位太厉害了。
    我一开始是这样初始化的,CreateIoCompletionPort(INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0),因为第二个参数ExistingCompletionPort: THandle的类型是个句柄值,所以我就传了一个NVALID_HANDLE_VALUE。把这个参数传个0就正常了,就像下边这样:
            CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);因为这个过程挺简单的,我都没有想到去检查这个地方。
    再次感谢两位,帮了我大忙了。
      

  8.   

    不过在完成端口句柄无效的情况下,GetQueuedCompletionStatus无法阻塞线程,这个问题挺奇怪的。