DataArrivalで、複数データを受信するには?

解決


泡盛  2005-02-26 01:45:22  No: 119941

DataArrivalイベントを利用して複数クライアントからの受信データ(256バイト固定長)を利用したいのですが、クライアントが一斉にデータを送信すると、受信データをうまく整理することができません。(混在する場合がある)
よいアドバイスがあれば教えてください。
なお、現在

Private Sub tcpServer_DataArrival(Index as Integer, Byval bytesTotal As Long)
    Dim tmpBuff As String

    tcpServer(Index).GetData tmpBuff, vbString, bytesTotal

    mRecLen(Index) = mRecLen(Index) + bytesTotal

    mRecvBuff(Index) = mRecvBuff(Index) & tmpBuff

    If (mRecLen(Index) >= 256) then

         ---  処理  ---

    End If

    :
    :

といった感じです。


泡盛  2005-02-26 01:47:28  No: 119942

追加ですが「mRecvBuff(Index)に、複数クライアントからの受信データが混在してしまいます。


。。。  2005-02-26 09:54:58  No: 119943

このソースだけでは現象が再現不可能なので、恐らく原因は他にあるのではないでしょうか。

私も、Winsockコントロールを使用して複数クライアントからの接続を行う
プログラムを作ったことがありますが、一斉に送信しても一つ一つの処理が
遅くなるだけで混在することはありませんでした。


泡盛  2005-02-26 18:56:52  No: 119944

「。。。」さん。早速の情報ありがとうございます。
私の情報が皆無でまことに申し訳ありません。
まず、環境ですが、OS:Windows2000 アプリケーション:VB(6.0)です。
プログラムの流れを簡単に申し上げますと、、、

Private Sub Form_Load()
    ReDim mRecLen(mMaxConnections)
    ReDim mRecvBuff(mMaxConnections)

    ' Winsockコントロール配列を作成
    For idx = 1 To mMaxConnections
        Load tcpServer(idx)
        tcpServer(idx).LocalPort = 0
        
        mRecLen(idx) = 0                ' 受信レコード長を初期化
        mRecvBuff(idx) = ""             ' 受信データの初期化
    Next

    ' リスニングコントロールの初期化
    tcpServer(0).LocalPort = cCONNECT_PORT
    tcpServer(0).Listen

End Sub

Private Sub tcpServer_ConnectionRequest
           (Index As Integer, ByVal requestID As Long)
    Dim idx As Integer

    If (Index = 0) Then
        For idx = 1 To mMaxConnections
            If (tcpServer(idx).State = sckClosed) Then
                tcpServer(idx).Accept requestID
                Exit For
            End If
        Next
    End If
End Sub

といった感じで、接続を待ちうけ、初回に書いた内容でデータを取得
しております。

通常の通信ではデータの混在は無いのですが、約7台以上のクライア
ントが一斉にデータ送った場合に、稀(数百回に1度程度)ですが、
データ混在が発生します。
(PHSを利用した基地局からのデータ受信です)

これでも未だ情報不足とは思いますが、何かヒントがあれば、教えて
ください。

どうぞ宜しくお願いします。


。。。  2005-02-26 19:09:47  No: 119945

mRecLen(Index)の初期化はどこでやっているのでしょう?
単に前の接続のデータが残っているとかではないですか?


泡盛  2005-02-26 21:43:45  No: 119946

「。。。」さん。お返事ありがとうございます。

早速なのですが、最初に見ていただいた「DataArrival」の中で、mRecLen(Index)の初期化を入れてあります。
下のような感じです。

Private Sub tcpServer_DataArrival(Index as Integer, Byval bytesTotal As Long)
    Dim tmpBuff As String

    tcpServer(Index).GetData tmpBuff, vbString, bytesTotal

    mRecLen(Index) = mRecLen(Index) + bytesTotal

    mRecvBuff(Index) = mRecvBuff(Index) & tmpBuff

    If (mRecLen(Index) >= 256) then

         ---  ↓↓処理↓↓  ---
          :
          :
         ---  ↑↑処理↑↑  ---

        mRecLen(Index) = 0          '<----この部分です。
        mRecvBuff(Index) = ""

    End If

初期化の場所が不適切なのでしょうか。。。?
素人質問で申し訳ないのですが、教えてください。
宜しくお願いします。


たけ  2005-03-01 04:23:08  No: 119947

クライアントとサーバーの通信の仕様はどうなっているのでしょう?
クライアントは1回の接続で256バイト送信すると接続を切断するのですか?
続けて次のデータを送信したりはしないのですか?
つまり、上記初期化方法では前回のデータと今回のデータの一部を受信した
時にDataArrivalイベントが発生すると今回のデータの一部が初期化されて
しまう可能性があります。


泡盛  2005-03-02 02:53:26  No: 119948

たけさん。問題解決のご協力ありがとうございます。

<クライアントとサーバーの通信の仕様はどうなっているのでしょう?
<クライアントは1回の接続で256バイト送信すると接続を切断するのですか?
<続けて次のデータを送信したりはしないのですか?

上記質問についてですが、クライアントはデータ送信後、サーバーが返すデータの受信を行った後に、接続を切断しております。
また、クライアントが送信するデータは256バイト固定にしております。

しかし、一点気になることが。。

         ---  ↓↓処理↓↓  ---
          :
          :
         ---  ↑↑処理↑↑  ---

の部分ですが、

     Call subXXXX(mRecvBuff(Index))

     tcpServer(Index).SendData mRecvBuff(Index)
     DoEvents

のようなことをしており、プロシージャで使用する引数と戻り値に同じものを利用しております。
この処理は4秒近くかかることもあり、これが原因で受信データと送信データ混在しているように思えます。(気のせいでしょうか???)
取り合えず、プロシージャ利用の際の引数と戻り値を切り分け、mRecvBuff(Index) の初期化をプロシージャ呼び出し前に行って症状がでないか確認してみます。
・・・実はテストできるのが今週3日になりますので・・・。

もし、ご意見等がございましたら、もう少しご教授願います。

宜しくお願いします。


ガッ  2005-03-02 03:43:43  No: 119949

横スマソ

ムチャクチャ勘だけど、DoEventsが少し怪しい気がする…
後、一応確認なんですけど、
複数のクライアントが送ったものが混ざっている様に見えるだけ、
というわけではないですよね?

※最近Winsockコントロールの挙動が怪しすぎるので、
  自分だけのライブラリを作り始めたガッでした。
  …もしかしたらWinsockコントロールのバグだったりして…
  ぃぁ…出来ている人がいるので、それはないか(orz


泡盛  2005-03-02 18:10:12  No: 119950

ガッさん。アドバイスありがとうございます。

<複数のクライアントが送ったものが混ざっている様に見えるだけ、
<というわけではないですよね?

ですが、改めて送受信データを解析したところ、複数クライアントのデータ混在ではなく、自分自身の送受信データ混在が発生していると断定できました。

したがって、やはり使用変数の初期化辺りに原因があるように思われます。
3日(木)に確認したいと思います。

しかし・・・。Winsock コントロールのバグ・・・あるのでしょうか・・・。(5.0のsp5ですが・・・)情報収集に努めてみます。

どうもありがとうございました。

その他にも、よろしければ色々とアドバイスお願いします。

宜しくお願いします。


泡盛  2005-03-02 18:12:55  No: 119951

すみません。Winsockですが、間違えていたので訂正します。

5.0 (SP5) --> ×
6.0 (SP5) --> ○

です。


ねろ  2005-03-03 03:30:33  No: 119952

2つ気になる点があります。
1、グローバルで宣言した文字列を、あちこちで使いまわすと
    思わぬバグが発生する。
2、次から次と割り込みがかかるサブルーティンで時間のかかる
    処理をすると、思わぬ不具合が発生する。

ここの2つの問題を同時に解決するには、Timerを使う手がありますが、
私ならWinsockコントロールと同じIndex番号を持つTextBoxを非表示で用意し、
  If (mRecLen(Index) >= 256) then
     Text1(Index) =  mRecvBuff(idx) 
  mRecvBuff(idx) = 0
  End If
とやり
テキストボックスのチェンジイベントの中で、
Private Sub Text1_Change(Index As Integer)
  ---  ↓↓処理↓↓  ---
          :
          :
        ---  ↑↑処理↑↑  ---
End Sub
とやる。
こうするとmRecvBuff(Index)はローカルで宣言すれば良く、
Private Sub tcpServer_DataArrival(・・・・)
の割り込みルーチンもデーターを受け取ってすぐに開放できる。


泡盛  2005-03-04 04:01:39  No: 119953

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

本日テストした結果、前回よりも多いアクセスにも関わらず、症状が
全くでなくなったので、うまいこと改善できたと思われます。

改善した内容は、送受信で利用していた「mRecvBuff(Index)」これを
データ受信時のみ利用し、「>=256」True で初期化しました。
また、「処理」のところでも、引数と戻り値を別で定義しました。

全く素人バグ&対応といった感じでお恥ずかしいのですが、「ふーん」
程度に受け止めてください。

しかしながら、皆様のアドバイスは今後のコーディング力アップに
役立てていきたいと思います。

本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加