Winsockのエラーコード「WSAENOBUFS(10055)」について

[戻る]
poroporo 2010/05/12(水) 22:48:41
[開発環境]
WindowsXP sp3, vs2003 mfc

はじめまして。
初めてTCP/IPで通信系のプログラムを作成しております。
mfcのCASyncSocketクラスを使用して、サーバ/クライアントを作成し、電文のやり取り
とかしているときに、WSAENOBUFS(10055)が発生する時があります。

MSDNライブラリで検索してみたところ以下のようなことが書かれてました。

・システムのバッファ領域が不足しているか、またはキューがいっぱいなため、ソケット
操作を実行できませんでした。
・Windows ソケットの実装が、バッファのデッドロックをレポートしています。

1つ目は、Windowsのリソースがなくなったからか?と思っています。
その場合、システムのバッファ領域を拡張する方法、キューを拡張する方法があるので
しょうか?
※そもそも2つが、何を示しているかもわかっていませんが・・・

2つ目は、内部的に使用している箇所で起こっていて回避不可能かな?と思っています。


上記のことは、的を得ている解釈でしょうか?
10055の原因について、もっと詳しい方がいらっしゃりましたら、教えていただけません
でしょうか?
よろしくお願いいたします。

poroporo 2010/05/12(水) 23:56:21
ちなみに過去のログにあるものはすでにためしております。

http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200611/06110007.txt

poroporo 2010/05/27(木) 23:02:18
ネットワーク系のプログラムに詳しい人はいらっしゃらないのでしょうか・・・

その後、いろいろ調査しているとタスクマネージャのカーネルメモリが増えていることが確
認できました。
ネットワーク系のカーネルメモリは非ページ領域を使っているようで、
何かしら内部的にネットワーク系のカーネルは非ページ領域のメモリをどれだけ使用してよ
い、となっているのかな〜と解釈しました。

以上です。

2010/05/28(金) 00:09:04
>電文のやり取りとかしているとき
どのような状況でどの関数を使った時にそのエラーが出ていますか?

再現できる自分と相手のやりとりの最低限のソースを提示されると
回答があるかも知れません。
#例外発生時にブレークしたらもう少し詳しく状況が分かるかも知れません。

ググる様「10055 winsock」で検索すると
>バッファが不足している。
>メモリ不足のためWinSockが操作に必要なバッファを確保することができない。
>または、バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。>バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。
こんなのがヒットしましたが心当たりはありませんか?

PATIO 2010/05/28(金) 10:15:29
掲示板の場合、都合よく回答できる人が見に来てくれるとは
限りませんのでレスポンスが付けばラッキーくらいに考えて
おかないと辛いですよ。

仕事が忙しければ1日や2日は見にこれないなんて事も
普通にありえますし。

あと、具体的なコードを提示して何処でそのエラーが出ている
と言うようにしないと詳しい回答は難しいと思います。

仲澤@失業者 2010/05/28(金) 11:35:50
WSAENOBUFS(10055)は、ブロッキングタイプのSend()で発生すると
http://support.microsoft.com/kb/201213/ja
にあります。ただし、対象がNTでした。
この問題が以降のOSで解決されたかどうかは知りません。
上記ページには対策として、
1.非ブロッキングタイプに変更する
2.ブロッキングのままとするが、一回の送信BYTE数を64KByte以内にして
  分割送信する
3.ソケットのバッファを使用せず、ユーザーデータのポインタから
  直接送信するように、ソケットオプションを変更する
とあります。

poroporo 2010/06/01(火) 00:10:38
>なさん

返答ありがとうございます。
最低限のソースとしては以下のような感じです。
#会社から書き込みできないため、うろ覚えのソースですがだいたい以下のような感じで
す。

******************************************************************************

// CAsyncSocketクラスを継承しているクラスがAとした場合の送信メソッドの処理抜粋
です。
その他、オーバーライドするべきOnCloseイベントやOnReceiveイベントはオーバーライド
しています。

bool B::Send(pBuff, pBuffのサイズ)
{
 bool bRet = true;
 For(ブロック数分ループ)
 {
    // pBuffは最大20MBぐらいになります。
       int ret = A->Send(pBuff, pBuffのサイズ )
   
    if(ret != WSAEWOULDBLOCK)
    {
      // エラー処理
      bRet = false;
      break; ←ここで抜ける際の、retがWSAENOBUFS(10055)となります。
    }
 }
 ・・・
 return bRet;
}

******************************************************************************


基本的に、以上のようなソースで送信できる場合と出来ない場合があります。

なさんが指摘してくださっている
>>バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。
>こんなのがヒットしましたが心当たりはありませんか?
ですが、pBuffは基本的に1KBくらいはあるため、心当たりはありません・・・。

poroporo 2010/06/01(火) 00:18:26
>PATIOさん

返答ありがとうございます。


>掲示板の場合、都合よく回答できる人が見に来てくれるとは
>限りませんのでレスポンスが付けばラッキーくらいに考えて
>おかないと辛いですよ。
>
>仕事が忙しければ1日や2日は見にこれないなんて事も
>普通にありえますし。
>
>あと、具体的なコードを提示して何処でそのエラーが出ている
>と言うようにしないと詳しい回答は難しいと思います。

すべておっしゃるとおりですね。
特に具体的なコードを提示しないで申し訳ないです。
今回の件については、検索した結果から1回の送信サイズが大きすぎるため、発生してい
るのであろうと考えまして、送信サイズを小さくするという暫定対応で間に合っているた
め、エラーコードの質問を行っています。そのためにコードを書きませんでした。。

ここの掲示板は、私より経験豊富な方がいっぱいいるため、ご存知の方がいるかな、いた
らラッキーだなくらいに考えていました。

poroporo 2010/06/01(火) 00:24:52
>仲澤@失業者さん

返答ありがとうございます。

>WSAENOBUFS(10055)は、ブロッキングタイプのSend()で発生すると
>http://support.microsoft.com/kb/201213/ja
>にあります。ただし、対象がNTでした。
まさにこの状況であると考えられますね。
しかし、対象がNTでその後のOSで解決されているという記述を見つけられなかったため、調
査を行っています。
ただ、2番目に近い暫定対応(64KBではなく1MBくらいで分割送信しています。)で発生しなく
なっているため、まだ解決されていないのかもしれません。

poroporo 2010/06/01(火) 00:32:20
提示したソースがダメダメすぎますね・・・
修正しました。


******************************************************************************

// CAsyncSocketクラスを継承しているクラスがAとした場合の送信メソッドの処理抜粋
です。
その他、オーバーライドするべきOnCloseイベントやOnReceiveイベントはオーバーライド
しています。

bool B::SendProc()
{
 for(ブロック数分ループ)
 {
   if(Send(pBuff, pBuffのサイズ) == false)
   {
    break;
   }
 }
}

bool B::Send(pBuff, pBuffのサイズ)
{
 bool bRet = true;
  int ret = 0;
 int pos = 0;

  while(pos != pBuffのサイズ)
 {
  
  // pBuffは最大20MBぐらいになります。
   ret = A->Send(pBuff[添え字], pBuffのサイズ )
  
    if( ret == SOCKET_ERROR )
  {
   if(ret != WSAEWOULDBLOCK)
   {
     // エラー処理
     bRet = false;
     break; ←ここで抜ける際の、retがWSAENOBUFS(10055)となります。
   }
  }
    else
    {
   // 送信済みのサイズを計算
    pos += ret;
    }
 }
 ・・・
 return bRet;
}

******************************************************************************

subaru 2010/06/01(火) 10:58:25
1回の送信BYTE数を64KByte以内に、というのは
IPパケットの最大長が65535オクテットだからではないでしょうか。
1回の送信サイズが大きくてもsend内部でうまくやってくれるようですが
なるべく小さいサイズの方が安全ということはあるかもしれません。

ITO 2010/06/02(水) 13:06:36
> しかし、対象がNTでその後のOSで解決されているという記述を見つけられなかったた
> め、調査を行っています。
僕もサイトを見ました。
対象は、NTですが、Ver 4.0です。
一様疑ってみたらどうでしょうか?

poroporo 2010/06/08(火) 23:14:52
>subaruさん

>IPパケットの最大長が65535オクテットだからではないでしょうか。

そうなんですか。上記のことは初めて知りました。
やっぱりTCP/IPの中身をあまり理解しないまま通信のプログラムを組んでいることはよろし
くなさそうですね。。。


>ITOさん

NTはあまり詳しくないのですが、Ver4.0であることは何かITOさんには引っ掛かるものがあ
るのでしょうか?

ITO 2010/06/09(水) 09:45:23
> Ver4.0であることは何かITOさんには引っ掛かるものがあ
> るのでしょうか?
NT のなかでVer4.0は一番バージョンが高く、よりWIN 2000/XPに近いです。
そしてporoporoさんも、
>まさにこの状況であると考えられますね。
とかいてあったので、一度調べてみたらと言う意味で書きました。

GARA 2010/06/10(木) 10:26:39
これまで、ソケット通信プログラムを作成したとき send() のエラー
の 10053 や 10054 は回線状態が悪い時によく見ましたが、10055 は
経験したことがありません。しかしこの経験から 10055 の対策としては
次の方法があると思います・

1.send() がノンブロックなら、ブロックに変えてみる。

2.send() を実行後、少しSleep() して次の send() を実行する。
 Sleep() の時間はいろいろ変えて(1〜10秒?)試してみる。

3.send() がエラーを返したら、同じデータの send() を再試行する。
 試行回数の管理が必要。また、再試行する前に少し Sleep() が必要。

4.recv() 側は応答を返すようにして、send() 側は応答を受信してから
 次の send() を実行する。send() 単位に送信長がわかるヘッダを
 設け、recv() 側が応答を返すタイミングを判断できるようにする。

1-> 4の順に実装は難しくなりますが、通信の信頼性は高くなります。
(1と2は本当の解決策ではありませんが、10055 の原因の切り分け
には役立つと思います。)
要求されるアプリケーションの信頼性により、2〜4のどれかを選択
するのがよいと思います。

回線状態(輻湊)が悪い時は下位レベルのソケット通信サービスの送信が
完了しないうちに上位アプリケーションから send() でソケット通信
サービスに送信要求を発行され、下位の送信キューが一杯になり
10055 になることが考えられます。
(回線の実効通信速度は常に上下しています。)

poroporo 2010/06/10(木) 22:42:11
[[解決]]
>ITOさん

そういう意味でしたか。XPについてはっきり記述が見つかりませんでしたが、NT4.0でも
根本的に直っているという記述も見つかっていないため、Windowsは全部この状態になる
ことは仕方がないことかもしれませんね。



>GARAさん

>1.send() がノンブロックなら、ブロックに変えてみる。
現状、ブロッキングしています。

>2.send() を実行後、少しSleep() して次の send() を実行する。
> Sleep() の時間はいろいろ変えて(1〜10秒?)試してみる。
これも、200ミリ秒ぐらいですが、Sleepを入れています。

>3.send() がエラーを返したら、同じデータの send() を再試行する。
> 試行回数の管理が必要。また、再試行する前に少し Sleep() が必要。
これは、10035(WSAEWOULDBLOCK)であれば再試行しています。
その他のエラーの場合は、MSDNの通信系のエラーコードのヘルプを読んで再試行しても無
駄だと判断しました。

>4.recv() 側は応答を返すようにして、send() 側は応答を受信してから
> 次の send() を実行する。send() 単位に送信長がわかるヘッダを
> 設け、recv() 側が応答を返すタイミングを判断できるようにする。
これは、現状の通信規定に違反してしまうため、できません。


>回線状態(輻湊)が悪い時は下位レベルのソケット通信サービスの送信が
>完了しないうちに上位アプリケーションから send() でソケット通信
>サービスに送信要求を発行され、下位の送信キューが一杯になり
>10055 になることが考えられます。
>(回線の実効通信速度は常に上下しています。)
結局、こういうことが原因であると、皆さまの回答や、調査した結果、判断しました。

この質問では、「システムのバッファ領域を拡張する方法、キューを拡張する方法がある
のでしょうか?」を、知識として知りたかったのですが、ここらへんで解決とさせていた
だきます。

皆さま、ありがとうございました。

古いスレッドには追加発言できません。