none
VBA+Winsockコントロールによるプログラムについて RRS feed

  • 質問

  • 現在、Windows10でExcel2016VBAのWinsockコントロールによるプログラムで、
    前回、マルチセッッション対応がうまくいかなかったので、とりあえず単純に、
    ホスト側でWaitconnect→クライアン側よりConnect→接続完了。その後、コマンドボタン
    押下で、クライアント側で、前回送信完了イベントフラグで送信済みを確認→Sendを実行、
    ホスト側では、受信イベントData_ArrivalでGetDataを実行すると、
    クライアント側でコマンドボタン押下後、一回ずつSendするようにすると問題ないのですが、
    Forループで5回ほど自動的に上述のようにしてSendすると、クライアント側が5回正常にSend
    できてしまうのですが、何故か、ホスト側では、2回の受信イベントData_ArrivalでGetDataしか
    実行できていません(何故か2回しか受信できません)。ホスト、クライアントとも特にエラーも
    発生しておりませんし、これ以上、単純なシーケンスは考えられなく、ほとほと困りはてています。
    どうか、VBAとWinsockコントロールに詳しい方にアドバイスを頂きたくよろしくお願いします。
    ちなみに、WindowsXPでVB6で作成した時はこんな現象は出ませんでした。
    どうかよろしくお願いします。
    2017年6月24日 7:56

回答

    • Win7+VB6で組んだ場合はSendData1回に対してDataArrivalが1回
    • Win10+ExcelVBAで組んだ場合は複数のSendDataに対して、少ない回数のDataArrival
    • Win10+ExcelVBAでもSendData1回に対してDataArrivalが1回にしたい

    という事でしょうか…

    Win10+Excel2016,Win7+Excel2013を試してみましたが、どちらも複数のSendDataに対して、少ない回数のDataArrivalでした。(でかいデータを送ればDataArrivalは増えますけどね)

    • VB6はネイティブにコンパイルされて、かつプログラムに(他の処理が走ってなければ)すぐにイベントが発生する
    • ExcelVBAは一応コンパイルされるがExcelの処理の合間に処理される(VBAで受信があっても最優先でイベントが発生するものではないはない)

    という違いがあるので、VBAでVB6と同等の送受信が行われることを期待するのが間違いじゃないかな。

    どうしても1:1にしたいなら、SendDataの後にDoEventsなどで遅延をいれてやればいんじゃないでしょうか。

    Private Sub cmdTcpSend()
        Dim strData As String
        Dim i As Integer
       
        For i = 1 To 5
            strData = "=> " & CStr(i) & " CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
            tcpClient.SendData strData
    
            'SendDataの後に相手が受信処理すると想定される時間遅延させる
            'DoEvents1回で足らないならある程度遅延するような処理をいれること
            DoEvents 
    
            Debug.Print strData
        Next i
    End Sub
    結局は他の人が書いているように、区切り文字で分割する処理を入れない限り、確実な方法はないですよ。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年6月26日 15:25
  • VB6でも組んでみましたが、くっついて読み出されました。DoEventsを入れると個別になりました。
    Wiresharkでパケットを見ると、DoEventsを入れるとSendData毎のパケットになってはいました。入れないとくっついて送出されてました。(setsockoptでSO_SNDBUFを短くしたら分割されて送出してしまいましたが)
    それでも読み出し側で意図的に他の処理を回している最中だと、その処理をぬけた後にくっついて読み出されました。
    つまり、VB6で1:1になっていたのは偶然でしょう。
    #とりこぼしというのは発生してませんが、受信側で処理が追いつかなくて受信バッファからあふれたら取りこぼすかもね。

    IBMの専用回線とやらをテストする環境はないのでテストはおしまい。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年6月27日 16:02

すべての返信

  • 質問者さんのオレオレ用語「マルチセッション」が一般用語で何に相当するのかはよくわかりませんが…。

    送信途中でデータが結合されることがあるため、送信回数と受信回数は無関係です。というわけで、データが結合されていないかを確認してください。

    2017年6月24日 8:31
  • アドバイス、ありがとうございました。どうも、Windows10とVBAで作成すると何か問題があるように思います。

    試しに、Windows7上でVB6で全く同じコーディングのプログラムを動作させますと、クライアント側でForループ

    で100回Sendをループさせても、1000回Sendをループさせても、ホスト側のDeda_Arrivalイベントでは

    正常に受信できます。Sendする時も送信完了のチェックをしたりすることも必要ありませんし、単純にSendして、

    Deda_ArrivalイベントでGetDataすることができます。まあ、これが当たり前と言えば当たり前なんですが、

    これをWindows10とVBAで実行させると、例えば、250文字のデータを送ると、5回ぐらいのForループでSendする

    と、ホスト側で2回しか受信しなかったり、5文字だと5回とも受信できますが、2回目~5回目の送信データがひとつ

    の受信データとなったりと、わけがわからない状況で、これ以上テストしても意味がないようです。何故なんでしょうか。

    ちなみに、C:\Windows\SysWOW64にあるMSWINSCK.OCXは、1998/6/24作成、2009/3/24更新のものです。

    MSWINSCK.OCXが動作不良なのか、VBAあるいはWindows10との相性のように思います。

    マイクロソフトサポートに電話しましたら、基本的にこの構成で動作するはずなので、このVBAフォーラムを紹介頂き、

    有識者のアドバイスを頂いたらよいと言われましたので投稿させて頂いたのですが・・困りはてています。

    どちらに聞けばよいかご存知でしょうか、ご存じでしたら教えて頂けませんでしょうか。

    それから、オレオレ用語「マルチセッション」と書かれていますが、何かご不快な書き方をしておりましたらこの場を

    お借りしてお詫び申し上げます。

    2017年6月24日 13:40
  • 私のところに Windows 10 と Excel 2016 の環境がありますので、再現させれたらと思っています。

    再現可能で、かつ最小限で、完全なソースを掲載することは可能でしょうか?

    2017年6月24日 14:11
  • ありがとうございます。エラー処理や送受信タイミングをとる処理をはずして、

    以下のようにしました。Winsockコントロール名、コマンドボタンは以下の

    ソースからご判断願います。一応、Win7、VB6では問題なく1000回の

    繰り返し送受信でも問題なかったです。小職のMSWINSCK.OCXがおかしい

    のかもしれませんが、とにかく、お手数をおかけしますが、再現テストをお願

    いします。お手間とらせまして申し訳ありません。ソースは以下のとおりです。

    「サーバ側」

    'クライアント接続要求待機
    Private Sub Form_Load()

        tcpServer.LocalPort = 10000
        tcpServer.Listen
        frmClient.Show
    End Sub

    'クライアント接続要求受付
    Private Sub tcpServer_ConnectionRequest _
    (ByVal requestID As Long)

        IcpServer.LocalPort = 10000
        tcpServer.Accept requestID
    End Sub

    '受信イベント
    Private Sub tcpServer_DataArrival _
    (ByVal bytesTotal As Long)
        Dim strData As String
       
        tcpServer.GetData strData
        Debug.Print strData
    End Sub

    「クライアント側」

    'ホスト接続コマンドボタン
    Private Sub cmdConnect()
        'ホストのIPアドレスは実機に合わせて修正して下さい。
        tcpClient.RemoteHost = "192.168.1.2"
        tcpClient.RemotePort = 10000
        tcpClient.LocalPort = 0
        tcpClient.Connect
    End Sub

    'ホスト送信コマンドボタン
    'Forループ5回でホストに送信
    Private Sub cmdTcpSend()
        Dim strData As String
        Dim i As Integer
       
        For i = 1 To 5
            strData = "=> " & CStr(i) & " CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
            tcpClient.SendData strData
            Debug.Print strData
        Next i
    End Sub

    以上、よろしくお願いします。

    2017年6月24日 16:31
  • tcpServer.DataArrivalイベントで得られるbytesTotalの値は確認されていないようです。

    「送信途中でデータが結合されることがある」と説明しました。

    tcpClient.SendDataメソッド呼び出し1回に対して、tcpServer.DataArrivalイベントは1回とは限りません。tcpServer.BytesRecievedプロパティが0になるまでtcpServer.GetDataメソッドを繰り返し呼ぶ必要があります。

    '受信イベント
    Private Sub tcpServer_DataArrival (ByVal bytesTotal As Long)
        Dim strData As String
    
        Debug.Print "Data Arrival: " & CStr(bytesTotal) & "bytes"
        While 0 < tcpServer.BytesReceived
            tcpServer.GetData strData
            Debug.Print strData
        End While
    End Sub

    WinSockの挙動が変わったとお考えかもしれませんがそうではありません。送信途中のデータが結合・分割されるのはWinSock登場以前よりTCP/IPの仕様であり、WinSock側で制御できるものではありません。


    • 編集済み 佐祐理 2017年6月25日 22:48 typoの修正
    2017年6月24日 22:13
  • TCP/IPはパケット通信ですので、データ分割されて送信されるのは承知しておりまして、
    以前、VB6で大容量のデータを送信する場合に考慮してバッファ容量の受信監視をしつつ変数に
    入れるようにしました。その際に、ByteReceivedのプロパティを活用しようとしたのですが、
    「コンパイルエラー メソッドまたはデータメンバーが見つかりません。」とコンパイルエラーになりました。
    ただ、送信データに、STX、ETXを付加して送信し受信側でエラーリカバリーするようにしました
    ので、パケットデータ分割による受信については特に問題になりませんでした。
    今回も、アドバイス頂いたByteReceivedプロパティをVBAテストPGに入れますと、やはり、同様
    のコンパイルエラーとなります。VB6のテストPGに入れましても、同様のエラーとなります。
    そちら側の環境ではエラーにならないでしょうか?
    それとも、こちら側で何か他に設定する箇所が必要でしょうか。
    ただ、今回、ローカルネットワーク上でのテストで数文字~数十文字のデータですので、分割と
    いう問題よりも、VBA上でのWinSock使用上の問題なのかなという気がするのですが・・
    実際、Win7上でVB6では、Send→DataArrivalを1000回Forループで回しても正常に送受信できます
    ので、何か、Windows10でVBAでWinSockを実行処理させると、受信側でバッファから取り出すこと
    ができないのか、パケットデータから受信バッファにセットできないのか・・
    というのは、VBAでも、コマンドボタンで1回毎にSendするようにすれば、問題なく受信できるの
    です。その辺で、何かあるような気がします。
    いろいろややこしいことを投稿しまして申し訳ありません。
    ご返事頂き感謝申し上げます。何か、思い当たる点があれば、またリプライ頂けますでしょうか。
    よろしくお願いします。
    2017年6月25日 1:31
  • 言葉足らずでしたので、補足説明させて頂きます。ホストの受信イベントDataArrivalは、本来、クライアント側から

    のSendが発生するとWinsockのイベント待ち行列に入り、FIFOの先入れ先出しの処理によりSend毎に対応する逐次

    イベント処理が実行され、各Sendに対応する受信バッファがセットされるのが、

    VBAでWinsockを実行させると、はその辺の制御がうまくされないので、とにかくSendが実行されると、

    DataArrivalイベント処理中にも次のSendデータが受信バッファにセットされてしまうのではないかと思います。

    実際、クライアント側でForループで数十文字のデータを5回Sendすると、ホスト側では1回目は正常に受信します

    が、2回目の受信イベントで2回目から5回目までの送信データがつながったデータが受信バッファにセットされ、

    受信イベントは2回で終わってしまいます。一応、5回分のSendデータは受信できるのですが、SendとDataArrival

    イベントの同期がとれないのが問題です。

    全く同じコーディングのプログラムをWin7上でVB6で動作させると、クライアント側の5回のForループのSend実行

    処理に対応して、ホスト側で5回のDataArrivalイベントが発生し、受信データも対応しています。

    Window10なのか、VBAなのか、MSWINSCK.OCXの問題なのか、その辺のところがよくわかりません。

    もし、そういう視点で思い当たるところがあればお教え頂けませんでしょうか。よろしくお願いします。

    • 編集済み sumiko VB 2017年6月25日 4:53
    2017年6月25日 4:49
  • アドバイス頂きありがとうございました。

    それで、そちら様のWindows10+Excel2016の環境で如何でしたでしょうか。

    もし、お分かり頂いたことがございましたらご返信お願いします。

    どうかよろしくお願い申し上げます。

    2017年6月25日 12:24
  • ですから、皆さん言われているように、それはTCP的には至って正常な動作です。「SendとDataArrivalイベントの同期」など、期待していはいけません。一回のSendが一回のRecv(DataArrivalイベント)と対応付く/過去は紐付いていた、などというのは、おおよそ単なる偶然なのです。再送と分割を考えれば、1:1でも、1:nでもn:1でもなく、n:mの関係ですネ。
    (もちろん、TCPスタックの作りには差があるのでOSによって「TCPのパケットがくっつきやすい」みたいな傾向があるのは否定しません。)

    ・STX,ETXで明示的に伝聞間に区切りをつけるのは、よくある(そして正当な)対処法です。
     DataArrivalで複数のSTX-ETXが含まれていれば複数回処理を駆動し、
     STXはあってもETXは無い時にはバッファリングして次のDataArrivalでくっつけて処理をするように
    すればよいのではないでしょうか。
    ・あるいは、1:1が期待できる方式としてUDPもあります(UDPにはUDPの副作用がありますが)。まーLAN上で伝文長も短いみたいなので、UDP使っても幸せになれますよ、きっと。


    jzkey

    2017年6月25日 12:36
  • tcpServer.DataArrivalイベントで得られるbytesTotalの値を確認しない理由を教えてください。Windows 7と10とで1回に得られている受信サイズが異なることが推測されるのですが。

    ByteReceivedはBytesReceivedのtypoでした。詳しくはリンク先を確認ください。

    2017年6月25日 14:35
  • アドバイスありがとうございます。TCPパケットは当然、回線上では分割して送信することは認識しており、実際、大きな

    ストリームが流れたりしますとデータが抜けたりすることは経験しましたし、そういう前提は理解しているつもりです。

    ただ、回線上はパケットで分割して送信しても、TCPプロトコルでパケットに札付きのパケットをマージして受信できる仕組み

    ではないでしょうか。MSWinsockは、FIFOのイベント待ち行列でパケット送受信の保証を行なっており、1回のSendに対して

    Receiveイベントで正確に受信できるような機能になっていると、MSの技術サポートからも説明を以前に受けており、

    VB6やVB2015でも、実際、海外とのデータ送受信の業務アプリを構築し、これまで1回のエラーも発生しませんでした。

    送受信だけのコーディングは、エラーリカバリーの機能を除けば、基本的に前回投稿しました内容と同じです。

    IBMの専用回線上でということもあるのかもしれませんが、1回のSendが数kBで、データ抜けは1回もありませんでした。

    これまでも書かせて頂きましたが、今回、上述のFIFOのイベント待ち行列のイベント制御がうまくいってないようです。

    受信イベント中に次の受信イベントが発生してしまいますので、受信イベント処理中に次のイベントが上がると、整合性が

    とれません。STX、ETXで受信データを抽出する機能も入れたのですが、結局、そのデータ抽出機能中に次の受信イベント

    が発生してしまい、うまくいきません。

    今回、どうしてもVBAでコーディングしないといけない実状がありそうしたのですが、MSの技術に確認しましたら、この

    ような現象を解明するには何やらややこしそうで、現象を経験された方がいらっしゃればと投稿させて頂いた次第です。

    2017年6月26日 6:59
  • bytesTotalの値はパッチをあてて確認したりしています。

    VB6では、数kBでも数十kBでも送信サイズと受信サイズは1000回Forループで回しても全く同じです。

    ただ、vb6はWindows10では動作できませんので確認不能です。

    10上でvb2015でもコーディングできるのですが、多分、問題ないと思います。

    VBAは、7でも10でも、送信サイズによってまちまちです。

    アドバイス頂き本当にありがとうございます。何か思い当たる点がございましたら

    2017年6月26日 7:09
  • >受信イベント中に次の受信イベントが発生してしまいますので

    ひょっとして、再入でしょうか。受信イベント中にイベントループを回すと、そこから(受信状況に応じて)DataArrivalイベントがまた発生することがありえます。DoEventや暗黙にイベントループを回すCOM系の関数呼び出しなどが書かれている、というわけでしょうか?


    jzkey

    2017年6月26日 11:50
  • 再入ですか? 受信イベントなく、Send側でテストでForループを回しているだけです。

    DoEvents~以降のアドバイスの内容はよく理解できませんでした。

    テストPGのコーディング内容は上の方に掲載させて頂いておりますのでご覧ください。

    TCP/IPはパケット通信なのでパケットを荷札付きで分割送信しますが、TCP/IPプロトコル

    で荷札を整理して受信します。WindowsではWinsockがその機能を果たします。

    ですので、sendしたデータは、TCP/IPプロトコルで認識及び整理して受信側に送られます。

    TCP/IPは、本来、非常に信頼性の高いプロトコルで、UNIXを起源とし、送信側でのホスト

    接続はconnect、受信側での接続待機はListen等、よく似たコーディングとなります。

    ただ、UNIXの場合はC+等でのコーデイングとなりますが・・ やっていることは同じです。

    それで、今、問題にしていますのは、VBAだと、上述のTCP/IPプロトコルがうまく動作して

    いないようなので、何故かなということです。

    Win7でVB6だと、send→getのデータ矛盾は全く発生しません。

    現在は、データの先頭にSTX、最後にETXを付加して送信し、データが分断された場合や接続

    された場合、受信側でSTX、ETXにより判断しデータを抽出するようにしましたが、あまりに

    冗長度が高く通信にかかる処理時間がばかにならないので困っています。

    また、何か、思い当たる点がございましたリプライ頂きますよう、よろしくお願い申し上げます。

    2017年6月26日 12:42
  • 結局、今の動作の(五回sendしたら、DataArrivalが二回しか発生しなかった)について、
    ・計2回のDataArrivalで2個分の伝文しか受信できなくて、残り3個分が消えてしまった、というお話なのでしょうか。
    ・2回目のDataArrivalで、2~5までの伝文が一気にとれる(くっつく)のがいやだ、というお話なのでしょうか。

    jzkey

    2017年6月26日 14:15
    • Win7+VB6で組んだ場合はSendData1回に対してDataArrivalが1回
    • Win10+ExcelVBAで組んだ場合は複数のSendDataに対して、少ない回数のDataArrival
    • Win10+ExcelVBAでもSendData1回に対してDataArrivalが1回にしたい

    という事でしょうか…

    Win10+Excel2016,Win7+Excel2013を試してみましたが、どちらも複数のSendDataに対して、少ない回数のDataArrivalでした。(でかいデータを送ればDataArrivalは増えますけどね)

    • VB6はネイティブにコンパイルされて、かつプログラムに(他の処理が走ってなければ)すぐにイベントが発生する
    • ExcelVBAは一応コンパイルされるがExcelの処理の合間に処理される(VBAで受信があっても最優先でイベントが発生するものではないはない)

    という違いがあるので、VBAでVB6と同等の送受信が行われることを期待するのが間違いじゃないかな。

    どうしても1:1にしたいなら、SendDataの後にDoEventsなどで遅延をいれてやればいんじゃないでしょうか。

    Private Sub cmdTcpSend()
        Dim strData As String
        Dim i As Integer
       
        For i = 1 To 5
            strData = "=> " & CStr(i) & " CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
            tcpClient.SendData strData
    
            'SendDataの後に相手が受信処理すると想定される時間遅延させる
            'DoEvents1回で足らないならある程度遅延するような処理をいれること
            DoEvents 
    
            Debug.Print strData
        Next i
    End Sub
    結局は他の人が書いているように、区切り文字で分割する処理を入れない限り、確実な方法はないですよ。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年6月26日 15:25
  • 結局は他の人が書いているように、区切り文字で分割する処理を入れない限り、確実な方法はないですよ。
    脱線しますが、SendDataでStringを送信した場合にTCP/IPとしてはどのようなデータが送信され、なぜGetDataでは文字列を形成できるのでしょうか? 長さ情報か、区切り文字がありそうに思うのですが…。これらが無いとしたら、質問者さんの挙げられたコードでは複数のStringが結合されて得られるようにも思いますが、そのような発言はありませんでしたし。
    2017年6月26日 22:41
  • いやだというのではなく、前回、書かせて頂きましたが、tcp/ipプロトコルが正常に機能していない状況です。

    2回目の受信イベントでとれてしまうのはたまたまで、文字数により3回目まで正常で、4回目で5回目のsend分を

    併せて受信バッファに入ったり、2回目以降、受信イベントがかからない場合もあります。

    MSではInsockの機能からしてそんな現象は発生しないとの回答でした。

    2017年6月26日 23:16
  • ありがとうございます。VBAの場合は私もおしゃるとおりかと思、MSに問い合わせると、その辺は、Winsockの機能で制御

    されるので、そのような現象は出ないはずですが・・との回答なので、どうしたものかなというところです。

    DoEventsやそれに遅延を加えたりもしてみましたが、同じ現象でした。基本的にVBA⇔Winsockのインターフェースで課題

    があるのかなと思います。今回、VBAなんかでやりたくないのですが、委託要件が通信部分もVBAで作成となっており、

    仕方ないですが、受信側での再度の仕訳け処理が発生し処理時間が大となり、他のタイマーイベント処理とか諸々に影響を

    受けるのと、データ受信漏れがないか心配です。

    2017年6月26日 23:49
  • どこかでも書きましたが、TCP/IPプロトコルでは、送信したデータは回線上で、送信したデータに対して荷札がついた

    パケットとして送られます。どこからどこに、サイズも含め属性情報とともに送られますので、分割されたパケットで

    受信しても、属性情報から受信側でパケットは結合され、送信したデータと同じデータがは正確無比に受信側のアプリ

    に引き渡されます。なので、通信アプリ上で長さや区切り文字とかは必要ありません。

    昔の電話回線上での無手順の送受信などでは、STX、ETX、BCCなどの情報がないとデータは抽出できませんが・・

    また、業務アプリ上、送信データのここからここが売上データ、次に売上先コード等を、可変長の場合、区切り文字を

    入れて抽出することはあります。

    私の確認したいことは、vb6(あるいはvb2015)⇔Winsockだと上述のTCP/IPプロトコルの機能が正常に動作するのに

    VBA⇔Winsockだと、何故今回のような動作になるのか?です。(MSに問い合わせてもそうはならないとの回答)

    現状のような動作シーケンスでも区切り文字を入れて受信側で抽出してやっていますが、その抽出処理の処理時間により

    他のタイマーイベント処理に影響して調整しているところです。

    それと、gekkaさんへのリプライにも書きましたが、通信テストで受信側での送信データの取りこぼしもあり、

    他のイベント処理も含め統合処理した場合の、取りこぼし再現テストもやっているところです。

    いずれにしてもこれにえらい時間を費やし、他の開発が延び延びで非常に困った状況です。

    2017年6月27日 0:25
  • 何どでも申し上げます。

    送信したデータと同じデータがは正確無比に受信側のアプリに引き渡されます。なので、通信アプリ上で長さや区切り文字とかは必要ありません」

    この認識は間違いです。TCPはストリーム(要はファイルと同じ)であり、「TCPが保証するのは、Sendでたとえば計500KB送ったら、Recvで計500KB受信でき、Recvした中身はSendの中身と同じ」というところだけです(そして、引用文の前半の意味でもありましょう)。
    500KBがsend1回で送ったのか、500K回で送ったのかは捨象されます。recvについても、その500KB受信するのに、1回~500K回のうち、何回Recvの呼び出しが必要かは一切TCPは規定しません。

    マスタリングTCP/IPにも、「送信した順番は保たれますが、区切り目のないデータ構造として、受信側アプリケーションに届きます」とあります。https://books.google.co.jp/books?id=KgyByBXNObUC&pg=PA222


    jzkey

    2017年6月27日 2:38
  • その、「何回Recvの呼び出しが必要かは規定しておりません」という意味がよく理解できません。

    私は、何回Recvの呼び出しが必要かについて言及しおりませんし、当然、何回なんて規定できるはずもありません。

    私は、通常、アプリでデータを区切り文字で囲む必要はありませんと書いただけです。

    それは、前回書きましたように、WindowsにしてもUNIXにしても、TCP/IPプロトコルで制御しておりますので、

    例えば、固定長の売上データを1件送信する場合、アプリで売上データの先頭と最後に区切り文字を挿入して送信さ

    れますか?

    ただ、顧客コード+売上データ+担当者コードで可変長のデータを送信する場合などは、途中に区切り文字を入れた

    りするでしょう。そういうことを述べただけです。

    それと、私がTCP/IPプロトコルの話を書きましたので正されたようです。申し訳ありません。

    私は、とにかく、Windows10上でVBAでWinSockを使用した場合のSend、Getdataの使い方についてアドバイスを

    頂きたくお願いしている次第です。どうか、その辺の具体的なアドバイス中心でアドバイス頂ければ幸いです。

    よろしくお願い申し上げます。


    • 編集済み sumiko VB 2017年6月27日 5:00
    2017年6月27日 4:59
  • bytesTotalの値はパッチをあてて確認したりしています。

    VBAは、7でも10でも、送信サイズによってまちまちです。

    「まちまち」では確認したことになりません。

    例えばですが、クライアント側でtcpClient.SendData "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"、tcpClient.SendData "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"、tcpClient.SendData "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"と3回呼び出した際に、サーバー側では、DataArrivalが3回発生しbytesTotalが200、200、200となるパターンと、DataArrivalが1回しか発生せずbytesTotalが600となるパターンがあるのではと推測しています。

    ですので、どのような値になっているのかを確認してください。


    • 編集済み 佐祐理 2017年6月27日 12:38 最小限の説明では質問者が理解できないようなので具体的な文字列長で説明を更新。
    2017年6月27日 7:49
  • すみません、まちまちとはあいまいな表現ですね、申し訳ありませんでした。

    引数のbytesTotalとプロパティのBytesReceivedでチェックしましたが、1バイトぐらいの送信でしたら、

    必ず、3回の送信に対応して3回の受信イベントが上がります。1バイトぐらいでしたら必ずそのパターンです。

    引数のbytesTotalとプロパティのBytesReceivedのバイト数も必ず同じで1回だけとか6回とかの受信イベントと

    なることはなかったです。何回もやりましたので、問題はないです。

    これを、数十文字、数百文字と送信バイト数を増やすと、これまで書きましたように、送信回数に対して受信イベント

    数は減少していきます。

    数回のSendデータが結合されて受信バッファにセットされ、受信イベント処理内で数回分のSendデータをGetData

    するという状況です。ただ、bytesTotal、BytesReceivedでチェックして送信バイト数合計と受信バイト数合計は

    必ず一致します。これが、他のタイマーイベント処理のプログラムと同時に実行させた場合に、どのような挙動を

    示すのか、繰り返しいろいろなパターンで確認しようとしているところです。今のところデータ落ちはなさそうです。

    ただ、STXとETXを区切り文字として送信データに付加して送信し、受信側でその区切り文字で送信データを抽出する

    ようにしましたが、TCP/IPの場合、送信データのバイト数はパケットに送信されませんので、受信側で受信バイト数を

    チェックしてもデータ落ちはわからないのが欠点です。

    今は、ローカルネットワークでテストしていますのでデータ落ちはありませんが、ブロードバンド経由の場合にどう

    なるのか、いろいろな回線経由でのテストをする予定です。

    アドバイスありがとうございました。また、思いつくことがございましたらリプライ下さい。

    このように推察しお考え頂き、感謝申し上げます。今後ともよろしくお願いします。

    2017年6月27日 8:53
  • 文章が長いだけで、質問者さんは何を尋ねたいのでしょうか? 質問文には

    クライアント側が5回正常にSend できてしまうのですが、何故か、ホスト側では、2回の受信イベントData_ArrivalでGetDataしか実行できていません(何故か2回しか受信できません)。ホスト、クライアントとも特にエラーも発生しておりませんし、これ以上、単純なシーケンスは考えられなく、ほとほと困りはてています。

    と書かれていたので各回答者はこれに答えていいたのですが。

    そしてTCP/IPの仕様として送信内容が結合・分割されることは避けられないことはくどいほど説明しています。その上で、

    数回のSendデータが結合されて受信バッファにセットされ、受信イベント処理内で数回分のSendデータをGetDataするという状況です。ただ、bytesTotal、BytesReceivedでチェックして送信バイト数合計と受信バイト数合計は必ず一致します。

    とのことですから、何も問題は発生していません。論点を明確にしてください。

    2017年6月27日 12:51
  • VB6でも組んでみましたが、くっついて読み出されました。DoEventsを入れると個別になりました。
    Wiresharkでパケットを見ると、DoEventsを入れるとSendData毎のパケットになってはいました。入れないとくっついて送出されてました。(setsockoptでSO_SNDBUFを短くしたら分割されて送出してしまいましたが)
    それでも読み出し側で意図的に他の処理を回している最中だと、その処理をぬけた後にくっついて読み出されました。
    つまり、VB6で1:1になっていたのは偶然でしょう。
    #とりこぼしというのは発生してませんが、受信側で処理が追いつかなくて受信バッファからあふれたら取りこぼすかもね。

    IBMの専用回線とやらをテストする環境はないのでテストはおしまい。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年6月27日 16:02
  • #とりこぼしというのは発生してませんが、受信側で処理が追いつかなくて受信バッファからあふれたら取りこぼすかもね。

    取りこぼしはあり得ません。受信バッファが溢れたら伝送経路上の各バッファに溜まり、最終的には送信バッファに溜まるだけです。送信バッファに溜まっている間はtcpClient.SendCompleteイベントが発生せず、更に送信バッファが溢れた場合、tcpClient.SendDataメソッドが完了しなくなります。送信データはシーケンス番号で管理されているため、中間のデータが抜けたり順序が入れ替わることもなく、受信側で整列後に読み出しが行われます。

    2017年6月27日 21:59
  • 検証頂きありがとうございました。VB6でも発生しましたか。

    こちらで、VB6でやると、サイズ、タイミングにかかわらず1:1になるのですが・・

    とにかく、VBAでくっつくのを条件にコーディングします。

    アドバイスありがとうございました。

    2017年6月28日 3:46
  • 私の言葉足らずでしょうか。また、長々と書きまして申し訳ありません。

    上の部分は、

    TCP/IPでは送信データは回線上でパケット分割送信され受信側で結合されアプリに引き渡されることは承知しています。

    これまでもそのように書いてきたつもりなんですが・・

    下の部分は、

    パケットで分割送信されますので、送信バイト数合計と受信バイト数合計のチェックをしてパケット落ちがないかを確認

    したということです。現状、問題は発生しておりません。

    2017年6月28日 3:56
  • 結局

    現在、Windows10でExcel2016VBAのWinsockコントロールによるプログラムで、
    前回、マルチセッッション対応がうまくいかなかったので、とりあえず単純に、
    ホスト側でWaitconnect→クライアン側よりConnect→接続完了。その後、コマンドボタン
    押下で、クライアント側で、前回送信完了イベントフラグで送信済みを確認→Sendを実行、
    ホスト側では、受信イベントData_ArrivalでGetDataを実行すると、
    クライアント側でコマンドボタン押下後、一回ずつSendするようにすると問題ないのですが、
    Forループで5回ほど自動的に上述のようにしてSendすると、クライアント側が5回正常にSend
    できてしまうのですが、何故か、ホスト側では、2回の受信イベントData_ArrivalでGetDataしか
    実行できていません(何故か2回しか受信できません)。ホスト、クライアントとも特にエラーも
    発生しておりませんし、これ以上、単純なシーケンスは考えられなく、ほとほと困りはてています。
    どうか、VBAとWinsockコントロールに詳しい方にアドバイスを頂きたくよろしくお願いします。
    ちなみに、WindowsXPでVB6で作成した時はこんな現象は出ませんでした。
    どうかよろしくお願いします。

    この質問文で尋ねたかったことが何なのかを教えてください。

    こちらからは次のやり取りしか見えていません。

    質問者「5回送信したが2回しか受信されない、ほとほと困りはてている」
    私「結合されるのが仕様である」
    質問者「結合されることはわかっている、1000回ループして~Win7で~VB6で~」
    私「だから何を知りたいの?」

    2017年6月28日 8:23
  • はい、「5回送信したが2回しか受信されない、ほとほと困りはてている」を解決したかったので、

    そのような経験をされた方がいらっしゃれば、アドバイスを頂きたく投稿させて頂いた次第です。

    次に、「結合されるのが仕様である」とお書きになった点ですが、

    私が書きました結合というのは、TCP/IPプロトコルは、パケットで分割され受信側でパケットの属性

    に基づいて結合されアプリに引き渡されると、TCP/IP上の基本的なことを書いただけです。

    受信バッファがくっついてしまうという結合とは意味が違うことをご理解下さい。

    それに、「結合されることはわかっている」という失礼なことは書いていませんが・・

    私は本フォーラムへの参加ははじめてで、簡潔に書かなかったので逆に混乱させたようですみません。

    ディベートをやるつもりはありませんでしたし、ただ、困りごとを解決したかっただけです。

    「だから何を知りたいの?」とかなり憤慨されているご様子です。お手間をとらせたようで申し訳ありません。

    どうかご容赦下さい。現在、他のイベントアプリも同時に動作させてsend、getの同期をとるようにカスタマイズ

    しています。回答者の皆様からいろいろヒントを頂きありがとうございました。

    2017年6月28日 9:26
  • >そのような経験をされた方がいらっしゃれば、アドバイスを頂きたく投稿させて頂いた次第です。
    受信処理を書き直しましょう

    >パケットで分割され受信側でパケットの属性に基づいて結合されアプリに引き渡される
    (シーケンスNOさえ連続であれば)分割パケットを受信後に、結合せず、アプリに引き渡しても、TCPとしては仕様通りの動きです。ここで結合されるとは一切保証されません。

    >受信バッファがくっついてしまうという結合とは意味が違うことをご理解下さい。
    なので、あなたにとっては意味が違うかもしれませんが、TCP上の意味は同じです。

    さて、具体的なアドバイスを書いておきます。

    ・伝文が固定長であるなら、GetDataの第三引数maxLenに、その伝文長を指定すれば解決します。リファレンスにしたがい、Emptyが戻ってくれば次のDataArriveを待ちましょう。
    ・伝文が可変長であるなら、送信側では長さをもつ固定ヘッダを付与して、受信側ではその固定ヘッダをmaxLenに指定し、次にmaxLenには固定ヘッダの指し示す長さを指定して読むようにするとか。
    ・他にはSTX-ETXで囲むとか、CRとかLFのデリミタをつけるとか、(当然受信側はそれをバッファリングして、剥がす処理が要りますがネ)そういう対処が要るでしょう。


    jzkey

    2017年6月28日 11:07
  • アドバイスありがとうございます。一応、Sendデータ毎にSTX、ETXで囲み、データブロックに伝送終了コードのEOTを

    付けて送信、受信側で1シーケンスのデータブロックを受信後、STX、ETXで受信データ配列を抽出するようにしました。

    1SendでSTXからETXの前まで、次にETXからSTXの前までとか、あらゆる送信パターンでテストしましたが、

    問題ないので、現在は、実際の全体システムに組み込み、他のイベント処理と同時処理した場合の確認をしています。

    GetDataの引数maxLenは、送信データの内容により送信バイト数がかなり異なりますので設定はむずかしいです。

    アドバイスのとおり、常に同じバイト数の固定長の送信でしたら有効ですね。でも、同じバイト数の固定長の送信データ

    がくっついて受信するような気もします。いずれにしても、上述のようなブロックシーケンスを組みましたので、

    可変長になろうが、送信データがくっついても、受信後、データ配列として抽出するように割り切りました。

    何だか、昔の2線式の無手順の通信プログラムのようになってしまいましたが、とりあえずはこの方法でやります。

    アドバイス頂き、ありがとうございました。

    2017年6月28日 14:58
  • はい、「5回送信したが2回しか受信されない、ほとほと困りはてている」を解決したかった

    それについてはTCP/IPの仕様であり解決はできない旨、何度も回答しています。

    私が書きました結合というのは、TCP/IPプロトコルは、パケットで分割され受信側でパケットの属性に基づいて結合されアプリに引き渡されると、TCP/IP上の基本的なことを書いただけです。

    質問者さんはTCP/IPの基本的なことが未だに理解できていないようです。送信前に1つのストリームとして結合されます。この時点で個々のSendDataの区切りは消失します。その上で送信側がストリームをパケットに分割します。個々のパケットが適当なタイミングで送信されます。受信側は受信したパケットを1つのストリームに復元します。SendDataの区切りは消失しているため、受信できた適当な区切りでDataArrivalイベントを発生させます。

    更にこの、ストリームへの結合・パケットへの再分割は中継するネットワーク機器(スイッチ・ルータ等)全てで行われるため、Windows 7であっても伝送経路によってはSendData回数とDataArrival回数が一致しない可能性は十分にあります。

    1000回試した、Windows 7やVB 6では発生しなかった等の発言がありましたが、問題を理解せず不十分な検証を繰り返したに過ぎません。

    そのような経験をされた方がいらっしゃれば、アドバイスを頂きたく投稿させて頂いた次第です。

    仕様ですからアドバイスも何もありません。回避策を求めているのであれば、回避したい問題となっているコードを提示しなければ始まりません。再現コードの提示はありましたが回避策を求めているという発言はありませんでしたし。プログラミングとは関係なく、第三者とコミュニケーションするにあたって何を伝える必要があるのかを考えるべきです。

    なお、jzkeyさんが提案されているGetDataのmaxLenパラメーターは推奨できません。Remarksセクションに

    It's common to use the GetData method with the DataArrival event, which includes the totalBytes argument. If you specify a maxlen that is less than the totalBytes argument, you will get the warning 10040 indicating that the remaining bytes will be lost.

    と警告されているように残りのデータがロストします。

    2017年6月29日 2:09
  • そのRemark自体言葉足らずであると思われます。
    10040はWinsockのWSAEMSGSIZEがそのまま帰ってきているのでしょうが、であれば発生するのはUDPに限定されます。
    「A message sent on a datagram socket was larger than the internal message buffer」
    動作を確認したわけではないので↑自体想像ですが、TCPでわざわざ残りのデータを捨てるようであれば、Winsockコントロールは相当アレなコントロールというしかないです。

    jzkey

    2017年6月29日 2:27
  • 上の部分はこれでにもご指摘された内容のようですが・・

    当初のコーディングについては、はじめの頃に掲載させて頂いており、皆様から修正方法をアドバイス頂きました。

    それで、前回書きました、現在のテキストブロック転送処理のプログラムについてはコーディングがかなり大ですし、

    詳細の処理フローを説明するにも長くなりますので割愛させて頂きます。

    TCP/IP云々については、ストリーム→パケットの挙動等、以前にも書かせて頂きましたが・・

    かなり以前に、UNIXのTCP/IPプロトコロルの仕様書、Cソースが公開されていますので、それらで詳細に勉強しま

    した。それで、MSのWinsockについては中身の詳細仕様もソースも公開されていませんので、MSに問い合わせても

    UNIX準拠としか言ってくれません。有償技術サポートでも中身の詳細については開示してくれませんでした。

    jzkeyさんも書かれていましたが、Winsockの挙動については想像するしかないです。

    2017年6月29日 5:23
  • それで、前回書きました、現在のテキストブロック転送処理のプログラムについてはコーディングがかなり大ですし、詳細の処理フローを説明するにも長くなりますので割愛させて頂きます。

    ですから、割愛するなら何を求めているんですか? 「ほとほと困りはてている」と嘆くだけなら質問の形式をとらず、ご自身のblogにでも書けば済む話でしょう。

    TCP/IP云々については、ストリーム→パケットの挙動等、以前にも書かせて頂きましたが・・

    質問者さんがTCP/IPの仕様について書いて何を主張したいのですか? どちらが質問者でどちらが回答者なのか理解していますか? 回答者の回答内容に誤りがあるのであれば、TCP/IPを語るのではなく、回答内容についてコメントしてください。文章が長いだけで必要なことが書かれていないとはそういうことです。

    MSのWinsockについては中身の詳細仕様もソースも公開されていませんので、MSに問い合わせてもUNIX準拠としか言ってくれません。有償技術サポートでも中身の詳細については開示してくれませんでした。

    UNIX準拠で十分な回答ですし、質問者さんの書き込みを読んでいる限りUNIX準拠の動作をしています。ですから、何を問題視されているのですか? いつもいつも不要な話題ばかりで必要なことが何一つ書かれていません。

    2017年6月29日 5:40
  • 1番目は、皆様のヒントを頂いて解決したソースはかなり大なので掲載を見合わせましたが、ヒントを頂いたことに対

    して謝辞のつもりで補足的に書いただけです。

    2番目も主張というわけではなく、TCP/IPに関する基本的なことを書いただけですし、

    3番目は、Winsockの挙動の中身がよくわかりませんから、どこに問題点があるか見えませんねとそう書いただけです。

    それを不必要なことばかり書いてくるとはとは逆に失礼です。そんなに憤慨して感情的になるような内容ですか?

    私が書いた、最終的に解決した伝送シーケンスの内容について読まれましたか?何の回答もなかったですが?

    kenjinoteさん、jzkeyさんにはヒントとなる回答を頂いたり、再現テストをして頂いたり本当に感謝申し上げます。

    小職の書き込みについては今回で終了とさせて頂きます。いろいろとありがとうございました。

    2017年6月29日 11:51