• ベストアンサー

WinsockAPIのrecvfromの受信データがおかしい

環境は WinXPSP2、VB6SP6です WinsockAPIでUDPのマルチキャスト通信で 定期的にrecvfromでデータを受信しています サーバの配信が止まってる時に recvfromをすると、以前に受信したデータが残っており 同じデータを受信し続けます、返り値はゼロではないです Cならばrecvfromの返り値にゼロが入るのですが・・・ どうすれば受信が終わった(正しく受信できない)ことを判ることができるでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • saitoha
  • ベストアンサー率81% (9/11)
回答No.5

>サーバ側のバッファサイズを1470にすることでエラーは出なくなりました >が、lngRetの返り値は常に1470に・・・ C側のバッファで、固定長配列のsizeof()をとっているからだとおもう strlen()をつかうか、std::string等の可変長コンテナをつかう

ackapapa
質問者

補足

ご指摘どおりサーバ側の処理で sendto ( sock, buf, sizeof ( buf ), 0, ( struct sockaddr * ) & addr, sizeof ( addr ) ); としておりこれを sendto ( sock, buf, strlen ( buf ), 0, ( struct sockaddr * ) & addr, sizeof ( addr ) ); とすることで希望通りの動作をすることができました 問題部分のほとんど全てを解決していただき本当にありがとうございます

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (4)

  • saitoha
  • ベストアンサー率81% (9/11)
回答No.4

参考URLでErr.LastDLLErrorをつかえみたいなことをBob Butlerさんが言っていますがどうなんでしょう。 WSAGetLastErrorとGetLastErrorはリンクしてるっぽい(いつも同じ値を返してくる)ので、そのへんがVBの例外処理機構に引っかかってこないように内部的にSetLastErrorしてるとか(推測) ところで受信するたびにAsyncSelectしなおす必要あるんだろうか

参考URL:
http://forums.devx.com/showthread.php?t=37954
ackapapa
質問者

お礼

WSAEMSGSIZEですがUDPソケットの送信サイズ1470バイトを無視していたため起きていました。 サーバ側のバッファサイズを1470にすることでエラーは出なくなりました が、lngRetの返り値は常に1470に・・・ エラーが出なければ大きな問題ではないためReplaceを使い対処します いろいろとありがとうございます

ackapapa
質問者

補足

情報ありがとうございます さっそく情報を元に確認してみました --------------------------------------- lngRet = recvfrom(udpsock, ByVal buf, ByVal LenB(buf), 0, fromaddr, LenB(fromaddr)) If (lngRet > 0) Then LbDiscv.Caption = "UPnP機器を検出しました" ElseIf lngRet = SOCKET_ERROR Then Label1.Caption = Err.LastDllError & " : " & WSAGetLastError() End If --------------------------------------- でクライアントのみ実行するとLabel1には "10035 : 0" の文字が・・・ サーバ、クライアント両方を起動させると "10040 : 0" となりました。 10035はWSAEWOULDBLOCK 10040はWSAEMSGSIZE です、これで原因を知ることができました よくこのような情報を見つけられますね、すごいです あとはWSAEMSGSIZEの対処をすることでなんとか対応できそうです ありがとうございます AsyncSelectですが、起動してそのまま何もしなければ 送信にあわせ受信し続けるんですが、ウィンドウを移動させる テキストボックス内でメニューを出す ブレークで一時止めるなど、何かしらの動作をさせると 受信しなくなってしまうため、AsyncSelectを入れてます・・・ コレでもたまに受信しなくなるためタイマ内に入れている状態です ※変な仕様ですね・・・VB(6)は・・・

全文を見る
すると、全ての回答が全文表示されます。
  • saitoha
  • ベストアンサー率81% (9/11)
回答No.3

>その状態でわざとrecvfromをすると返り値は相変わらず-1ですが >bufの中身は 0x00*512 文字分で返ってきました GetLastErrorで WSAEWOULDBLOCK が返っていたら -1 でも正常だと思います。 なぜならWSAAsyncSelectを呼んでソケットが非ブロッキングモード(戻り値をすぐに返すモード)になっているから。 受信データがないので、結果を返したくても返せず、関数は失敗と見なされるんじゃないでしょうか。 受信するまで制御を返すなというのであれば、ブロッキングモードを使用すべきです。 >前回(06/02)の時点では受信していたのですが・・・ このときのGetLastErrorは、ひょっとして 10014のWSAEFAULT だったんじゃないでしょうか? recvfromの最後のパラメータがSOCKADDRのバイト長に満たないと、たぶんそうなるとおもいます。 SOFTBANKのWinsock2.0本(初版)にはfromlenを初期化しなくていいとか書いてあって、それでハマったことがあります。

ackapapa
質問者

補足

今までのやり取りに関することでは常に recvfromの返り値は-1で GetLastErrorではゼロが返っており 受信エラーが起き原因は不明という状態でコレの原因が不明でした 現在わかってる範囲で関係ありそうなのはバッファサイズです 現在はCサーバのバッファサイズは4096、VBクライアントは512としていますがこれを Cのサーバのバッファサイズを char buf [ 64 ]; VBのクライアントのバッファサイズを Dim buf As String * 128 とし、recvfromをすると返り値は 64、サーバ側のバッファサイズが返ってきます もちろんサーバのバッファサイズ内(64以下)に収めたデータを送信しています おそらくVB側の受信データ取得サイズがバッファのサイズを越えているためrecvfromでエラーを吐くと思われますが GetLastErrorではゼロになっております

全文を見る
すると、全ての回答が全文表示されます。
  • saitoha
  • ベストアンサー率81% (9/11)
回答No.2

recvfrom直前でのバッファ初期化忘れ? もし、Winsock内部のバッファが消えてないのなら、受信データがおかしいとかの以前に、FD_READが送られ続けてアプリがフリーズするはず。 あと気になったのは(bufのタイプがわからないのでアレですが)、LenよりLenBを使うべきかと。 こちらで実験したところ、stringのときはマルチバイトを含む場合も文字数をかえしてしまうし、内部形式がバイト配列のVariantだと半分の長さしか返してきませんでした。

ackapapa
質問者

お礼

さきほどサーバ、クライアントを起動した状態で サーバを停止させるとクライアント側のFD_READも停止しました その状態でわざとrecvfromをすると返り値は相変わらず-1ですが bufの中身は 0x00*512 文字分で返ってきました 一応これを未受信状態と判断しておきます しばらくコレで様子を見ます 前回(06/02)の時点では受信していたのですが・・・ おそらくソケットを使用してのデバッグ作業なので 途中でソケット自体ががおかしくなった為なのかもしれません・・・ 聞く前に再起動をするべきでしたね・・・すみません あとは一定時間受信できていなければ未接続と判定することで一応解決できそうです アドバイスありがとうございます 2,3日締め切らずに置いておきます 何かご意見ご指摘があればよろしくお願いします

ackapapa
質問者

補足

返事が遅れてすみません recvfromの前に初期化はしています 仰るとおり常にFD_READが起きていますがフリーズはせずDoEventsをかけた様な状態です。 VBの受信部分の詳細ですが ------------------------------------------------ Private Sub Data_Read() Dim lngRet As Long Dim portstr As String Dim addrstr As String Dim strErrMsg As String Dim buf As String * 512 Dim buf2 As String buf = "" lngRet = recvfrom(udpsock, ByVal buf, ByVal LenB(buf), 0, fromaddr, LenB(fromaddr)) buf2 = Replace(buf, Chr(0), "") Label3.Caption = buf2 If (lngRet > 0) Then   Label1.Caption = "接続されています" ElseIf lngRet = SOCKET_ERROR Then   LbDiscv.Caption = ""   If WSAGetLastError() = WSAEWOULDBLOCK Then     Label1.Caption = "未接続"   ElseIf WSAGetLastError() > 0 Then     strErrMsg = "send:" & strWSAErrorGet(WSAGetLastError())     closesocket udpsock     lngRet = WSACleanup()     GoTo exitSend:   End If Else   Exit Sub End If addrstr = CStr(fromaddr.sin_addr / &H1 Mod &H100) & "." & _      CStr(fromaddr.sin_addr / &H100 Mod &H100) & "." & _      CStr(fromaddr.sin_addr / &H10000 Mod &H100) & "." & _      CStr(fromaddr.sin_addr / &H1000000 Mod &H100) portstr = CStr(ntohs(fromaddr.sin_port)) Label2.Caption = "address of " & addrstr & " : " & portstr Call WSAAsyncSelect(udpsock, Text1.hWnd, &H100, FD_READ) Exit Sub exitSend:   On Error Resume Next   If strErrMsg <> "" Then   MsgBox strErrMsg, vbOKOnly + vbExclamation, App.Title   End If End Sub ------------------------------------------------ このような感じです 上記でbufを初期化はしていますが buf = "" としても内部は 0x00*512 文字分で埋まって LenB の返り値は512で recvfrom の返り値は常に -1 です おそらく String * 512 で宣言しているせいかと思われますがそうしないと受信できません ただの String で初期化し、LenB() をするとゼロが返るためゼロ文字受信することになってしまいます これも改善方法がわからない状態です アドバイスどおりLenからLenBに変えてみました

全文を見る
すると、全ての回答が全文表示されます。
  • saitoha
  • ベストアンサー率81% (9/11)
回答No.1

自信ないですが、フラグがMSG_PEEK(0x2)になっているとか?

ackapapa
質問者

補足

ご返事ありがとうございます サーバ、クライアント共にフラグの設定はしていません Cサーバ側 ------------------------------------------------ sendto ( sock, buf, sizeof ( buf ), 0, ( struct sockaddr * ) & addr, sizeof ( addr ) ); /* サーバへデータと自分のアドレス情報を送信 */ ------------------------------------------------ VBクライアント側 ------------------------------------------------ recvfrom(udpsock, ByVal buf, ByVal Len(buf), 0, addr, Len(addr)) ------------------------------------------------ といった感じです。 特におかしそうな部分は無いと思うのですが・・・ 一応VB側の部分の詳細も書いておきます ------------------------------------------------ Call WSAStartup(&H101, musrStartup) udpsock = socket(AF_INET, SOCK_DGRAM, 0) addr.sin_family = AF_INET addr.sin_port = htons(10000) addr.sin_addr = INADDR_ANY Call bind(udpsock, addr, Len(addr)) Call WSAAsyncSelect(udpsock, TextBox1.hWnd, &H100, FD_READ) Call MemCopy(mreq, 0, Len(mreq)) mreq.imr_interface = INADDR_ANY mreq.imr_multiaddr = inet_addr("239.192.1.2") Call setsockopt(udpsock, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq, Len(mreq)) ------------------------------------------------ としています(上記はエラー処理の部分などは端折って書いています) TextBoxのKey_Downにハンドルを当て、反応があればrecvfromをしています コレに関してはタイマーでも同様の動作をします

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • UDP受信時の通信異常検知について

    お世話になります。 UDPマルチキャストを勉強しているのですが、 recvfrom()で受信を待っている間にifdownなどでネットワーク断が 発生した場合、一般的にどうやってそれを検知するのでしょう? tcpdumpではifdownを打つと同時にNetwork is down.と表示された ので、なんらかの方法で検知できるとは思うのですが、、、 どなたかご存じでしたらご教授ください。

  • Winsockコントロールを使用してマルチキャスト通信

    まずは環境を WindowsXPSP2、VisualBasic6.0SP6です。 目的はサーバがマルチキャスト配信しているのを VBのクライアントがWinsockコントロールを使用して受信する方法を教えていただきたいのです。 Winsockコントロールを使用してUDPでの通信ができたのですが WinsockコントロールにはCの「setsockopt」に該当する命令が無い(わからない)のです。 Winsockコントロールで実現できないのであれば winsockのAPIを使用する方法でもかまわないのですが APIの情報も見つからず困っています。 APIを実装しても「IPPROTO_IP」「IP_ADD_MEMBERSHIP」などの定数の定義の情報がなくわからないのです。 これらの情報が載っているページや、解説しているページがあればお教えください。 または実装方法を知っておられるならお教えください。 よろしくお願いします。

  • UDP通信での受信方法について

    MFCでUDPプロトコルを使ったサーバー・クライアントをソケットプログラムで書いております。 ソフトの内容は、クライアントから送信されたの文字列のコマンドをサーバで処理をするだけです。 サーバー側での受信の仕方は WSAAsyncSelect()関数を使ってソケットイベントが発生するごとに親ウィンドウに自作のメッセージ(WM_MY_MSG)が送られるように設定する ↓ WM_MY_MSGのメッセージハンドラ内でrecvfrom()関数を使って受信する。 という方法を用いています。クライアント2台ぐらいであれば正常に動きます。 質問1: まだ試したことはないのですが(というより試す環境がない)、ほぼ同時ぐらいに複数(10台ぐらい)のクライアントからコマンドが送信された場合、それを全部正確に受信できるものなのでしょうか?要するに一つのコマンドを処理中に別のコマンドが送られてきた場合の動作はどうなるのでしょうか? 質問2: UDP通信での受信をする場合の何かもっとスタンダードな方法があるのでしょうか? よろしくお願いします。

  • UDPのマルチキャストについて。

    趣味でネットワークゲームを作成しているのですが、質問があります。 リアルタイム性が強く比較的失われても構わない情報をUDP通信にてLinuxサーバーから複数のクライントに送信しています。 今回、このようなケースの場合マルチキャストを利用するとネットワーク負担が減るのではないか?と考えました。 そこで調べてみたのですが、サンプルがどれも同一ネットワーク内のものばかりです。 動画配信などで利用されているという記事を見たのでできると考えているのですが、実際問題どうなのでしょうか? またルーターを超える場合はルーターが対応していなければならないと知りました。 知りたい内容は以下です。 ・一般家庭にあるルーターはマルチキャストに対応しているのかどうか? ・そもそものマルチキャストの考え方はあっているのか? ・もし対応していないルーターがあった場合このようなシステムを利用しているプログラムはどうしているのか? 用語を知ったばかりで的を外れた質問をしていたらすいません。 自分でも調べてみますが、参考URLや用語などをいただけると大変助かります。 よろしくお願いいたします。

  • 自動FTP受信ソフトについて

    現在、サーバーから定期的にFTP受信する ソフトウェアを探しています。 どなたかご存知の方いらっしゃらないでしょうか? 尚、S.Kasuya様が作成された[CSVコマンドライン型FTPプログラム Ver 0.90]において受信はしていましたが データに異常があるため使用を控えております。 尚、通常FTPにおいての受信においてなんら問題はありません。 定期的に受信するファイルはUNIXのログファイルでgzipで圧縮されています。 [環境] Windows NT4.0 通信環境はADSL [要望] 定期的な(日次)受信 ワイルドカードが使用できると便利です。 以上よろしくお願いします。

  • マルチキャストについて

    マルチキャストって最近、TV会議システムとかで使われてますが、いまいちよく理解できません。 まず、マルチキャストってグループ配信とか言われてますがどういうことですか? 実際にパケットをキャプチャしてみて確認しましたが、配信先が239.x.x.x宛て(マルチキャストアドレス)にパケットを送信しています。見たい人はどうやってパケットを受信するのですか?グループ配信とか書いてありましたが、受信しなくない人にもパケットは送付されているようです。(パケットをキャプチャして確認しました)ってことは、ブロードキャストと何が違うのでしょうか? NICが受け取るまでは一緒で、その上の処理が違うということでしょうか? スイッチがブロードキャストを全ポートに送信するように、マルチキャストも全ポートに送信しているように思えます。ということは、受信したくない拠点でもマルチキャスト分の帯域は使用しているのでしょうか? うまく説明できなくて申し訳ありませんが、マルチキャストについてお詳しい方、ご教授願えないでしょうか。 よろしくお願い致します。

  • パケット受信 recvfrom( )について

    お世話になります。 WinSockを用いてUDPパケット受信プログラムを作成しています。 環境はVC++です。ダイアログベースで、 STARTボタンを押すと受信開始します。 送信側よりパケットを1000個送信した場合、受信側では、 int num; SOCKET sock; char buf[1500]; //ソケットの設定は省略 while(1){ num = recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL);  printf("%d",num ); if( num == SOCKET_ERROR){ break; } } recvfrom()で受信していますが、送信側で送信が完了しても while()を抜けないのか、応答なし(フリーズ)してしまいます。 printf()で表示してみると、 ちゃんと送信パケット個数回表示されています。 送信パケット数は変動するため、受信側のプログラムには 個数がわかりませんため、while()でループしています。 while()を抜ける条件はどうしたらいいのでしょうか? ちなみにrecvfrom()の戻り値として SOCKET_ERROR,0を指定してもエラーになります。 長くなりましたが、どうぞよろしくお願い致します。

  • XMLデータの受信

    他のアプリからPOSTされたXMLデータをASP.NETで受信したいのですが、 どのクラスやメソッドを使えばよいのでしょうか? もしくは参考になるサイトや書籍等教えていただけると幸いです。 開発環境はVS2008、言語はVBです。

  • 宛て先の特定の為の手段

    『リンクローカルさえもさえも』と『プライベートスコープ』と『グローバルスコープ』との3種類に、マルチキャストアドレスが分類され得る、 という事情を最近に初めて私は知りました。 但し、残念乍ら、格好悪いことに、 マルチキャスト通信で『宛先の全端末群へ同時にデータが送られるのか、 それとも、『中継者のルータだけに送られた後に、 其処から配下の全ホスト群へ配信されているのか』、 或いは更に別の手段が採用されているのかさえもが全く私には分かりませんから、 畏れ入りますが、マルチキャストの仕組みにつきましての御教授を賜れませんでしょうか?

  • GPSデータ(NMEA)受信

    USB接続のGPS受信機の購入を検討中で、 GPS受信機よりNMEAデータのUTCを取得したいと考えています、 NMEAデータ取得の方法について教えてください。 プログラム上(VB.net,C#)より RS232CのようにUSBのポートに接続し、 ボーレートを設定して受信すればよいのでしょうか? NMEAデータがRS232Cシリアル通信のように垂れ流されてくるイメージなのですが、 問題ないでしょうか?