C++ IOCP問題備註

【1236錯誤】情況如下: 1.client連接server
      2.client發送消息到server
      3.client使用closesocket(sock);
      4.server接收到關閉socket消息 (lpNumberOfBytesTransferred爲0,  注意:如果客戶端發送的是空消息同樣爲零) 然後使用closesocket函數關閉了這個客戶端的socket
      5.這時候情況發生了,GetQueuedCompletionStatus函數返回false並且GetLastError() == 995 或者 1236

    原因如下: 經過本人測試發現, 如果不向系統投遞這個客戶端recv操作, 那麼客戶端使用closesocket函數服務端是無法接收到消息的, 那麼說明closesocket對於服務端來說也是一個特殊的recv操作,
    但是投遞了接收操作卻會出現995或者1236錯誤,995錯誤的說明是中止I/O操作引起的,但是我們已經接收到了客戶端的close消息, 所以才說closesocket是特殊的recv操作, 

      

      那麼1236錯誤呢? 最開始我是在網絡上查找這種錯誤說明,但是都沒有太大的幫助, 直到我找到了一篇文章,上面是這樣說的

    我們主要看第二條消息, 和我們的995錯誤是不是很類似,那麼我們就可以假設是因爲投遞的I/O操作返回引起的

  解決方法:
    既然是因爲I/O操作未完成引起的,那麼我們何不讓它完成,這樣就從根源上解決了問題發生,經過本人測試, 我們只需要讓客戶端再發一次消息,然後服務端再關閉客戶端的socket, 這時候既沒有出現995錯誤,也沒有1235錯誤, 完美解決。

 

【WSASend或者WSARecv未接受到失敗消息引起的資源未釋放問題】
1. 發生情況:例:OVERLAPPED* overlapped = &context->overlapped;
WSASend(context->accept, wsabuf, 1, &bytes, flags, overlapped, NULL),
如果我們使用了自定義結構體中overlapped投遞了send或者recv,然後又使用free或者delete釋放了該結構的內存,那麼GetQueuedCompletionStatus將不會返回失敗消息,我們也就不會刪除客戶端信息,那麼就會造成信息未釋放的問題。
2.解決辦法:#1: 不要清理這塊內存
#2: 只留下兩種context, recv和send,   recv可以充當accept的context,因爲accept操作使用完之後就不再需要context了

 

【AcceptEx返回10022】
1.發生情況:使用DisconectEx返回成功後的socket
2.解決辦法:注意DisconnectEx的flags參數必須爲 TF_REUSE_SOCKET,纔是表示關閉連接並複用socket,爲 0 則表示只關閉連接

 

【CreateIoCompletionPort返回87】
1.發生情況:使用DisconectEx返回成功後的socket
2.解決辦法:其實複用的socket已經綁定了完成端口,不用第二次綁定,直接投遞之後的recv或者send操作就好了


【WSASend返回10055】
1.發生情況:投遞多個send請求,且發送速度 > 對方接受速度導致數據堆積佔用非分頁內存
2.解決辦法:1. 限制投遞數量, 如果限制一個客戶端同時只能存在一個send和recv請求(增加併發量,減少吞吐量,但是理論上併發過多還是可能會出現這種情況)
      2. 投遞之前將WSABUF的緩存區size設置爲0, 那麼將不會鎖定用戶投遞的緩存區,也不會佔用非分頁內存, 但是緩衝區將不會接收數據, 所以需要我們提前將客戶端的連接socket使用setsocketopt函數設置非阻塞套接字,然後在收到接收通知或者發送通知的時候再使用recv/send函數取出客戶端發來的數據或者再向客戶端發送數據(提高吞吐量,降低併發量)

 

【客戶端連接服務器返回10055】
1.發生情況:大量併發客戶端連接(不超過6w左右)
2.解決辦法:添加MaxUserPort參數到註冊表, 添加之後除開系統底層和其他應用佔用的端口單機應該能到6w左右併發量,如果有其他客戶端機器可以突破這個數量
 

【NO_ERROR錯誤】
1.發生情況:投遞WSASend/WSARecvGetQueuedCompletionStatus函數返回NO_ERROR錯誤
2.解決辦法:WSARecv必須關閉客戶端連接(這時客戶端已經使用了closesocket斷開連接),WSASend則表示沒有錯誤。

 

【WSA_IO_PENDING錯誤】
1.發生情況:投遞WSASend/WSARecvGetQueuedCompletionStatus函數返回WSA_IO_PENDING錯誤
2.解決辦法:WSARecv必須關閉客戶端連接(這時客戶端已經使用了closesocket斷開連接),WSASend則表示沒有錯誤。

 

【投遞WSARecv後GetQueuedCompletionStatus不返回】
1.發生情況:正常投遞WSARecv
2.解決辦法:投遞WSARecv或者WSASend前加一句memset(&context->overlapped, 0, sizeof(OVERLAPPED));  不然很容易造成各種奇怪的問題,當然,如果你是在不復用context的情況下不需要memset, 因爲你每次投遞都是一個新的overlapped, 自然不需要memset。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章