编写的一个程序,C/S模式,server完成端口。
client发送登录信令,server接收并发送回复信令,然后client接收回复信令并发送用户信息信令,这一步server没有接收到数据。问题一:
是否在每次server接收完数据后都要清空接收缓冲区??也就是要清空单操作数据??原来代码太复杂,但主要与以下代码相似,而client端的代码是我自己写来测试的
问题二:
server中的 
if (PerIoData->BytesRECV > PerIoData->BytesSEND)判断有什么用,能说说吗?情况1,server中else注释掉,client中sleep不注释,server端接收没问题。
情况2,server中else注释掉,client中sleep注释,server端接收有问题,只受到第一个数据。
情况3,server中else不注释,client中sleep注释,server端接收没问题。
《windows网络编程》中的代码修改如下:server中的线程函数:
THREAD_PRO IOCompletionThd(LPVOID CompletionPortID)
{

   HANDLE CompletionPort = (HANDLE) CompletionPortID;
   DWORD BytesTransferred;
   LPOVERLAPPED Overlapped;
   LPPER_HANDLE_DATA PerHandleData;
   LPPER_IO_OPERATION_DATA PerIoData;
   DWORD SendBytes, RecvBytes;
   DWORD Flags;
   
   while(TRUE)
   {
  if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
         (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
      {
;//error
  }      if (BytesTransferred == 0)
      {
;//error
if (::closesocket(PerHandleData->Socket) == SOCKET_ERROR)
         {
;//error
 }         GlobalFree(PerHandleData);
         GlobalFree(PerIoData);
      
 continue;
      }      // Check to see if the BytesRECV field equals zero. If this is so, then
      // this means a WSARecv call just completed so update the BytesRECV field
      // with the BytesTransferred value from the completed WSARecv() call.      if (PerIoData->BytesRECV == 0)
      {
         PerIoData->BytesRECV = BytesTransferred;
         PerIoData->BytesSEND = 0;
      }
      else
      {
         PerIoData->BytesSEND += BytesTransferred;
      }      if (PerIoData->BytesRECV > PerIoData->BytesSEND)
      {         // Post another WSASend() request.
         // Since WSASend() is not gauranteed to send all of the bytes requested,
         // continue posting WSASend() calls until all received bytes are sent.
printf("recv %s\n",PerIoData->DataBuf.buf);
  

         ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));         PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
         PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;         if (!CSock::Send(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
            &(PerIoData->Overlapped)))
         {
            ;//error
         }
 
 
      }
  /*   else这段注释与不注释结果不一样
      else
      {
         PerIoData->BytesRECV = 0;         // Now that there are no more bytes to send post another WSARecv() request.         Flags = 0;
         ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));         PerIoData->DataBuf.len = DATA_BUFSIZE;
         PerIoData->DataBuf.buf = PerIoData->Buffer;         if (!CSock::Recv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
            &(PerIoData->Overlapped)))
         {
 ;//error
         }
      }
*/   
   }
   
return 0;
}client的线程函数中的一个循环发送代码段:
                  for(i=0;i<dwCount;i++)
{
ret=::send(sClient,szMessage,sizeof(szMessage),0);

if(ret==0)
break;
else if(ret==SOCKET_ERROR)
{
cout << "send failed " << ::GetLastError() << endl;
break;
}
cout << "send " << ret << "bytes.\n";
// ::Sleep(1000);//sleep注释与否代码不一样
}

解决方案 »

  1.   


    1.主要看你接收的数据做啥.如果只是接收数据,然后啥都不用做了,那自然清空没问题,但如果要保存到文件呀或做其它操作,你得完成这些操作才能清空.
    2.if (PerIoData->BytesRECV > PerIoData->BytesSEND)判断有什么用,能说说吗? 
    那是接收到的数据长度大于发送出去的,那就是接收到的数据还没发送完,还得继续发.
      

  2.   

    就是不知server第一次能接收,第二次就不能接收是什么问题
      

  3.   


    你的代码,应该是接收到数据后,然后再发送出去.但发送完后,你没有继续有WSARecv()的投递,自然不可能再接收到数据.上面注释掉的代码,就是检查已发送完,然后清空buff,继续再接收的代码.
      

  4.   

    是的.必须要清空,否则无法再接受新数据.
    WSARecv SWASend 才能继续触发完成端口的工作者线程.
      

  5.   


    清空buff只是一个好的habit,buff不清空一样可以投递进去接数据.
      

  6.   


    清空buff只是一个好的habit,buff不清空一样可以投递进去接数据.
      

  7.   

    你的client循环发送时候出现了wsaewouldblock异常,你sleep注释后只能发一条数据就是证明
    client是什么网络模型?
    还有你完成端口 LPPER_IO_OPERATION_DATA结构帖出来
      

  8.   

    就我理解,缓冲区不能清,因为如果应该程序接受完数据后,buff会自动清空,如果没有接受完,新来的数据是接在原来数据后存放在buff中的,这个时候你清空恐怕会有问题吧
      

  9.   

    //单io操作数据
    typedef struct
    {
       OVERLAPPED Overlapped;
       WSABUF DataBuf;
       CHAR Buffer[TAGSIZE];
       DWORD BytesSEND;
       DWORD BytesRECV;
    } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
    //单句柄数据
    typedef struct 
    {
       SOCKET Socket;
    } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
      

  10.   

    我还看得不是很清楚,不过大致好像
    if (PerIoData->BytesRECV > PerIoData->BytesSEND)以及下面的else与包粘连有关,好像是解决包粘连问题的
      

  11.   


    抱歉,搞错~!
    明白你的程序是什么意思了,就是把所有收到的数据全部转发出去~!
    那么它是通过BytesRECV 和BytesSEND的大小来决定下一步的io操作的,这两个值之全程累加的
    如果收大于发,那就该send出去
    否则就该继续recv了
      

  12.   

    现在有个新问题server还是完成端口,client变为异步select。
    server的工作线程在执行GetQueuedCompletionStatus后取得995错误码,怎么解决,上网搜过,得知可能是socket销毁引起,但是检查代码没错阿。请教大家!!!!