[開発環境]
WindowsXP sp3, vs2003 mfc
はじめまして。
初めてTCP/IPで通信系のプログラムを作成しております。
mfcのCASyncSocketクラスを使用して、サーバ/クライアントを作成し、電文のやり取り
とかしているときに、WSAENOBUFS(10055)が発生する時があります。
MSDNライブラリで検索してみたところ以下のようなことが書かれてました。
・システムのバッファ領域が不足しているか、またはキューがいっぱいなため、ソケット
操作を実行できませんでした。
・Windows ソケットの実装が、バッファのデッドロックをレポートしています。
1つ目は、Windowsのリソースがなくなったからか?と思っています。
その場合、システムのバッファ領域を拡張する方法、キューを拡張する方法があるので
しょうか?
※そもそも2つが、何を示しているかもわかっていませんが・・・
2つ目は、内部的に使用している箇所で起こっていて回避不可能かな?と思っています。
上記のことは、的を得ている解釈でしょうか?
10055の原因について、もっと詳しい方がいらっしゃりましたら、教えていただけません
でしょうか?
よろしくお願いいたします。
ちなみに過去のログにあるものはすでにためしております。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200611/06110007.txt
ネットワーク系のプログラムに詳しい人はいらっしゃらないのでしょうか・・・
その後、いろいろ調査しているとタスクマネージャのカーネルメモリが増えていることが確
認できました。
ネットワーク系のカーネルメモリは非ページ領域を使っているようで、
何かしら内部的にネットワーク系のカーネルは非ページ領域のメモリをどれだけ使用してよ
い、となっているのかな~と解釈しました。
以上です。
>電文のやり取りとかしているとき
どのような状況でどの関数を使った時にそのエラーが出ていますか?
再現できる自分と相手のやりとりの最低限のソースを提示されると
回答があるかも知れません。
#例外発生時にブレークしたらもう少し詳しく状況が分かるかも知れません。
ググる様「10055 winsock」で検索すると
>バッファが不足している。
>メモリ不足のためWinSockが操作に必要なバッファを確保することができない。
>または、バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。
の
>バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。
こんなのがヒットしましたが心当たりはありませんか?
掲示板の場合、都合よく回答できる人が見に来てくれるとは
限りませんのでレスポンスが付けばラッキーくらいに考えて
おかないと辛いですよ。
仕事が忙しければ1日や2日は見にこれないなんて事も
普通にありえますし。
あと、具体的なコードを提示して何処でそのエラーが出ている
と言うようにしないと詳しい回答は難しいと思います。
WSAENOBUFS(10055)は、ブロッキングタイプのSend()で発生すると
http://support.microsoft.com/kb/201213/ja
にあります。ただし、対象がNTでした。
この問題が以降のOSで解決されたかどうかは知りません。
上記ページには対策として、
1.非ブロッキングタイプに変更する
2.ブロッキングのままとするが、一回の送信BYTE数を64KByte以内にして
分割送信する
3.ソケットのバッファを使用せず、ユーザーデータのポインタから
直接送信するように、ソケットオプションを変更する
とあります。
>なさん
返答ありがとうございます。
最低限のソースとしては以下のような感じです。
#会社から書き込みできないため、うろ覚えのソースですがだいたい以下のような感じで
す。
***************************************************************************
// 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くらいはあるため、心当たりはありません・・・。
>PATIOさん
返答ありがとうございます。
>掲示板の場合、都合よく回答できる人が見に来てくれるとは
>限りませんのでレスポンスが付けばラッキーくらいに考えて
>おかないと辛いですよ。
>
>仕事が忙しければ1日や2日は見にこれないなんて事も
>普通にありえますし。
>
>あと、具体的なコードを提示して何処でそのエラーが出ている
>と言うようにしないと詳しい回答は難しいと思います。
すべておっしゃるとおりですね。
特に具体的なコードを提示しないで申し訳ないです。
今回の件については、検索した結果から1回の送信サイズが大きすぎるため、発生してい
るのであろうと考えまして、送信サイズを小さくするという暫定対応で間に合っているた
め、エラーコードの質問を行っています。そのためにコードを書きませんでした。。
ここの掲示板は、私より経験豊富な方がいっぱいいるため、ご存知の方がいるかな、いた
らラッキーだなくらいに考えていました。
>仲澤@失業者さん
返答ありがとうございます。
>WSAENOBUFS(10055)は、ブロッキングタイプのSend()で発生すると
> http://support.microsoft.com/kb/201213/ja
>にあります。ただし、対象がNTでした。
まさにこの状況であると考えられますね。
しかし、対象がNTでその後のOSで解決されているという記述を見つけられなかったため、調
査を行っています。
ただ、2番目に近い暫定対応(64KBではなく1MBくらいで分割送信しています。)で発生しなく
なっているため、まだ解決されていないのかもしれません。
提示したソースがダメダメすぎますね・・・
修正しました。
***************************************************************************
// 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;
}
***************************************************************************
1回の送信BYTE数を64KByte以内に、というのは
IPパケットの最大長が65535オクテットだからではないでしょうか。
1回の送信サイズが大きくてもsend内部でうまくやってくれるようですが
なるべく小さいサイズの方が安全ということはあるかもしれません。
> しかし、対象がNTでその後のOSで解決されているという記述を見つけられなかったた
> め、調査を行っています。
僕もサイトを見ました。
対象は、NTですが、Ver 4.0です。
一様疑ってみたらどうでしょうか?
>subaruさん
>IPパケットの最大長が65535オクテットだからではないでしょうか。
そうなんですか。上記のことは初めて知りました。
やっぱりTCP/IPの中身をあまり理解しないまま通信のプログラムを組んでいることはよろし
くなさそうですね。。。
>ITOさん
NTはあまり詳しくないのですが、Ver4.0であることは何かITOさんには引っ掛かるものがあ
るのでしょうか?
> Ver4.0であることは何かITOさんには引っ掛かるものがあ
> るのでしょうか?
NT のなかでVer4.0は一番バージョンが高く、よりWIN 2000/XPに近いです。
そしてporoporoさんも、
>まさにこの状況であると考えられますね。
とかいてあったので、一度調べてみたらと言う意味で書きました。
これまで、ソケット通信プログラムを作成したとき 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 になることが考えられます。
(回線の実効通信速度は常に上下しています。)