VB2005 の初心者です。
System.Net.Sockets を利用して、Windows端末のAシステムとLinux端末のBシステムの通信部分のサービスを開発しています。
その際、NetworkStream で受信したデータのバイト数を取得したいのですが、やり方が分かりません。
データを取得している部分のソースは下記です。
10000バイトずつ取得していますが、
最後、10000バイトに満たない場合、取得したデータのバイト数が知りたいです。
Dim LNetworkStream As NetworkStream
Dim LbEndFlg As Boolean
Dim LucData(10000) As Byte
Do While LbEndFlg
LNetworkStream = objClient.GetStream()
Redim LucData(10000)
LsLen = LNetworkStream.Read(LucData, 0, LucData.Length)
LbEndFlg = FindEndOfClaim(LucData)
LstrText = System.Text.Encoding.UTF8.GetString(LucData, 0, LsLen)
MstrXML = MstrXML & LstrText
Loop
NetworkStream には、SetLength というメソッドがありますが、
これは必ず例外を発生させてしまうので、
例外を発生させずにバイト数を取得したいです。
何かヒントになることでも良いので、
皆様の知恵をお借りできれば幸いです。
よろしくお願い致します。
No.4ベストアンサー
- 回答日時:
追記。
>以下のように「自分で実装する必要」があります。
もちろん、自分で実装などせずに「ReadTimeoutプロパティに適切な値をセットし、DataAvailableプロパティがTrueだろうがFalseだろうが構わずにReadメソッドを呼び出し、Readメソッドが0以外を返した場合は処理を継続し、Readメソッドが0を返した場合はセッションが切れたかタイムアウトした時だからそこで強制終了する」と言う実装をした方が、遥かに簡単です。
お礼が遅くなり、申し訳ございません。
自分で実装せずに、Readメソッドが0以外を返した場合は処理を継続し、Readメソッドが0を返した場合はセッションが切れたかタイムアウトした時だからそこで強制終了する、という処理にしました。
途中で途切れたデータを送信する送信側のテストプログラムを作成し、動作確認もしました。
説明いただいた内容がとても分かり易く、よく理解できました。
本当にありがとうございました。
No.3
- 回答日時:
>LNetworkStream.ReadTimeout にタイムアウトミリ秒数を設定できますが、
>タイムアウトの秒数に達したことはどのように判定すれば良いのでしょうか。
ReadTimeoutは「DataAvailableプロパティがFalseの状態でReadメソッドを呼び出した際に、Readメソッドの中でデータが来るまで何ミリ秒待つか?」を指示するプロパティです。0を指定した場合は「データが来るまで永久に待つ」になっていたと思います。
DataAvailableプロパティがTrueであれば、Readメソッドは「今読めた分だけ読んで、読んだバイト数」を返します。
DataAvailableプロパティがFalseであれば、Readメソッドは「ReadTimeoutの時間だけデータが来るのを待ち、来た場合は来た分だけ読んで読んだバイト数を、待っても来なかった場合は0」を返します。
多分、質問者さんは「DataAvailableプロパティがFalseであれば、Readメソッドを呼ばない」と言う実装をしている筈ですから、タイムアウト処理は、以下のように「自分で実装する必要」があります。
1.タイマーによるイベントが起きていないかどうかを示すBoolean型グローバル変数をFalseにセットする
2.指定時間後にイベントを起こすタイマーをEnableにする
3.DataAvailableプロパティがTrueになっていてReadメソッドを呼んだ場合は、指定時間後にイベントを起こすタイマーをリセットし、再度、指定時間後にイベントを起こすようにする
4.DataAvailableプロパティがFalseになっていてReadが呼べずに待っている間は、タイマーイベント発生を示すBoolean型グローバル変数がTrueになってないかチェックする。Trueであれば「タイムアウト」として、処理を中断する
5.リード処理のループを抜けたらタイマーをDisableにする(正常終了時も、中断終了時も、どちらの場合でも)
指定時間後にイベントを起こすタイマーからコールバックするイベント関数では、タイマーイベント発生を示すBoolean型グローバル変数をTrueにセットし、タイマーをDisableにする。
No.2
- 回答日時:
>サービスだとNetworkStream の DataAvailable では、
>終了を正しく判定できないのでしょうか。
DataAvailableプロパティは終了を判定するプロパティではありません。
DataAvailableプロパティは「今の瞬間、Readメソッドで読み取り可能なデータがあるか?」を示すのみです。
>LNetworkStream.DataAvailable が、FormだとTrueの所が、
>サービスだとFalseと判定して、データの途中で読み込みを終了してしまいます。
データの送受信は非同期で行われる為、送信よりも受信の方が早く処理されると「受信が送信に追い付いてしまい、途中で受信すべきデータが尽き、続きが送られて来るのを待たなければならない」と言う状況になります。
「途中で受信すべきデータを待つ」とは「途中でReadメソッドで読み取り可能なデータが尽き、DataAvailableプロパティがFalseになるので、再びTrueになるのを待つ」と言う意味です。
つまり「DataAvailableプロパティがFalseになる状況」とは、
・すべてのデータが届いて、それ以上送信するデータが無い時
・受信が送信に追い付いてしまい、途中で受信すべきデータが尽きた時
・送信側がハングアップした時
・送信側が故意に途中で通信を切断した時
・通信の途中で不意に回線が切れた時
などです。
質問者さんは「DataAvailableプロパティがFalseになる状況」を
・すべてのデータが届いて、それ以上送信するデータが無い時
しか想定していないので、想定外の状況が起きると
>データの途中で読み込みを終了してしまいます。
と言う事になります。
「データの終了」は「来なかったら終り」ではなく「データの中には存在しない、特殊な終了マークが来たら終わり」とか「送り側は、データ長情報など、最初に固定長の制御情報を送り、それに続いて可変長のデータを送り、受け側は、最初に受け取った制御情報の中のデータ長情報を使って、その長さまで読んだら終わり」とかって処理で判定します。
むろん、こういう処理を行えば、送信側がハングアップした時などに「データが途中までしか来てないが、いっこうに続きが来る気配がない」なんて事も起きます。つまり、強制切断時の対処やタイムアウトの処理も必要になるって事です。
再度、詳細な説明をいただき、本当にありがとうございます。
通信では、XMLデータを受信しているのですが、
3種類の文字コードで送信される可能性があるため、
受信データの末尾がEOT、かつ、
受信データをMSXML2.DOMDocumentでLoadできれば、
終了と判定してループから抜けるようにしました。
とりあえず、正常処理は正しく終了するようになりましたが、
送信側がハングアップした時などのために、タイムアウトの処理を追加したいのですが、
やり方が分かりません。
LNetworkStream.ReadTimeout にタイムアウトミリ秒数を設定できますが、
タイムアウトの秒数に達したことはどのように判定すれば良いのでしょうか。
それらしきメソッドはないのでしょうか。
何度も初歩的な質問で大変申し訳ありませんが、
何かご存知でしたらご教授いただければ幸いです。
よろしくお願い致します。
No.1
- 回答日時:
>LsLen = LNetworkStream.Read(LucData, 0, LucData.Length)
>LstrText = System.Text.Encoding.UTF8.GetString(LucData, 0, LsLen)
この2行の意味、理解してますか?
1行目:LucDataの位置に、LucDataのサイズ(10000バイト)分、読み込んで、結果として「読み込んだバイト数」をLsLenに格納する。
2行目:LucDataの位置の「さっき読んだバイト数(LsLen)」の分のデータを、UTF8の文字列と思ってエンコードして、結果文字列をLstrTextに格納する。
>最後、10000バイトに満たない場合、取得したデータのバイト数が知りたいです。
「取得したデータのバイト数」とは何か?
「LsLen」には何が格納されているか?
この2点を「よ~く考えて」みましょう。
それと「10000バイトづつ読んで、最後だけ10000に満たない」と思っているようですが、そんな事はありません。仕様書のどこにも必ずそうなるとは書かれていません。
もしかしたら「1回目は9997バイト、2回目は9996バイト、3回目は3521バイトで終了」って可能性もあります。
あと、この読み込み処理はバグってます。根本的な変更が必要です。
「1回目の読み込みの10000バイト目に、多バイト文字の第1バイトが来て、2回目の読み込みの1バイト目に、多バイト文字の第2バイト以降が来たら、どういう結果になるか?」を考えてみて下さい。
例えば、全角文字の「あ」は、UTF8では16進で「E3、81、82」の3バイトになります。「あ」を3335文字送ると「E3、81、82」が3335回繰り返した、10005バイトのデータが送られます。
読み込みは10000バイト単位で繰り返すので、1ブロック目のデータは
00001:E3
00002:81
00003:82
00004:E3
|
09997:E3
09998:81
09999:82
10000:E3 ☆
になり、2ブロック目のデータは
10001:81 ☆
10002:82 ☆
10003:E3
10004:81
10005:82
になります。
すると、System.Text.Encoding.UTF8.GetStringは、上記の☆が付いたデータを、正しく「あ」にエンコードできません。
つまり、貴方が書いたプログラムは「10000バイト単位に、他バイト文字が文字化けし、正しく受信できない」と言うバグがあるのです。
ですので、System.Text.Encoding.UTF8.GetStringで文字コードのエンコードを行う際は「全部のデータを取得し終わってから、最後に1回だけ、一気に行う必要」があります。
それが不可能であれば「改行コードなど、絶対に多バイト文字じゃない文字で区切ってreadする」、つまり、「1行ごとに読み込みを繰り返す」などの処理が必要でしょう。
ともかく、バグってて使い物にならないので、上司から指摘されて恥をかく前に、全面的に作り直しましょう。
お恥ずかしいソースにも関わらず、丁寧なご説明、ありがとうございました。
教えていただいたとおり、文字コードのエンコードは、全部のデータを取得し終わってから行うよう修正しました。
しかし、Formなら正しく動くのですが、Serviceにすると正しく動きません。
LNetworkStream.DataAvailable が、FormだとTrueの所が、
サービスだとFalseと判定して、データの途中で読み込みを終了してしまいます。
下記が、データを取得部分のソースです。
Do
LNetworkStream = objClient.GetStream()
Redim LucData(100000)
'受信データの読み出し
LsLen = LNetworkStream.Read(LucData, 0, LucData.Length)
Redim Preserve LucTotalBytes(LsTotalLen + LsLen)
'データを連結する
For LiCnt = 0 To LsLen - 1
LucTotalBytes(LsTotalLen + LiCnt) = LucData(LiCnt)
Next
'全バイト数
LsTotalLen = LsTotalLen + LsLen
Loop Until LNetworkStream.DataAvailable = False
サービスだとNetworkStream の DataAvailable では、
終了を正しく判定できないのでしょうか。
何かご存知のことがあれば、ご教授いただければ幸いです。
何度も申し訳ありませんが、よろしくお願い致します。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
このQ&Aを見た人はこんなQ&Aも見ています
-
ゆるやかでぃべーと タイムマシンを破壊すべきか。
これはディベートの論題だと仮定したうえでの回答お願いします。あなたは、その末にタイムマシンを壊してしまうのか、使い道を探すのかどうかを考えてもらいたいです。
-
フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
あなたが普段思っている「これまだ誰も言ってなかったけど共感されるだろうな」というあるあるを教えてください
-
映画のエンドロール観る派?観ない派?
映画が終わった後、すぐに席を立って帰る方もちらほら見かけます。皆さんはエンドロールの最後まで観ていきますか?
-
海外旅行から帰ってきたら、まず何を食べる?
帰国して1番食べたくなるもの、食べたくなるだろうなと思うもの、皆さんはありますか?
-
天使と悪魔選手権
悪魔がこんなささやきをしていたら、天使のあなたはなんと言って止めますか?
-
NetworkStreamからのRead()で、処理が止まる(C#)
C言語・C++・C#
-
TCP/IP通信時のサーバーからの受信
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
エクセルVBA WEBからデータ取...
-
【C#】textBoxの指定行のデータ...
-
C# DataTableに最後に追加した...
-
【VB.NET】Excelの最終行までの...
-
【EXCEL VBA】 1/100秒単位で時...
-
ListViewで表示されたデータの...
-
ACCESS VBA フォーム複数条件
-
クリスタルレポートでレコード...
-
Excel VBA で日付を4ケタの数値...
-
VB2008,DateTimePickerで指定す...
-
非同期のプロセス間通信(パイプ...
-
エクセルのCSV読み込みについて
-
VBAコンボボックスの内容が反映...
-
ActiveReportでデータが0件の場...
-
富士通(汎用機)のAIMについて
-
【ExcelVBA】値を変更しながら...
-
ExcelVBAで戻り値を返すには
-
XMLでデータとして画像を指定す...
-
Excel VBAでフォルダ内の全テキ...
-
「Nullの使い方が不正です」の...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
「Nullの使い方が不正です」の...
-
【C#】textBoxの指定行のデータ...
-
VBAでアクセスDBからデータの取...
-
C# DataTableに最後に追加した...
-
COBOL数値転記の仕様
-
XMLでデータとして画像を指定す...
-
Excel VBAでフォルダ内の全テキ...
-
VBAコンボボックスの内容が反映...
-
VB2010で、選択した系列を最前...
-
【VB.NET】Excelの最終行までの...
-
【ExcelVBA】値を変更しながら...
-
Excel VBAでグラフの可変データ...
-
部品表
-
アクセスでウェブ上のデータを...
-
クリスタルレポートでレコード...
-
MSFlexGrid 行選択状態
-
エクセルのCSV読み込みについて
-
ADODBでの行番号の取得、もしく...
-
富士通(汎用機)のAIMについて
-
エクセルのセル最終行取得
おすすめ情報