トップページ > 過去ログ > 記事閲覧
通信待ち
名前:ライブラリ使用者 日時: 2007/12/10 00:49

 しばらくぶりです。 ある程度のレベルでアプリは完成したのですが、通信接続エラー(待ち?)が発生する場合があります。 発生の条件としては、多人数接続時です。 あとはPC性能は悪い人ほどでしょうか? 原因特定したいのですが、どうにも解析しようがありません。(私自身の環境では再現しません 下記ソースの一部ですが、TIMEOUTを10000(10秒)にすると、接続人数が30人以上になるとこのループが終わりません。(ほぼ全員で、タイムアウトすらしない そこで、60000(60秒)とすると、この問題は解決しました。(最大60名弱まで同時接続チャットできました。 が、何名かはこのループが終わらない人が出てきてしまいます。 状況としてはこのループがまわっていない感じです。 GetNetWorkDataLength()内でも処理待ちなような事は発生しえるのでしょうか? ※エラーの発生ですが、ある時以降全員がなるのではなく、特定の人がなり続けます。 接続できない人がいても、その後に接続試行して繋がる人もおります。 アプリの概要として、スレッド処理にて通信処理してる部分もありますがCriticalSection()を用い、排他的に処理ができていると思います。 長くなりましたが、対応のご助言頂けないでしょうか //タイマー開始 timer = GetNowCount(); while((GetNowCount() - timer) < TIMEOUT) { if(ProcessMessage() == -1) { //エラー処理抜け return 0; } if(CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break;//ESCキーが押されたらループから抜ける } DataLength = GetNetWorkDataLength(GetNetHandle()); if(DataLength > 0) { //データ取得用バッファ確保 char* buf = new char[DataLength]; //接続キャラデータの取得 NetWorkRecv(GetNetHandle(),buf,DataLength); //このタイミングで来た、キャラデータ以外は無視する int DataPos = 0; do { pmsg = (SendMessageType*)&buf[DataPos]; switch(pmsg->Type) { case SMT_ALLEND: //取得完了 Finish = TRUE; break; default: //チャットデータなどはすべて無視する break; } //次ぎのメッセージまで移動する DataPos += pmsg->PacketSize; } while(DataLength > DataPos); delete [] buf; if(Finish) break; }

Page: 1 | 2 | 3 |

Re: 通信待ち ( No.11 )
名前: 日時:2007/12/19 16:43

こちらのスレッドに通信バッファのクリアが 紹介されていたので、張っておきます。 ttp://hpcgi2.nifty.com/natupaji/bbs/patio.cgi?mode=view&no=661
Re: 通信待ち ( No.12 )
名前:ライブラリ使用者 日時:2007/12/21 01:10

知り合いと連絡がつき、再現させました。 予想してた中で一番やっかいなとこなのかな。 最初のあげたソースの DataLength = GetNetWorkDataLength(GetNetHandle()); 部分にて、DataLength=0を返すパターンが非常に多いケースでした。 勿論、時間経過で少しずつデータ受信しています。 (ですので、正確には無限ループではない 戻り値0ですので、未受信データがない状態となっているってことですがなぜこれが発生するかが不明。。 ここでの処理をもう少し詳細に話しますと、チャットプログラムの既存接続者のデータ一覧を受信させています。 1人当り500バイト程度の情報(名前など)を持たして、それを私の環境では、ダミーで500人接続者がいる時に発生しました。(100人では発生せず データ量的には、500×500=250000バイト程度以上 データ送信NetWorkSendが完了していて、未受信のデータGetNetWorkDataLengthがとれないパターンというのがあるのでしょうか。。
Re: 通信待ち ( No.13 )
名前: 日時:2007/12/21 11:32

>DataLength=0を返すパターンが非常に多いケース >時間経過で少しずつデータ受信しています。 なるほど、だとすればやはりその後の受信の際に 欲しいデータ分のサイズがまだ受信されていない 場合があるわけなので、 「データが何かあったら」受信を開始ではなく 「欲しいデータのサイズ以上を受信したら」とする 必要がありそうです。 最初のコードの該当部分は、 if(DataLength > 0) 私のコードの該当部分が if ( DataLength >= sizeof(PK_HEADER) ) のようなところです。 if(DataLength > 0)これだと、欲しいデータを 受信し終わっていなくても、データを取り出そう とするため、その後の受信処理の while(DataLength > DataPos);このループでハマる 可能性が非常に高そうです。 私のコードの場合は欲しいデータのサイズがまだ 受信されていない場合はタイムアウトまで受信する のを待ち、それでも欲しいデータのサイズに届かな ければタイムアウトとして返すようになっています。 >データ送信NetWorkSendが完了していて、 >未受信のデータGetNetWorkDataLengthが >とれないパターンというのがあるのでしょうか NetWorkSendが完了していても未受信のデータが あるかという解釈でよいでしょうか? であれば、もちろん考えられます。
Re: 通信待ち ( No.14 )
名前:Will 日時:2007/12/21 11:41

推測になりますが「NetWorkSendが完了」は「相手の受信バッファにデータが入った」では無いと思います。(そんな機能はかなり非現実的) 通信環境とか受信側のMTUとかにも影響されると思いますが多少のディレイが発生するのはごく当たり前のことかと。
Re: 通信待ち ( No.15 )
名前:ライブラリ使用者 日時:2007/12/22 00:35

>「欲しいデータのサイズ以上を受信したら」とする 必要がありそうです。  デバッグしていて気付いたのですがSendしたサイズ以下の最中は常にDataLength=0を返しているようです。つまり未受信バッファが、(例)sizeof(PK_HEADER)より大きくなった場合にif(DataLength>0を満たし受信します) (これはNetWorkSendにて送信するデータ量を指定しているため、受信に対してDxLib側で補正が掛かっているんじゃないかと思ってます) >Willさん >多少のディレイが発生するのはごく当たり前のことかと 多少ではないのですよ。。 通常1秒程度(以下)なのですが、10分以上入れない。 (上記検証するまで無限ループの疑いもあったので、最後まで待ったことはないです。途中までの経過から概算で1,2時間待てば接続はするのかもしれません) また、実際に人を呼んでのテストをした時はアプリ再起動、PC再起動でも同現象が続き、テストしていた4,5時間の間に一度もログインできなかった人が出た始末です。 極論ですが、ディレイや動作が重いということは許容(少なくとも現状)して、まず接続できないというケースを無くしたいと思ってます。
Re: 通信待ち ( No.16 )
名前: 日時:2007/12/22 14:45

>Sendしたサイズ以下の最中は常にDataLength=0を >返しているようです 送信側が送信したデータはNetWorkSendを行った 直後に相手に送られるわけではありません。 送られるタイミングは場合によってまちまちです。 これは、 >NetWorkSendにて送信するデータ量を指定している >ため、受信に対してDxLib側で補正が掛かっている にも関係してきますが、送信されるデータや受信 されるデータのサイズ補正はDXライブラリの 仕様ではなく、WinSockやソケット関連の関数の 仕様でしょう。 単純にまだ送信待ちか、受信側にキャッシュされて いないだけではないかと。 >未受信バッファが、(例)sizeof(PK_HEADER)より >大きくなった場合にif(DataLength>0を満たし受信 言葉足らずかな。。受信バッファがPK_HEADERサイズ 以上になれば、「参照」し今度は参照したヘッダの サイズ部分から、受信バッファがパケットのサイズ を超えているかを判断しヘッダ部分とデータ部を あわせて「受信」するということです 単純に「DataLength>0」と比較したのでは、 ヘッダ部分が受信されていても、データ部の 全てを受信しているとは限らないためです。
Re: 通信待ち ( No.17 )
名前:Will 日時:2007/12/22 16:50

>(これはNetWorkSendにて送信するデータ量を指定しているため、受信に対してDxLib側で補正が掛かっているんじゃないかと思ってます) TCP/IP仕様を考えればそうなっていると思います。 1バイトでも受信→即アプリに通知では、データの刈り取りが難しくなるので。 >通常1秒程度(以下)なのですが、10分以上入れない。 入れない、って言うのはどういうことでしょうか? 問題の切り分けをしたいのですが、通信接続待ちエラーが発生するとき対象のPCから1バイトも受信が出来ていないのですか? それとも、何らかのデータは受信できているけど間違ったデータであるため接続の条件に引っかからないのどちらでしょうか? 私がこういった問題に対処する場合は 1.サーバ、クライアント双方に送信/受信データをファイルダンプする機能をつける 2.LANアナライザーを使って実際にLAN上に流れているデータを確認する どういうデータを送受信したとき問題が発生するのかを見極めてからソースを追っかけたほうが解決は早いと思います。(私の経験上)
Re: 通信待ち ( No.18 )
名前:管理人 日時:2007/12/22 22:01

通信プログラムを殆ど組んだことの無い人間ですので詳しい方にお任せします。m(_ _;m 因みにTCP/IPの仕様ではどうなっているかわかりませんが、DXライブラリの 通信機能の仕様では、NetWorkSend で送信されたデータの先頭には送信された データのサイズが付けられていて、DXライブラリの受信プログラム側で そのサイズを確認した上で受信データサイズが NetWorkSend で送信された データサイズ以上になるまで GetNetWorkDataLength 関数の戻り値には 受信データとして反映されないようになっています。(例えば NetWorkSend で 1MB 送信して、受信側では最初は 512KBしか受信出来なかった場合、 残りの 512KBを受信できるまで GetNetWorkDataLength 関数の戻り値は 0 のまま) TCP/IPの仕様が Willさんのご発言の通りであれば無駄な処理ですが。orz
Re: 通信待ち ( No.19 )
名前: 日時:2007/12/23 03:19

>データのサイズ補正 意味を履き違えてました。(失礼 補正って言うのがデータが、ぶつ切りにされて 送受信される事を指してるとおもってた。。。 #DxLib側で補正しているで正しいと言うことですね > 残りの 512KBを受信できるまで > GetNetWorkDataLength 関数の戻り値は 0 のまま) このような機能がDXライブラリには既にある ようなので、固定長のデータであれば、タイプ だけ見てサイズの判断はしなくてもOKですね >無駄な処理 TCP/IP的には実際に送受信されるデータは、 指定サイズそのものではないので、DXライブラリ の機能は無駄ではないと思います。 Willさんの意見にありますが、 少し問題を整理したほうがよさそうです。 最初提示されたソースも多少変わっていると 思いますし。 DXライブラリの仕様が上記ならば、 受信できたタイプが何なのかは画面にでも出せば わかりそうですが、何バイト受信できているか 利用者側から確認するのは少し難しそうです。 まずは、サーバとクライアントがそれぞれ、 異常な動作のとき、どのような挙動をしているかが まだ、はっきりしていないので、そこから手繰る方 がいい気がします。 これはログを入れてどういう動作をしてるか みるしかないですね。 その次に、パケット内容などをダンプして、 なぜその挙動になるのかを突き止めなければ なりません。 蛇足ですが。 Sendするときに、データ量的には、500×500=250000 バイトを指定すると受信側も、この受信サイズを 超えるまでは、 GetNetWorkDataLength は0を帰す ことになりますが、これでタイムアウトしてる 可能性は。。。?
Re: 通信待ち ( No.20 )
名前:ライブラリ使用者 日時:2007/12/23 15:36

様々なご意見有難う御座います。 まとめながら個別にレスつけていきます。 管理人さんの内容からGetNetWorkDataLengthの戻り値が0を返す点についてまでは問題ないと思われます。 受信させた結果ですが、3パターンほど確認できました。 1.正常終了するパターン 2.データを全て受信しているが、ループを抜けないパターン =ソースのSMT_ALLENDがこないパターン。で且つ以後GetNetWorkDataLengthがタイムアウトまで0になる (データ受信終わっているため、SMT_ALLENDが来ていないと判断) 3.データを全て受信しているが、DataPos += pmsg->PacketSize のサイズが0のパターン (無限ループ) 上記はクライアント側にて、 データの総受信量(DataLengthの和)、 データの受信回数(GetNetWorkDataLength>0)、 データ処理するループ回数(do while())、 受信完了したユーザ数(switch()の1パターン) に対してログを仕込み確認しました。 補足 ログの計測結果ですが、 1.のパターンでは 126000バイト、1回、500回、500人 2.のパターンでは 126000バイト、2回、1回、3**人 (1回目のRecvで(3**-1)人分のデータを取得しているのだと思います) 3.のパターン 126000、2回、無限ループ、3**人 ※350人分のデータ付近でばらつき ここまでで分かった事。 ・データはすべて受信し終っていて、受信データ、及びその処理にて問題がある。 データの扱いについてですが、データはvectorにて管理しており一括して送信はしておりません。 (構造体と異なり、データが連続していないためvectorのポイントだけでは無理でした。ひょっとしたらできるのかもしれませんが、やり方が分かりませんでした) そのため、1回の送信にて1名分のデータを送信し、それをループするという処理をしております。 (500×252:1名あたり252バイトでした。。) ですので、受信側も252バイト単位にて処理されております。 ※1度も受信が行われず、タイムアウトは発生しておりません。 次はパケットの中身を確認してみます。
Re: 通信待ち ( No.21 )
名前:ライブラリ使用者 日時:2007/12/23 19:06

ダンプの結果 差はネットワークハンドルの値以外ありませんでした。 ただ、正常時は1回の受信ですべて処理が終わっていますが、異常時は2回以上に分かれているようです。 もう少し、ダンプのパターンを見てみようと思います。
Re: 通信待ち ( No.22 )
名前: 日時:2007/12/25 13:33

再度確認しますが、最初に私が指摘したコードの DataPos += pmsg->PacketSizeはまだあるという 事でしょうか?このコードは非常にまずいです。 そもそものシーケンスが何かまずい気がします。 いま期待している動作を箇条書きでよいので、 どのような意図でコードを書いているか、 説明してもらえ無いでしょうか? #できれば最新のコードと説明が良いですが。。。 DXライブラリは指定分のサイズしか受信バッファ からは取り出さないはずなので、そもそもの内側の ループが何故必要なのか少し疑問です。 アプリケーションの受信したあと動作がおかしいと いくらパケットの内容をダンプしても問題は解決 できないと思いますし。
Re: 通信待ち ( No.23 )
名前:Will 日時:2007/12/25 14:00

>管理人さんへ ごめんなさい、言葉足らずで変な誤解を与えてしまったようです。 TCPはプロトコル自身がパケット分割を行いパケット範囲内であれば複数の電文を一括して送信したり、 パケット以上の送信データであれば一つの電文複数に分けて送信したりします。 そのため、アプリ(DXライブラリ)側で受信データが規定の長さになるまで待っているのは正しいと思います。 ※私はWindows環境でのTCP/IPをした事が無い(Winsockを使ったとこがない)ので、Windows環境では上記の通りではないかもしれませんが。
説明その1 ( No.24 )
名前:ライブラリ使用者 日時:2007/12/26 00:08

こちらの説明が不十分のようでしたが、今現在はNG発生の原因を探っている所です。 そのため、ソース自体は最初の記載したままでダンプなど確認しています。  始めに通さんにご指摘頂いたように、pmsg->PacketSize=0になるパターンなど確かにソース上で見つかったバグですが、この時点ではあくまで潜在バグです。  そのため、問題提起時点でなにが起きてるか分かりませんでしたので、当時のソースにて何が起きていたが探っていた状態です。そして今回のダンプからご指摘のあった箇所にてエラー発生しているという事はほぼ確実と思われます。  ただ、そのパターンに何故陥るかと言う事は依然として不明のままですが。。(今はここが一番重要な所と考えています。) 指摘頂いた点に対して、値を確認すればNGをNGとして捉えることが可能と思います。が、それはNGとして認識するようにしただけであって問題の解決にはならないと思います。 (勿論、NGを出せるようになることでOKになるまで試せばいいという解釈もできますが、出来る限りNGの原因を根本から直したいと思っています) ※pmsg->PacketSize=0 時に無限ループになるから値0の時ループを抜ける処理を追加するのではなく、pmsg->PacketSize=0となるパターンを発生させなくしたいです。 ソースですが、一気に書いても長くなってしまうので何回かに分けて記載いたします。
説明その2 ( No.25 )
名前:ライブラリ使用者 日時:2007/12/26 00:09

サーバー側のソースです。 //送受信データ型 struct SendMessageType { int Type; //送受信データタイプ int PacketSize; //SendMessageTypeの全サイズ char data[1]; //データ詳細 }; SendMessageType* pmsg_all; //送信用データ vector<CharaInfo>::iterator it = g_CharaInfo.begin(); for(it = g_CharaInfo.begin(); it != g_CharaInfo.end(); it++) { pmsg_all->Type = SMT_ALL; pmsg_all->PacketSize = size; ::memcpy(pmsg_all->data,(void*)it,sizeof(CharaInfo)); //現在の接続者データ一覧を送信する NetWorkSend(handle,pmsg_all,size); } pmsg_all->Type = SMT_ALLEND; pmsg_all->PacketSize = size; ::memcpy(pmsg_all->data,&buff_add,sizeof(CharaInfo)); //現在の接続者データ一覧を送信完了を通知する NetWorkSend(handle,pmsg_all,size); ・ユーザ情報はvectorにて管理しております。 ・vectorは配列と異なり、データが連続していなかったためイテレータにて各要素にアクセスし、 その要素単位にてNetWorkSend()しております。 つまり、その時点での接続人数分のループがまわる事になります。 ・そして最後に、SMT_ALLENDを送りユーザ一覧の取得が終わった事を確認させるようにします。
Re: 通信待ち ( No.26 )
名前:ライブラリ使用者 日時:2007/12/26 00:09

クライアント側のソースです。 int CChat::GetAllCharaData() { int re; int DataLength = 0; int timer; int count = 0; int size = sizeof(SendMessageType)+sizeof(CharaInfo); SendMessageType* pmsg; BOOL Finish = FALSE; re = RequestNetworkState(); //デバッグ用 FILE* fp; char state[100]; strcpy(state,""); int GetDataTotal = 0; int LoopSMTCount = 0; int LoopTimeCount = 0; //::MessageBox(NULL,"これからキャラデータ取得します:テスト@",APP_TITLE,MB_OK); if(re) { //タイマー開始 timer = GetNowCount(); //ダンプ取得用(1回の起動分のみ) fp = fopen("./dump/dump.txt","wb"); while((GetNowCount() - timer) < TIMEOUT) { re = (GetNowCount() - timer); if(ProcessMessage() == -1) { //エラー処理抜け return 0;//** } if(CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break;//ESCキーが押されたらループから抜ける } DataLength = GetNetWorkDataLength(GetNetHandle()); if(DataLength > 0) { //受信動作継続中確認 GetDataTotal += DataLength; LoopTimeCount++; sprintf(state,"Loading中:%d/%d",GetDataTotal,LoopTimeCount); DrawBox(100,300,300,320,0xFFFFFF,TRUE); DrawString(100,300,state,GetColor(0,0,255)); //!!!!!ここでキャラ情報が数人分まとめてきている //データ取得用バッファ確保 char* buf = new char[DataLength]; if(buf == NULL) { //メモリ確保失敗 ::MessageBox(NULL,"データ取得用バッファ確保失敗",APP_TITLE,MB_OK); } //接続キャラデータの取得 re = NetWorkRecv(GetNetHandle(),buf,DataLength); //if(NetWorkRecv(GetNetHandle(),buf,DataLength)) //{ //エラー発生時(要求データ量よりも一時記憶バッファに保存されている データ量の方が少ない場合) if(re) ::MessageBox(NULL,"接続キャラデータの取得失敗",APP_TITLE,MB_OK); //} //else //{ //pmsg = (SendMessageType*)buf; //このタイミングで来た、キャラデータ以外は無視する //データダンプ int i; for(i=0; i<DataLength; i++) { fputc(buf[i],fp); } fputs("DUMP_END",fp);//1回分毎に区切る int DataPos = 0; LoopSMTCount = 0; do { //受信動作継続中確認 LoopSMTCount++; sprintf(state,"SMT_Loop:%d,%d",LoopSMTCount,count); DrawBox(100,320,300,340,0xFFFFFF,TRUE); DrawString(100,320,state,GetColor(0,0,255)); pmsg = (SendMessageType*)&buf[DataPos]; switch(pmsg->Type) { case SMT_ADD: //この場合も処理いるね。 case SMT_ALL: //キャラ画像はあとでまとめて実施(GameMain::ReadAllCharHandle) AddCharaData((CharaInfo&)pmsg->data); count++; break; case SMT_ALLEND: //取得完了 Finish = TRUE; break; case SMT_DELETE: break; default: //チャットデータなどはすべて無視する break; } //次ぎのメッセージまで移動する DataPos += pmsg->PacketSize; } while(DataLength > DataPos); delete [] buf; if(Finish) break; //} } } fclose(fp); } else { //ここは来ないルート(既に接続済み) ::MessageBox(NULL,"認証エラー バージョンが古い可能性があります。\nバージョンを1度ご確認ください。",APP_TITLE,MB_OK); return FALSE; } ・while((GetNowCount() - timer) < TIMEOUT)文では、時間制限を区切ってユーザ情報を取得します。 (サーバー側が送信したユーザ情報を取得する処理) ・DataLength = GetNetWorkDataLength(GetNetHandle());  受信しているデータがあるのであれば、データ処理に入ります。 ・re = NetWorkRecv(GetNetHandle(),buf,DataLength);  受信した実データの取得。  通さんのサンプルとは異なり、受信したデータを一度に取得しています。  そしてその後、データをヘッダ情報に従い切り分けつつ処理していきます。 ・do while(DataLength > DataPos)文  取得したデータをSendMessageType毎に切り分けます。 ・switch(pmsg->Type)文  SendMessageTypeのTypeに従った処理を行います。  SMT_ALL時、ユーザ情報はAddCharaData((CharaInfo&)pmsg->data); にて、クライアント側のvectorにpush_backされます。 ※データの中身の処理は以下のような構想に基づいています。 受信したデータを仮に1000バイト、data[999]とします。 1単位の情報(ヘッダ、実データ込み)を100バイト固定とします。 pmsg = (SendMessageType*)&buf[DataPos]; そうすると、最初のデータはdata[0]〜data[99]となります。 内訳は SendMessageTypeのType:data[0]〜data[3] PacketSize:data[4]〜data[7] data:data[8]〜data[99] ここでTypeにはSMT_ALLなど PacketSizeは100(仮) dataは実データ  を指します。 これを1ループ(do while文)とし、次にデータのオフセット処理をします。 DataPos += pmsg->PacketSize; すると、次ぎのデータはdata[100]〜data[199]となります。 data[200]〜data[299] data[300]〜data[399] ・・・ data[900]〜data[999] これをオフセット値が1000バイト(仮)を越えるまでループさせる。 この場合ですと、SMT_ALLが9回、最後がSMT_ALLEND となることで9人分のユーザ情報を取得することなります。 >そもそもの内側のループが何故必要なのか少し疑問です。 上記のように、通さんとのサンプルとの差異は ・受信したデータを、ヘッダ情報に従い切り分け処理を繰り返すか(私の処理) ・ヘッダ情報に従い必要なバイト数分のみデータを受信させる事を繰り返すか(通さんの処理)  ただし、通さんのサンプルでは1度実データを取得したら終わりで、繰り返すようにはなっていませんが。 のように、処理させる順番の差だと思っています。(している処理は同じ)
Re: 通信待ち ( No.27 )
名前: 日時:2007/12/26 03:35

書けと言って置いて、忙しくてなかなか じっくり読めなくてすみません。。。。;; まだ流し読み程度ですが、 いくつか疑問に思ったことを挙げます。 あくまでも実行した結果ではなく、 論理的な動作です。 while((GetNowCount() - timer) < TIMEOUT) {  //この時点では数名分のサイズが帰ってくる  //(送信予定の人数分ではなく送信された実人数分のみ)  DataLength = GetNetWorkDataLength(GetNetHandle());  if(DataLength > 0)  {   //受信動作継続中確認   GetDataTotal += DataLength;   //!!!!!ここでキャラ情報が数人分まとめてきている   //とありますが、実際の以下のコードで確保されるのは   //送った人数分とは限りません(サーバ側が1名ずつ送信しているので   //送信途中の可能性もあるきがします)   char* buf = new char[DataLength];   re = NetWorkRecv(GetNetHandle(),buf,DataLength);   int DataPos = 0;   LoopSMTCount = 0;   do{    //受信動作継続中確認    LoopSMTCount++;    pmsg = (SendMessageType*)&buf[DataPos];    switch(pmsg->Type){    case SMT_ADD:    case SMT_ALL:     AddCharaData((CharaInfo&)pmsg->data);     count++;     break;    case SMT_ALLEND:     Finish = TRUE;//取得完了     break;    default:     break;    }    //サーバ側ではPacketSizeに入っているのはデータ部のみのサイズのようですが、    //bufにはTypeとPacketSizeのサイズ分のデータがあるので    //このときのPosが例えば以下のようなデータだとどうなるのか。    //[0 - 3 ] Type :SMT_ALL    //[4 - 7 ] PacketSize :sizeof(CharaInfo) が92の場合    //[8 - 99] CharaInfo    //DataPosは 0 -> 92となります。    //DataLengthは100なので100>92でループ続行。    //(SendMessageType*)にキャストしますが、DataPosがさしているのは    //[92 - 95 ] Type? : ????    //[96 - 100] PacketSize? : ????    //[??? - ???] ---- : ????    //2回目のpmsg->PacketSizeはぎりぎりアクセス範囲内ですが、    //ここがキャラデータでちょうど数値的に0だったりすると無限ループ    //それ以外の場合は見かけ上受信は成功する。    //実際にはDataPos += pmsg->PacketSize+sizeof(int)+sizeof(int)が必要では?    //次ぎのメッセージまで移動する    DataPos += pmsg->PacketSize;   }while(DataLength > DataPos);  } }
Re: 通信待ち ( No.28 )
名前:Will 日時:2007/12/26 10:20

私もソース拝見させていただきましたが通さんがほとんど指摘されていますので、それ以外で気づいたところ(不具合とは直接関係しないですが) re = (GetNowCount() - timer); ここでreに入れた値はどこも使ってないよ ダンプ処理は間違っていると思います。 ダンプファイルをバイナリオープンしていますが実際にはテキストデータを出力しています。 バイナリデータをテキストデータでダンプしたいのであれば fputc(buf[i],fp); ではなくて fprintf(fp, "0x%02x ", buf[i]); としないと正しいデータは見れないと思います。
Re: 通信待ち ( No.29 )
名前:ライブラリ使用者 日時:2007/12/26 21:54

深夜にこれだけのソースを呼んで頂き有難うございます。そして、すいません。肝心なとこを省略してしまいましたね。 No25のサーバー側のソースですが、 size = sizeof(SendMessageType) + sizeof(CharaInfo); pmsg_all = SetSMTMemory(size); このサイズを pmsg_all->PacketSize = size; として持たせていますので、タイプ、ヘッダ情報、実データのサイズとして処理しています。 (実は+1なのはご割愛ください) あとソース上のコメントへと指摘ですが、実動作でのコメントです(−− (手加えていませんので) ダンプのとこでも記載しましたが、サーバー、クライアントのタイムラグ、及びPC環境を踏まえて動作させた結果、1回ですべて受信バッファに溜まっています。 (例外としてNGケースの場合は溜まりきっていないようですが、、 勿論、サーバー側で送信中の場合もあると分かっています。そのためにwhile()文で繰り返し受信させています。また、前回管理人さんが書かれたように、送信バイトに達しない場合は未受信バッファとカウントされないはずですので、半端な状態で受信することもないはずです。 また、1回のdowhile()のループで全て受信しない場合も考慮して、 case SMT_ALLEND: //取得完了 Finish = TRUE; が来るまではループ継続させています。 前回の例でいいますと、 data[900]〜data[999] がSMT_ALLの場合、 10人分のユーザ情報を取得後 再度、DataLength = GetNetWorkDataLength(GetNetHandle());の受信待ちに戻り、SMT_ALLENDが来るまで次ぎのdata[***]を処理し続けます。 >Willさん re = (GetNowCount() - timer); これですが、デバッグ用にreの値を見るだけのために置いてあるものです>< そもそもの問題で、ループが終わらないと言う事なのでwhile((GetNowCount() - timer) < TIMEOUT) このタイムアウト処理が正しく動作しているか確認しています。 ダンプ処理ですが、あー確かにテキスト形式での出力させちゃってますねぇw これをバイナリエディタで中身確認していた為、特に気にしていませんでした。
Re: 通信待ち ( No.30 )
名前: 日時:2007/12/28 09:54

返信遅れました。 意図したこと事がやっと見えてきました。。。(遅; > size = sizeof(SendMessageType) + sizeof(CharaInfo); 了解です。 後は、実行している環境の問題という可能性も無くは 無いと思いますが。。。 >例外としてNGケースの場合は溜まりきっていない ここから手繰るしかなさそうですね。。。 一応確認しますが、CharaInfoは構造体名で良い? ポインタ変数などのsizeofだとそもそもマトモに 動かないと思うので無いとは思いますが。。。 念のため。 >(実は+1なのはご割愛ください) 少し細かいですが、 C/C++ではアライメントによるサイズの最適化が 起こるのでこの場合は4になる可能性が高いです。 構造体名にsizeofを使っている以上、上記の ような間違いが無ければ問題はなさそうですが。 #とりあえず、フリダシにもどった感じなので、 #分かったところを含めて、ライブラリ使用者さんの #コードを元に、一気に取得する場合のコードを #自分で書いてみようかと。。。:-( >バイナリエディタで中身確認していた為、特に気にしていませんでした 昔つくったヤツですが。 バイナリエディタっぽく出力する関数です。 呼び出し側はdump関数に出力するファイルポインタと データの先頭とサイズを渡せばOKです。 オプションでオフセットかメモリアドレスの どちらかで、出力させるか選びます。 ------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <ctype.h> typedef enum {  DUMP_ADDRESS = 0x0001,  DUMP_OFFSET = 0x0002, }dump_opt; #define MAXBIN_LEN (16) //1行に表示するOffset #define MAXROW_LEN (8+3+(MAXBIN_LEN*3+1)+MAXBIN_LEN) #define BINMASK (0x000000FF) /* * dump hex data */ void _hex( int idx, unsigned long ulSize, unsigned long cur, unsigned char* cdData, FILE* ofp, dump_opt type ) {  unsigned long i;  if ( type == DUMP_ADDRESS ){   fprintf( ofp, "%08p | ", cdData+cur );  } else {   fprintf( ofp, "%08X | ", cur );  }  for( i=0; i<MAXBIN_LEN; i++ ){   if( i<(ulSize-cur)){    fprintf( ofp, "%02X ", cdData[idx*MAXBIN_LEN+i]&BINMASK );   } else {    fprintf( ofp, " " );   }  }  fprintf( ofp, " " ); } /* * _ans * ascii char set print */ void _ans( int idx, unsigned long ulSize, unsigned long cur, unsigned char* cdData, FILE* ofp ) {  unsigned long i;  for( i=0; i<MAXBIN_LEN; i++ ){   if( i<(ulSize-cur) ){    if( isprint( cdData[idx*MAXBIN_LEN+i] )){     fprintf( ofp, "%c", cdData[idx*MAXBIN_LEN+i] );    } else {     fprintf( ofp, "." );    }   } else {    fprintf( ofp, " " );   }  }  fprintf( ofp, "\n" ); } /* * dump out to ofp stream */ int dump( FILE* ofp, void* cdData, unsigned long ulSize, dump_opt type ) {  unsigned long i,j;  unsigned long nCurrent,nCnt=0;  if( cdData==NULL || ofp==NULL ) return 1;  fprintf( ofp, "\n" );  if ( type == DUMP_ADDRESS ){ fprintf( ofp, "ADDRESS | " ); }  else { fprintf( ofp, "OFFSET | " ); }  for( j=0; j<MAXBIN_LEN; j++ ) fprintf( ofp, "%02X ", j );  fprintf( ofp, " " );  for( j=0; j<MAXBIN_LEN; j++ ) fprintf( ofp, "%X", j );  fprintf( ofp, "\n" );  for( j=0; j<MAXROW_LEN; j++ ) fprintf( ofp, "-" );  fprintf( ofp, "\n" );  for(i=nCurrent=0; nCurrent<ulSize; i++, nCurrent += MAXBIN_LEN ){   _hex( i, ulSize, nCurrent, cdData, ofp, type );   _ans( i, ulSize, nCurrent, cdData, ofp );  }  fprintf( ofp, "\n" );  return 0; }

Page: 1 | 2 | 3 |