如果回答客户端不能同时投递2个WSAsend,也是有问题的!
原因1、客户端的TCO可以将多个小数据包组合为一个发送
    2、如果客户端的发送缓冲区大于服务器端的接收缓冲区,客户端的一个WSASend,服务器端仍要多次接收才行,仍然可能出现多个线程同时收到一个socket的数据,之后,乱序就极可能发生

解决方案 »

  1.   

    如果回答客户端不能同时投递2个WSAsend,也是有问题的!
    原因1、客户端的TCP可以将多个小数据包组合为一个发送
        2、如果客户端的发送缓冲区大于服务器端的接收缓冲区,客户端的一个WSASend,服务器端仍要多次接收才行,仍然可能出现多个线程同时收到一个socket的数据,之后,乱序就极可能发生
        3、即便是一个小的数据包,发送端和服务器端都可能分次发送与接收,
    我的问题是:多个服务器端线程,同时收到同一个socket发来的数据,在写入缓冲区的时候,如何保证不乱序(线程切换时随机的,不能100%保证先收到的数据先写入缓冲区)? 
      

  2.   

    谁会把一个socket用两个线程接收
      

  3.   

    4.3.4 包重新排序问题
    虽然使用 I/O 完成端口的操作总会按照它们被提交的顺序完成,但是线程调度问题可能
    会导致关联到完成端口的实际工作不按正常循序完成。例如,有两个I/O 工作线程,应该接
    收“字节块1,字节块2,字节块3”,但是你可能以错误顺序接收到了这3 个字节块:“字节
    块2,字节块1,字节块3”。这也意味着通过在完成端口上投递发送请求发送数据时,数据
    实际会以错误的方式被发送。
    这个问题可以通过仅使用一个工作线程,仅提交一个I/O 调用,然后等待它完成来避免。
    但是这样就失去了IOCP 的所有优点。
    此问题的一个简单的解决方法是向提交的缓冲区对象中添加序列号,如果缓冲区序列号
    是连续的,就处理缓冲区中的数据。这意味着,有着错误序号的缓冲区要被保存下来,以便
    今后使用。
    另外,大多数网络协议都是基于封包的,这里,开始的X 个字节描述协议头,协议头包
    含的信息说明了整个封包到底有多大。服务器可以读协议头,计算出还需要读多少数据,然
    后继续读后面的数据,直到得到完整的封包。当服务器一次仅做一个异步读调用时,这工作
    得很好。但是,如果想要发挥IOCP 服务器全部潜力,就应该有多个未决的异步读操作等待
    数据的到来。这意味着,多个异步读操作不按顺序完成,未决读I/O 返回的字节流不能按顺
    序处理,接收到的字节流可能组合成正确的封包,也可能组合成错误的封包。所以,必须要
    为提交的读I/O 分配序列号。
      

  4.   

    4.3.4 包重新排序问题
    虽然使用 I/O 完成端口的操作总会按照它们被提交的顺序完成,但是线程调度问题可能
    会导致关联到完成端口的实际工作不按正常循序完成。例如,有两个I/O 工作线程,应该接
    收“字节块1,字节块2,字节块3”,但是你可能以错误顺序接收到了这3 个字节块:“字节
    块2,字节块1,字节块3”。这也意味着通过在完成端口上投递发送请求发送数据时,数据
    实际会以错误的方式被发送。
    这个问题可以通过仅使用一个工作线程,仅提交一个I/O 调用,然后等待它完成来避免。
    但是这样就失去了IOCP 的所有优点。
    此问题的一个简单的解决方法是向提交的缓冲区对象中添加序列号,如果缓冲区序列号
    是连续的,就处理缓冲区中的数据。这意味着,有着错误序号的缓冲区要被保存下来,以便
    今后使用。
    另外,大多数网络协议都是基于封包的,这里,开始的X 个字节描述协议头,协议头包
    含的信息说明了整个封包到底有多大。服务器可以读协议头,计算出还需要读多少数据,然
    后继续读后面的数据,直到得到完整的封包。当服务器一次仅做一个异步读调用时,这工作
    得很好。但是,如果想要发挥IOCP 服务器全部潜力,就应该有多个未决的异步读操作等待
    数据的到来。这意味着,多个异步读操作不按顺序完成,未决读I/O 返回的字节流不能按顺
    序处理,接收到的字节流可能组合成正确的封包,也可能组合成错误的封包。所以,必须要
    为提交的读I/O 分配序列号。不知道这是什么书写的,不过怎可能失去所有优点,你一个服务器又不是面对一个链接,为什么要增加这种复杂性非要把同一个socket分成多个线程去操作,这不用iocp,用其他的模型出的问题是一样的。
    而且上面提到的方法,我是想不出对提出的问题有任何帮助。你用几个工作线程和你一个socket只用一个线程投递一次请求不冲突。
      

  5.   


    没看懂你的问题。1. 不说线程。说异步既然是异步,那么也是无法保证顺序的。需要自己搞。工作线程不断的getqueuecompletionstatus ,分析数据,然后继续投递。完全有可能同一个线程对 同一个套接字投递。即使不存在 同时投递,那么 也有问题要考虑:线程1 ,线程2 都接受 同一个客户端的数据, 你说这种可能性有没有?所以必须对数据编号。
      

  6.   

    我详细说一下这种情况:客户A发送2000字节数据客户B发送3000字节数据网络数据流为2000   30000Get收到2000字节数据,线程C处理(线程D空闲)
    在C处理数据的时候,客户B的数据到了,于是,Get收到1800字节B的数据,通知D线程处理,此时,C线程处理完了(投递Recv后,空闲),此时Get收到B的剩余1200字节,只能由C处理,
    这时候,C,D 2线程同时操作客户B数据,由于线程切换的随机,有可能D线程仅将800字节写入缓冲后,被切换,此时C线程执行,将1200字节写入缓冲,之后D线程获得时间片,再将剩余的1000字节写入缓冲,于是乱序出现了,实际上这种情况也不乱序,我不知道为什么不会乱序?
      

  7.   

    谁会把一个socket用两个线程接收 
    ----------------------------
    IOCP哪里规定了必须使用同一个线程接收一个socket的数据?
      

  8.   


    是一次一个socket只能投递一个recv  不管你多少个工作线程  这样怎么会乱序?你一个socket投递2个recv那当然可能乱序...
      

  9.   

    你都还没理解iocp的工作机制  也就是说你还用不来iocp  多下些比人的示例看看吧...
      

  10.   

    是一次一个socket只能投递一个recv  不管你多少个工作线程  这样怎么会乱序?你一个socket投递2个recv那当然可能乱序... 
    --------------------------------------------------------------------------------------我说的你没理解:
    你说的:
    一个socket只能投递一个recv
    我问你:
    假设客户端要发送1234567890,由于网络很卡,发送方发送了2次,假设服务器也很卡,数据分3次才收完,那么在第一次和第二次收完后(假设收到1234567),getqueuecompletionstatus不返回?服务器端一定要收到全部的1234567890才返回?如果是这样,原因是什么?
      

  11.   

    关键的问题是:如果网络很卡,发送端的一个数据包接收端要多次接收才行(假设10次),那么getqueuecompletionstatus何时返回?他怎么知道要连续接收10次才可以?
      

  12.   

    不会发生getqueuecompletionstatus接收半包就返回的情况?如果会,我说的就可能发生,如果不会,能说一下原因吗?
      

  13.   


    你的如果不成立  你提交的recv100字节的操作 系统只会尽可能的多收数据满足你recv1000字节的要求只来了500 getqueuecompletionstatus肯定会返回告诉你只收到500数据...
      

  14.   

    你的如果不成立  你提交的recv100字节的操作 系统只会尽可能的多收数据满足你recv1000字节的要求只来了500 getqueuecompletionstatus肯定会返回告诉你只收到500数据... 
    ------------------------------------------------------------------------
    是不是应该为:你提交的recv1000字节的操作 
      

  15.   


    当然会有收到部分的情况  这种情况要看你自己定义的协议是什么 如果发送端会保证需要发多少就一定能发多少 那你接收端只要不停recv就是了 事实上大部分的网络编程 recv的操作都是不停的recv  将recv的数据放到缓冲区或者只直接就开始解析...
      

  16.   


    当然会有收到部分的情况  这种情况要看你自己定义的协议是什么 如果发送端会保证需要发多少就一定能发多少 那你接收端只要不停recv就是了 事实上大部分的网络编程 recv的操作都是不停的recv  将recv的数据放到缓冲区或者只直接就开始解析...-----------------------------------------------------
    照你的说法,我请教一下:
    如果服务器收到了500字节,此时客户端是不是已经发生了1000字节?
      

  17.   

    假设客户端发送了1000字节,服务器第一次收到了500字节后getqueuecompletionstatus返回,线程C处理这500字节数据,如果此时,后面的500字节又到了?getqueuecompletionstatus不返回吗?(假设还有工作线程空闲),如果不返回,后面别的用户的数据也被阻塞了,如果返回,由于线程C正忙着,所以必须使用线程D接收后500数据,于是,我说的情况出现了
      

  18.   


    当然会有收到部分的情况  这种情况要看你自己定义的协议是什么 如果发送端会保证需要发多少就一定能发多少 那你接收端只要不停recv就是了 事实上大部分的网络编程 recv的操作都是不停的recv  将recv的数据放到缓冲区或者只直接就开始解析...-----------------------------------------------------
    照你的说法,我请教一下:
    如果服务器收到了500字节,此时客户端是不是已经发生了1000字节?客户端有没有发1000字节 这个客户端那里检测不到么? 只要客户端保证发送了1000字节 只要socket没有断你的服务器recv逻辑在getqueuecompletionstatus告诉你只收到500字节后你只要保存好这500字节 再投递个recv500就是了肯定会收到剩下的500字节要是getqueuecompletionstatus又返回个300你还是只要保存好这300加上前面的500 你再投递个recv200 只要保证socket没有断(TCP是保证不会丢包的) 你就一定能收到客户端发的这1000字节 你自己只要保证永远只有一个recv请求操作提交给iocp  就不存在你说的乱序...
      

  19.   

    假设客户端发送了1000字节,服务器第一次收到了500字节后getqueuecompletionstatus返回,线程C处理这500字节数据,如果此时,后面的500字节又到了,getqueuecompletionstatus不返回吗?(假设还有工作线程空闲),如果不返回,后面别的用户的数据也被阻塞了(结果是大家都在等,IOCP使用一个工作线程就行了,可是盖茨建议使用2N线程,显然这种情况不会发生),如果返回,由于线程C正忙着,所以必须使用线程D接收后500数据,于是,我说的情况出现了
      

  20.   


    你没有投递recv的动作  getqueuecompletionstatus当然不返回  数据只是存在网卡而已  要是你一直不接收客户端还一直send最后的结果就是 客户端send会失败 getlasterror 就是缓冲区不够... 
      

  21.   


    是每个socket都有自己的缓冲区 你这个链接不接收不影响其他的链接 一直不接收只会让你这个链接的远端send失败...
      

  22.   

    只有一个recv请求操作提交给iocp  
    -----------------------------
    这个是必须的
      

  23.   


    是每个socket都有自己的缓冲区 你这个链接不接收不影响其他的链接 一直不接收只会让你这个链接的远端send失败...
    ----------------------------------------------------------
    getqueuecompletionstatus不是一个队列吗?前面的不出列,后面的如何出去?
      

  24.   

    就像水龙头堵了,有多少人打水,都得等,getqueuecompletionstatus队列不是这样的吗?
      

  25.   


    这个能保证怎么会乱序? 正常的处理都是在getqueuecompletionstatus返回的时候解析数据或者保存数据然后才投递下一个recv(所以哪个线程去收数据都不影响业务)   根本就不存在2个线程同时收同一个socket的数据的情况...
      

  26.   


    是每个socket都有自己的缓冲区 你这个链接不接收不影响其他的链接 一直不接收只会让你这个链接的远端send失败...
    ----------------------------------------------------------
    getqueuecompletionstatus不是一个队列吗?前面的不出列,后面的如何出去?那你意思是一个recv投递了 客户端不发数据  iocp就堵死了是吗?? 你是这样理解iocp的?
      

  27.   

    completionstatus 看名字就知道是完成状态的意思吧  这个是获取那些已经完成了的请求的 和你发送请求那个队列就不是一个东西...
      

  28.   

    我只是没有理解getqueuecompletionstatus队列,这就是问题的结!我知道队列有先进先出和后进先出,难道还有可以任意出列的队列?
    难道getqueuecompletionstatus不是队列,是个类似Vector的结构?
      

  29.   

    也就是说你可以投递1000个请求   只要其中有请求完成了getqueuecompletionstatus就会返回  并不是这1000个请求一定要第一个、第二个...这样顺序处理  要是这样那就不是iocp了
      

  30.   

    这个是系统管理的吧,程序员不需要知道,只需要在getqueuecompletionstatus 后处理。还有,所谓的连续,是你自己意淫出来的,  难道必须是 连续接受,就不能够做其他吗?
    什么是半包? 求解释
      

  31.   

    这问题居然都顶了几十个楼了,大致看了看,源于你很能抬杠啊。
    为什么要把简单问题复杂化呢。还是没想明白你为什么非要“两个线程”“同时”对一个socket投递。
      

  32.   


    一个完成任务的队列 只要有完成的任务 将等待的一个工作者线程唤醒 getqueuecompletionstatus返回这个任务就行了  不知道你的问题是什么...
      

  33.   


    发你的 iocp代码 ,我看看, 我是解决乱序是参考一本书上的思路,
    另外:一般工作线程有2个,  线程代码是一样的,  我觉得楼主有些昏了, 楼主看这里: // 关联新连接到完成端口对象
    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
    再看这里: BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);这的socket等per-hanlde 信息是 对应的。   每一个socket 可以多次投递io请求。至于 会不会 多个线程同时投递io请求, 拙见: 不可能。   io等函数 只能被同一个线程调用吧,数据取出,或者投递后,其他线程 不会再处理了。高手斧正
      

  34.   


    我看你是把那些请求都当成系统会去线性处理了 一个一个任务顺序处理下去 你是这样理解的吧? 所以你就想不通某个任务没完成其他任务也就收不到完成了  你理解错了  你所有的请求甚至都可以同时进行 getqueuecompletionstatus只是让系统把你请求的任务结果告诉你 有就返回没有就等待... 
      

  35.   


    发你的 iocp代码 ,我看看, 我是解决乱序是参考一本书上的思路,
    另外:一般工作线程有2个,  线程代码是一样的,  我觉得楼主有些昏了, 楼主看这里: // 关联新连接到完成端口对象
    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
    再看这里: BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);这的socket等per-hanlde 信息是 对应的。   每一个socket 可以多次投递io请求。至于 会不会 多个线程同时投递io请求, 拙见: 不可能。   io等函数 只能被同一个线程调用吧,数据取出,或者投递后,其他线程 不会再处理了。高手斧正老实说只要你保证只会有一个recv操作 真的没什么乱序的问题(前提是tcp) 也就不存在你说的解决乱序一说...
      

  36.   

    accept接受1个客户端,马上投递一个recv
    对每个socket只投递一个recv,不同的socker可以同时投递
    recv先接受1个包头,含有信息长度,如果recv收的字节不够,投递1个recv
    如果收完整了,可以就地处理或用队列转发到其他工作线程。再投递下1个recv
    这样一个客户端的包顺序是有保障的。对1个socket同时投递多个recv的,我只能表示膜拜。
      

  37.   


    单个数据包太大了,建议小于一般的MTU值乱序的问题暂时未发现,跟IOCP没多大关系~~建议你把封包里加上包序,单个包1024左右即可~~
      

  38.   

    正常的流程就是 accept后给iocp投递一个recv工作者线程中处理recv的结果 处理完根据业务逻辑投递下一个recv这样是不可能乱序的 即便你socketA recv100来了200一次GetQueuedCompletionStatus也只能收这100 下个100你不做recv投递的话 根本就收不到GetQueuedCompletionStatus关于socketA的通知  反之recv200来了100GetQueuedCompletionStatus只会返回告诉你来了socketA的100 你要是不再投递recv  也你不可能再收到GetQueuedCompletionStatus的socketA的返回
      

  39.   

    getqueuecompletionstatus 返回发现包不全,就继续投递recv 知道包全了未知
      

  40.   


    发你的 iocp代码 ,我看看, 我是解决乱序是参考一本书上的思路,
    另外:一般工作线程有2个,  线程代码是一样的,  我觉得楼主有些昏了, 楼主看这里: // 关联新连接到完成端口对象
    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
    再看这里: BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);这的socket等per-hanlde 信息是 对应的。   每一个socket 可以多次投递io请求。至于 会不会 多个线程同时投递io请求, 拙见: 不可能。   io等函数 只能被同一个线程调用吧,数据取出,或者投递后,其他线程 不会再处理了。高手斧正老实说只要你保证只会有一个recv操作 真的没什么乱序的问题(前提是tcp) 也就不存在你说的解决乱序一说...
    如何保证?接受数据与否,不是用recv函数,亲,而是用getqueuecompletionstatus函数的,难道对该函数加锁不成?只需要对其加锁乎?  
      

  41.   


    发你的代码我看看,可以不?看你是如何处理的, 最好是上传整个可以使用的工程,多谢
    去网上找 网络编程(第二版)附补充材料(源代码)
    里面有个iocp服务器的源码,可以参考。
      

  42.   


    发你的 iocp代码 ,我看看, 我是解决乱序是参考一本书上的思路,
    另外:一般工作线程有2个,  线程代码是一样的,  我觉得楼主有些昏了, 楼主看这里: // 关联新连接到完成端口对象
    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
    再看这里: BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);这的socket等per-hanlde 信息是 对应的。   每一个socket 可以多次投递io请求。至于 会不会 多个线程同时投递io请求, 拙见: 不可能。   io等函数 只能被同一个线程调用吧,数据取出,或者投递后,其他线程 不会再处理了。高手斧正老实说只要你保证只会有一个recv操作 真的没什么乱序的问题(前提是tcp) 也就不存在你说的解决乱序一说...
    如何保证?接受数据与否,不是用recv函数,亲,而是用getqueuecompletionstatus函数的,难道对该函数加锁不成?只需要对其加锁乎?  我要是不投递异步WSARecv  你getqueuecompletionstatus就等着吧...
      

  43.   


    发你的 iocp代码 ,我看看, 我是解决乱序是参考一本书上的思路,
    另外:一般工作线程有2个,  线程代码是一样的,  我觉得楼主有些昏了, 楼主看这里: // 关联新连接到完成端口对象
    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
    再看这里: BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);这的socket等per-hanlde 信息是 对应的。   每一个socket 可以多次投递io请求。至于 会不会 多个线程同时投递io请求, 拙见: 不可能。   io等函数 只能被同一个线程调用吧,数据取出,或者投递后,其他线程 不会再处理了。高手斧正老实说只要你保证只会有一个recv操作 真的没什么乱序的问题(前提是tcp) 也就不存在你说的解决乱序一说...
    如何保证?接受数据与否,不是用recv函数,亲,而是用getqueuecompletionstatus函数的,难道对该函数加锁不成?只需要对其加锁乎?  我要是不投递异步WSARecv  你getqueuecompletionstatus就等着吧...你不会以为只要数据来了 不管你有没投递WSARecv  getqueuecompletionstatus都会返回?
      

  44.   

    TCP IOCP 本身不会有这个问题,问题是你代码,LINUX EPOLL 也一样,你自己业务处理有问题而已!
      

  45.   

    呵呵,的确是楼主理解错了,响应了getqueuecompletionstatus这个函数,只能说明有一个操作已经完成,需要你把数据处理下,你即使不处理,函数还不是照样返回,进行下一个getqueuecompletionstatus。即使堵塞在这个线程的getqueuecompletionstatus中,还有其他线程在响应。线程池就是这个作用,不是一个单向的栈式