#define MAX_BUFFER_SIZE 10240 dwFlags = 0;
dwRecvBytes = 0;
perIOData->wsDataBuf.buf = perIOData->pBuffer;
perIOData->wsDataBuf.len = MAX_BUFFER_SIZE;
ZeroMemory(&(perIOData->overLapped), sizeof(OVERLAPPED));

WSARecv(perHandleData->sock, &perIOData->wsDataBuf, 1, &dwRecvBytes, &dwFlags,&perIOData->overLapped, NULL);
我用完成端口
客户端 WSARecv  接收服务器发过来的时候 
为什么有的时候 服务器发过来的数据 perIOData->wsDataBuf.len 小于 MAX_BUFFER_SIZE;
BOOL ret = GetQueuedCompletionStatus(pThis->m_hIOCP, &dwTransferByte,
(LPDWORD)&perHandleData, (LPOVERLAPPED*)&perIOData, INFINITE);
它得到的dwTransferByte会等于10240呢? 这个是什么问题呀

解决方案 »

  1.   

    tcp的话,包不会乱的
    一般自定义一个协议,在数据前设个字段,表示数据的长度
      

  2.   


    一个连接只允许一个线程收  这样处理实在是太慢了 要求当然要多线程啦我发送数据前面有弄个
    typedef struct _CMD_PACK_INFO 
    {
    int nCMD; //包命令
    int nID; //包ID
    int nPos; //包位移
    short sDataSize; //包大小
    }CMD_PACK_INFO, *LPCMD_PACK_INFO;
    可是有的时候 判断包命令都错误  就别说下面了
      

  3.   

    typedef struct _CMD_PACK_INFO 
    {
        int nCMD;                        //包命令
        int nID;                        //包ID
        int nPos;                        //包位移
        short sDataSize;                //包大小
    }CMD_PACK_INFO, *LPCMD_PACK_INFO;
    我要看的是你的数据流的格式,不是结构体的格式。
      

  4.   

    我在接收到一定的数据后让它sleep一下 这样数据就可以成功
    可是时间就慢了很多 没sleep 包就有错误 这个是怎么回事呢  
      

  5.   

    sleep 这个东东不要用,他只会让你的程序运行更慢,更没效率。用了Sleep可以成功的原因,是用一段时间来等待,数据都写到你机子的缓冲区了,所以读的时间都读出来了。如果网络情况再差一点,Sleep就又不好使了。
      

  6.   


    问题是
    我客户端请求下载文件 
    客户端会突然就没反应了  但是服务器那边有把数据发送完
    服务端是按块发送的 块发送完 客户端接收完 会给服务器一个反馈 服务器接收到反馈后才会继续发送数据
    可是 客户端停住了 服务器还是会继续发 我跟踪又发现不了 问题 所以我考虑 让它sleep一下  sleep100 我就没发现原来那个问题 
    测了好几次 都没问题
    可是sleep传输文件实在是太慢了 
     
    各位大侠们  帮帮忙 看看这个是怎么回事分我加了 不够可以继续加
      

  7.   

    当然有啦 用完成端口的
    -----IoSend
    -----IoRecv
    dwTransferByte = 18 
    -----NP_SC_FILEINFO
    -----IoSend
    -----IoRecv
    dwTransferByte = 7314 
    -----NP_NC_FILEDATA
    dwFileMapviewUsed = 7300 --- dwFileMapview =65536 
    -----IoRecv
    dwTransferByte = 7314 
    -----NP_NC_FILEDATA
    dwFileMapviewUsed = 14600 --- dwFileMapview =65536 
    -----IoRecv
    14是包头大小 7300是内容
      

  8.   

    我最近翻译了一篇文档,里面就有这个解决方案 :(翻译的不好,请多包涵)   
        
        一、   WSAENOBUFS   错误问题。   
        
                  这个问题通常很难靠直觉发现,因为当你第一次看见的时候你或许认为是一个内存泄露错误。假定已经开发完成了你的完成端口服务器并且运行的一切良好,但是当你对其进行压力测试的时候突然发现服务器被中止而不处理任何请求了,如果你运气好的话你会很快发现是因为WSAENOBUFS   错误而影响了这一切。   
        
                每当我们重叠提交一个send或receive操作的时候,其中指定的发送或接收缓冲区就被锁定了。当内存缓冲区被锁定后,将不能从物理内存进行分页。操作系统有一个锁定最大数的限制,一旦超过这个锁定的限制,那么就会产生WSAENOBUFS   错误了。   
        
                如果一个服务器提交了非常多的重叠的receive在每一个连接上,那么限制会随着连接数的增长而变化。如果一个服务器能够预先估计可能会产生的最大并发连接数,服务器可以投递一个使用零缓冲区的receive在每一个连接上。因为当你提交操作没有缓冲区时,那么也不会存在内存被锁定了。使用这种办法后,当你的receive操作事件完成返回时,该socket底层缓冲区的数据会原封不动的还在其中而没有被读取到receive操作的缓冲区来。此时,服务器可以简单的调用非阻塞式的recv将存在socket缓冲区中的数据全部读出来,一直到recv返回   WSAEWOULDBLOCK   为止。   
        
              这种设计非常适合那些可以牺牲数据吞吐量而换取巨大并发连接数的服务器。当然,你也需要意识到如何让客户端的行为尽量避免对服务器造成影响。在上一个例子中,当一个零缓冲区的receive操作被返回后使用一个非阻塞的recv去读取socket缓冲区中的数据,如果服务器此时可预计到将会有爆发的数据流,那么可以考虑此时投递一个或者多个receive来取代非阻塞的recv来进行数据接收。(这比你使用1个缺省的8K缓冲区来接收要好的多。)   
        
      总结:   
        
        解决方法一:   
        
            投递使用空缓冲区的   recevie操作,当操作返回后,使用非阻塞的recv来进行真实数据的读取。因此在完成端口的每一个连接中需要使用一个循环的操作来不断的来提交空缓冲区的receive操作。   
        
      解决方法二:   
        
        在投递几个普通含有缓冲区的recevie操作后,进接着开始循环投递一个空缓冲区的recevie操作。这样保证它们按照投递顺序依次返回,这样我们就总能对被锁定的内存进行解锁。   
      

  9.   

    能具体点吗  可以的话 帮我代码给敲处理看下 真的是没办法了 
    我在网上也看到关于这个的技术文章  看了还是解决不了
    http://tech.ddvip.com/2008-12/122855295598098.html
    服务器是发送端  客户端是接收端
      

  10.   

    你的IOWork线程是多个还是一个。
      

  11.   

    原则上在一个连接上同一时间只能存在一个WSARecv请求,你的程序是否随着时间的推移,同一个连接上的WSARecv会越来越多。导致被锁住的内存越来越多,最后达到上限。
      

  12.   

    你可以把程序先改为单线程的,这样调试就比较简单了,单线程没有问题了再改为多线程的看看.
    如果单线程没问题而多线程有问题,就可能是互斥或者数据访问的随机导致的问题.你说"客户端停住了 服务器还是会继续发",这个你描述的不清楚,因为你说"客户端接收完 会给服务器一个反馈 服务器接收到反馈后才会继续发送数据",所有你说的情况理论上是不会发生的,如果发生了,那就是你程序的逻辑根本就没实现"服务器接收到反馈后才会继续发送数据".至于那位仁兄说"原则上在一个连接上同一时间只能存在一个WSARecv请求",我觉得投递几个WSARecv请求没有问题,我写的程序都是投递5个WSARecv请求,此后每完成一个就再投递一个WSARecv请求.但是每个socket都有一个lock,这样每个socket每次只能处理一个WSARecv请求.
      

  13.   

    我在iocp的模式下还碰到了一个问题:投递了多个发送数据的WSASend后,很长一段时间内这些WSASend都没有完成,等了一段时间后(最多的可能有1分钟),这些WSASend才突然完成了,不知道你是不是碰到的也是这个问题.
    现在我仍然没有找到解决方法,上面有位仁兄说"投递一个字节为数0的包",不知道可不可以解决,以后试一下看看.
      

  14.   


    单线程尝试过了 还是不行 就是文件小的时候可以成功 大的文件发到一半就不发了 
    是呀 客户端接收完一块后 会给服务器一个反馈  服务器才继续下一块 可是
    客户端 收到2883584
    -----BLOCK OK
    -----dwFileOffset = 2883584
    -----IoSend
    -----IoRecv
    dwTransferByte = 7314 
    -----NP_NC_FILEDATA
    而服务器 已经到了3866624
    -----dwFileOffset = 3866624
    -----IoSend
    -----IoSend
    -----IoSend
    -----IoSend
    我如果把发送反馈的屏蔽掉 服务器下面就不发了
    所以这个我感觉很纳闷
      

  15.   

    你逻辑上有问题吧,你把block的size设置的小一下看看.
      

  16.   


    你这样,一个连接上投递了多个WSARecv之后,又加了锁与投递一个有什么区别呢?
      

  17.   

    我测过了,下载没有问题的。测试环境,单机运行服务端与客户端,下载文件600多M,OS为Windows Server2003。
      

  18.   


    你看下 服务器那边数据发送完的是 =-=-这个标识
    而客户端接收完的标识是 Down OK
      

  19.   

    对了 我用的系统是winxp 双核 单双核 好像好点区别 
      

  20.   

    我测试过 有下面三种情况
    1.运气好  就OK  下载都成功
    2.服务器那边打印出来的是数据都传完  而客户端到一半就没打印了 要不就是提示客户端接收到的信令错误
    3.服务器和客户端 中途就没打印了
    到我跟踪的时候 好像要都没问题 如果加上sleep100的 大部分就都下载成功  有的时候也错误
      

  21.   

    我的配置是
    winxp 双核 2G  Fat32 
      

  22.   

    你这样,一个连接上投递了多个WSARecv之后,又加了锁与投递一个有什么区别呢?-------------------------------------------
    加锁只是在对接收到的数据进行处理的时候加锁
      

  23.   


    你看看下面的帖子,就了解了。
    http://www.cppblog.com/sherrylso/archive/2008/06/23/30858.html
      

  24.   

    -----IoRecv
    dwTransferByte = 7314 currThreadID = 2280
    perPackInfo->nCMD ERROR!
    -----IoRecv
    dwTransferByte = 7314 currThreadID = 2348
    perPackInfo->nCMD ERROR!
    -----IoRecv
    dwTransferByte = 2830 currThreadID = 2280
    perPackInfo->nCMD ERROR!
    就是有些时候 会出现这样 包乱了 
    包乱了 就不怎么好处理了
      

  25.   

    包乱了,我怎么没发现这种现象。我的也是双核的。Dell笔记本,LATITUDE D630.服务端与客户端都在一台机子上。你哪公司?
      

  26.   

    你多试几次  看看会不会出现
    我试都会出现这个问题  纳闷呀
    你要看客户端是否出现了 Down OK 这个 是的话就说明下载OK
    我福州的
      

  27.   

    客户端请求下载
    服务端发送数据 客户端接收数据
    包乱是客户端接收到 提示perPackInfo->nCMD ERROR 
    你可以到代码里面查找一下
    OutputDebugString("perPackInfo->nCMD ERROR!\r\n"); 
    这个是完成端口接收到数据 解析信令 可是信令是错的还的时候 就是我不是有把收到的数据信息 打到DEBUG里面吗
    用那个跟踪  有的时候发到一半就不打印了 可是网络还是通的 你怎么都没出现这个问题 我跟踪了几次都有这个现象 难道是RP问题?下载成功一个文件是 服务器那边数据发送完是OutputDebugString("=-=-=-=-\r\n");
    客户端是 Down OK
      

  28.   

    OutputDebugString("-----IoRecv\r\n");
    perPackInfo = (LPCMD_PACK_INFO)perIOData->pBuffer;
    char temp[256] = {0};
    sprintf(temp, "dwTransferByte = %d \r\n", dwTransferByte);
    OutputDebugString(temp); switch(perPackInfo->nCMD)
    {
    case NP_SC_FILEINFO:
    {
    OutputDebugString("-----NP_SC_FILEINFO\r\n");
    BOOL bRet = pThis->DealFileInfo(perHandleData, perPackInfo);
    if (!bRet)
    {
    break;
    }
    }
    break;
    case NP_NC_FILEDATA:
    {
    OutputDebugString("-----NP_NC_FILEDATA\r\n");
    BOOL bRet = pThis->DealFileData(perHandleData, perPackInfo);
    if (!bRet)
    {
    break;
    }
    }
    break;
    default:
    {
    OutputDebugString("perPackInfo->nCMD ERROR!\r\n");
    }
    break;
    } dwFlags = 0;
    dwRecvBytes = 0;
    perHandleData->recvIO->wsDataBuf.buf = perHandleData->recvIO->pBuffer;
    perHandleData->recvIO->wsDataBuf.len = MAX_SEND_SIZE + nStructSize;
    ZeroMemory(&(perHandleData->recvIO->overLapped), sizeof(OVERLAPPED));

    WSARecv(perHandleData->sock, &perHandleData->recvIO->wsDataBuf, 1, &dwRecvBytes, &dwFlags,
    &perHandleData->recvIO->overLapped, NULL);
    }
    break;
    肯定不对的。你还是接收了一次,要判断dwTransferByte,如果小于预定的值就不要处理,继续接收才对。直到接收达到预定的值再处理。
      

  29.   

    老兄 我卡在这边很久了挤点时间给我吧  感谢ing...
    我这天被老板催了紧 就只能细节的先来 都不知道对不对 烦!
      

  30.   

    你自己得封装recv函数,多次调用,直到大小等于完整数据大小。一般于1024就会有这个问题。
      

  31.   

    你用个while循环读,直到空了再退出,我看很多老外写的socket类都是这样干的!
      

  32.   

    先接收sizeof(CMD_PACK_INFO)个字节,如果dwTransferByte<sizeof(CMD_PACK_INFO),则重新设置BUF与LEN,接收剩下的字节,直到接收到完整的 CMD_PACK_INFO后,取出消息体的长度BODY_LEN。之后再利用上面的方法接收消息体。
      

  33.   

    TCP是基于流模式的,存在着数据一次发送多次接受的问题,所以,在接受的时候要做拼包处理,
    UDP是基于数据报模式,不存在一次发送多次接受问题,但不保证数据的正确性,所以每个包都要做正确性的校验
    因此TCP拼包,udp正确性校验是做网络通讯必不可少的步骤。
      

  34.   


    先接收sizeof(CMD_PACK_INFO)个字节?
    重新设置BUF与LEN 这个要怎么设置呀 应该按什么来设置呢?
    我的数据是一个包整体发过去了 是 命令+数据 再+上一些偏移什么的呢
    如果可以的话  代码直接贴出来吧  那样更好理解
      

  35.   

    TCP 接受的顺序,是没有问题的,但是在多线程环境下,IO操作无序,所有给每个包几个号,然后判断,要么等收完了 重组包
      

  36.   

    TCP协议下是没有"包"的概念的,换句话说,如果你能够切出一个个的逻辑"包",那么你必须先排序,在协议当中并不会因为你发送的时候标识是1的包就全部由1号线程收全.1. 事实上如果你有两个线程在接收(任何不管是异步还是同步模式当中都存在这个问题,这个并不是IOCP的专利),在IOCP当中只是对于编码人员来说,由于封装而使得这个问题更加隐蔽.对于楼主提出的对同一个Socket需要发起多个未决的接收请求,个人并不认同,首先就是前面提到的乱序问题,而这个乱序是完全不可能还原的,在单核系统下可以造线程的先来后到在一定程序上保证顺序,但是这个并没有任何依据,在Windows平台下CPU是按时间分片调度给线程的,从而并没有任何一种方式可以保证:A比B先发Recv请求,就有Recv后续的指令,A一定比B先执行到(如果不是很理解的话,个人建议增加一点点基础知识,特别是相关OS当中CPU调度的相关知识,如果觉得难,或者没有必要,那么就记住这是一个要点),特别是在多CPU和(或)多核心的系统下,这个问题就更加突显。但是,如果使用的不是TCP协议,而是其它基于“报文”的协议,特别是协议本身就不保证到达顺序的(比如UDP协议),那么不存在此限制。2. 楼主提到同时发起多个Recv可以提高效率。事实上,如果网络的IO能力相当有限的情况下,这样子一个短时间片内的利用,并不会提升太多的效率,而如果你的业务系统的压力并不在于网络的IO,那么这个提升实际上又并不存在多少意义。由1当中提到的乱序问题,先不说能不能进行序号重排,假定可以重排的前提下,这个重排的消息也已经大大增加了系统的压力,网络IO本就不是一个瓶颈的系统下,是不是有点画蛇添足的味道?如果你的网络带宽足够,而在业务系统有足够资源的前提下并不能很好地利用你的网络资源,那么可以试试几个方面的调整:
    1). 取消MTU限制
    2). 修改TCP协议栈默认窗口大小(window size)
    3). 扩大发送和接收缓冲区
    3. 事实上,如果想好好地利用带宽而不受业务处理的影响,就是当WSARecv在GetQueuedCompletionStatus返回之后对接收数据的缓冲队列加锁,然后立即起发起WSARecv操作,然后再存到队列当中,如此就可以保证后面Recv到的数据受队列锁的限制而排到了后续,从而保证顺序,并且可以由多线程而达到更高的网络利用。4. 如果觉得这些还不够,那么就采用WSARecv的Routine模式,让所有的Recv完成操作排到同一个线程当中,如此你就针对某一个Socket保证全部WSARecv都在同一个线程当中发起即可,发起多个也不会影响到达的顺序,处理时采APC切换(SleepEx)。
      

  37.   

    必须标记!好贴!谢谢vagrantisme
      

  38.   

    是不是在点子上不应该从你的主观上进行判断.你得先从另外一个角度去看问题,否则就会一直钻在自己给自己设计的"死胡同"里.
    要解决问题先从根本上解决问题.另一个是,我只能假定除了逻辑上面的问题,其它编码上你的代码是没有问题的,要不然问题就更难定位.完成端口说难也不难,说简单也不简单.之所以说不难,是因为它就那么几个函数,几个逻辑;说它难,是因为很多人被它的特性给套住其中,特别是其封性以及多个线程的并发性等导致了一些隐蔽性的问题.个人建议,对于初次使用的时候,先不要弄啥多线程,就一个线程处理GetQueuedCompletionStauts事件,并且所有需要发送的数据都通过PostQueuedCompletionStatus投递到该线程当中,并由该线程发起WSARecv操作等.先保证"流数据"的连续性和准确性,然后再解决报文边界问题,当这些问题解决了之后,再考虑使用多线程.特别要明白的是,对于单个连接在IOCP当中强加于同一个连接的并行Receive是没有太多性能提升的.重要的仍然在于处理,对于服务器程序则重点在于让多个连接产生高并发操作而提升网络IO能力.
      

  39.   

    一个套接字上是可以处理多个文件,但是并不是象你这种处理方式。即使是传输多个文件,并且对文件进行分片(如果是大文件),那么各分片至少需要保证完整性,从而客户端在收取的时候可以根据分片当中的标识信息来区分。如果你把多个文件的分片混在一起,或者是接收时乱拼一气,那么你得到的内容将会是一个个“新文件”,不信可以试一下,你创建多个文件,每个文件当中的内容都是同一个字的多个复本,比如A文件10MB,全是“A”字符,B文件是15MB,里面全是“B”字符,然后看看你收取到的内存是什么样子的,然后再来重新思考你的设计。
      

  40.   

    我每个文件下载的时候 我都有用文件ID来标识它的  
    你说的分片 我就是用内存映射来处理的  
    每次数据过来我都是有判断它的ID 然后再来处理它的  
      

  41.   

    看了一下楼主的代码,一个很严重的问题就是没有处理“报文”边界和对流协议当中数据的粘连做出切分处理,从而导致认为每一个WSARecv的返回都是对端一个WSASend出来的完整报文。首先在“case IoRecv:”分支当中先判断是否在接收“正文”数据,如果不是则判断是否足够一个_CMD_PACK_INFO包,不足则把Receive使用的Buffer偏移移动到dwTransferByte,并且记录下dwTransferByte,下次再处理的时候把缓冲的量加上,再加以判断。如果一个_CMD_PACK_INFO结构完整收齐,则先切出这一部分,并且把后续的数据缓存起来(这个时候之前的Buffer可能不够用,如果够用则只须移动Buffer偏移),然后设置为“正文”标记,并且前面已经缓存下来的字节数作为已经完成的字节数保存下来(当然这个缓存也可以直接写入文件当中),下次接收的时候就根据_CMD_PACK_INFO.sDataSize和已经下载的数据字节数进行接收,当完成一个block之后重置标记为接收“包头”循环上述工作。
      

  42.   

    不知楼主是否已解决问题. 我觉得,问题的关键在于:对接收到数据的处理不当估计楼主对于接收到的数据,拆包以后,将剩余的数据都给抛弃了。由于TCP是安全的连接,可以理解成是按流的方式传送数据的,数据内部没有包的概念。接收到的数据一定要由接收端自行拆包,拆包剩下的数据要保留,因为它们是后续包的数据,一旦抛弃,后续的数据包就被破坏了。调试的时候,能够确保发送端发一个数据包后就停止,因而 能够正常工作
      

  43.   

    http://topic.csdn.net/t/20051008/23/4313282.html
    对几个api函数的理解不够!希望上面的帖子对楼主有所帮助!
      

  44.   

    看看typedef struct _CMD_PACK_INFO 
    {
        int nCMD;                        //包命令
        int nID;                        //包ID
        int nPos;                        //包位移
        short sDataSize;                //包大小
    }CMD_PACK_INFO, *LPCMD_PACK_INFO;