dポイントプレゼントキャンペーン実施中!

環境は WinXPSP2、VB6SP6です
WinsockAPIでUDPのマルチキャスト通信で
定期的にrecvfromでデータを受信しています

サーバの配信が止まってる時に
recvfromをすると、以前に受信したデータが残っており
同じデータを受信し続けます、返り値はゼロではないです

Cならばrecvfromの返り値にゼロが入るのですが・・・

どうすれば受信が終わった(正しく受信できない)ことを判ることができるでしょうか?

A 回答 (5件)

>サーバ側のバッファサイズを1470にすることでエラーは出なくなりました


>が、lngRetの返り値は常に1470に・・・

C側のバッファで、固定長配列のsizeof()をとっているからだとおもう
strlen()をつかうか、std::string等の可変長コンテナをつかう

この回答への補足

ご指摘どおりサーバ側の処理で

sendto ( sock, buf, sizeof ( buf ), 0, ( struct sockaddr * ) & addr, sizeof ( addr ) );

としておりこれを

sendto ( sock, buf, strlen ( buf ), 0, ( struct sockaddr * ) & addr, sizeof ( addr ) );

とすることで希望通りの動作をすることができました
問題部分のほとんど全てを解決していただき本当にありがとうございます

補足日時:2006/06/05 15:21
    • good
    • 0

参考URLでErr.LastDLLErrorをつかえみたいなことをBob Butlerさんが言っていますがどうなんでしょう。


WSAGetLastErrorとGetLastErrorはリンクしてるっぽい(いつも同じ値を返してくる)ので、そのへんがVBの例外処理機構に引っかかってこないように内部的にSetLastErrorしてるとか(推測)
ところで受信するたびにAsyncSelectしなおす必要あるんだろうか

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

この回答への補足

情報ありがとうございます
さっそく情報を元に確認してみました
---------------------------------------
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)は・・・

補足日時:2006/06/05 14:26
    • good
    • 0
この回答へのお礼

WSAEMSGSIZEですがUDPソケットの送信サイズ1470バイトを無視していたため起きていました。

サーバ側のバッファサイズを1470にすることでエラーは出なくなりました
が、lngRetの返り値は常に1470に・・・

エラーが出なければ大きな問題ではないためReplaceを使い対処します
いろいろとありがとうございます

お礼日時:2006/06/05 14:35

>その状態でわざとrecvfromをすると返り値は相変わらず-1ですが


>bufの中身は 0x00*512 文字分で返ってきました

GetLastErrorで WSAEWOULDBLOCK が返っていたら -1 でも正常だと思います。
なぜならWSAAsyncSelectを呼んでソケットが非ブロッキングモード(戻り値をすぐに返すモード)になっているから。
受信データがないので、結果を返したくても返せず、関数は失敗と見なされるんじゃないでしょうか。
受信するまで制御を返すなというのであれば、ブロッキングモードを使用すべきです。

>前回(06/02)の時点では受信していたのですが・・・

このときのGetLastErrorは、ひょっとして 10014のWSAEFAULT だったんじゃないでしょうか?
recvfromの最後のパラメータがSOCKADDRのバイト長に満たないと、たぶんそうなるとおもいます。
SOFTBANKのWinsock2.0本(初版)にはfromlenを初期化しなくていいとか書いてあって、それでハマったことがあります。

この回答への補足

今までのやり取りに関することでは常に
recvfromの返り値は-1で
GetLastErrorではゼロが返っており
受信エラーが起き原因は不明という状態でコレの原因が不明でした

現在わかってる範囲で関係ありそうなのはバッファサイズです
現在はCサーバのバッファサイズは4096、VBクライアントは512としていますがこれを

Cのサーバのバッファサイズを
char buf [ 64 ];

VBのクライアントのバッファサイズを
Dim buf As String * 128

とし、recvfromをすると返り値は 64、サーバ側のバッファサイズが返ってきます
もちろんサーバのバッファサイズ内(64以下)に収めたデータを送信しています

おそらくVB側の受信データ取得サイズがバッファのサイズを越えているためrecvfromでエラーを吐くと思われますが
GetLastErrorではゼロになっております

補足日時:2006/06/05 12:31
    • good
    • 0

recvfrom直前でのバッファ初期化忘れ?


もし、Winsock内部のバッファが消えてないのなら、受信データがおかしいとかの以前に、FD_READが送られ続けてアプリがフリーズするはず。

あと気になったのは(bufのタイプがわからないのでアレですが)、LenよりLenBを使うべきかと。
こちらで実験したところ、stringのときはマルチバイトを含む場合も文字数をかえしてしまうし、内部形式がバイト配列のVariantだと半分の長さしか返してきませんでした。

この回答への補足

返事が遅れてすみません
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に変えてみました

補足日時:2006/06/05 09:59
    • good
    • 0
この回答へのお礼

さきほどサーバ、クライアントを起動した状態で
サーバを停止させるとクライアント側のFD_READも停止しました

その状態でわざとrecvfromをすると返り値は相変わらず-1ですが
bufの中身は 0x00*512 文字分で返ってきました
一応これを未受信状態と判断しておきます
しばらくコレで様子を見ます

前回(06/02)の時点では受信していたのですが・・・
おそらくソケットを使用してのデバッグ作業なので
途中でソケット自体ががおかしくなった為なのかもしれません・・・
聞く前に再起動をするべきでしたね・・・すみません

あとは一定時間受信できていなければ未接続と判定することで一応解決できそうです

アドバイスありがとうございます
2,3日締め切らずに置いておきます
何かご意見ご指摘があればよろしくお願いします

お礼日時:2006/06/05 10:59

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

この回答への補足

ご返事ありがとうございます
サーバ、クライアント共にフラグの設定はしていません

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をしています
コレに関してはタイマーでも同様の動作をします

補足日時:2006/06/02 17:22
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています