C# シリアル通信のバイナリデータ受信

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 4,496

START_

score 25

現在、VisualStudio2015 のWindowsフォームアプリケーションにてソケットを使用したシリアル通信のコードを書いています。

基本的な通信(送受信)はできるようになったのですが、受信メソッドにて

 //受信データのバイト数分蓄積
  do
  {
     int length = ComPort.Read(buffer, readlen, rbyte - readlen);
     readlen += length;
  } while (readlen < rbyte);
   recData += System.Text.Encoding.ASCII.GetString(buffer);
   logText(recData, ComPort);


これで送られてきたByteをそのバイト分貯めてから
文字列に変換してテキストボックスにinvokeして書き込んでいますが、受信時にデータが分割されて送られてしまいます。

なので改行まで取得するように改良して以下のようにしました。

 // 受信データを改行まで蓄積
do
{
   int length = ComPort.Read(buffer, readlen, rbyte - readlen);
   readlen += length;
} while (readlen < rbyte);
 recData += System.Text.Encoding.ASCII.GetString(buffer);
 newline = recData.IndexOf(Environment.NewLine);
if (newline >= 0)
{
   logText(recData, ComPort);
}
else
{
   Sendbuffa += recData;       //データをいったん保存
}


ですが、これだと一度の2回送信分のデータ(改行コードがふたつある)を受信してしまったときに、
1回送信分の中に改行コードが2つあると判断されてしまいます。
なので、改行コードを見つけたらその時点でテキストボックスに書きだしたいのですが、どう修正すればよいでしょうか。

尚、上記両コードとも表記したコードをCOMポート接続されている間ループさせております。

よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

こんにちは。
Split()関数を使って改行(\r\n)で分割してはどうでしょうか?

            // 改行で分割
            string[] lines = recData.Split(
                new[] { Environment.NewLine },
                StringSplitOptions.None
            );

            if (lines.Length > 1)
            {
                // lines[0] だけを送信して残りはバッファに詰めて次回
            }
            else
            {
                // lines[0]を送信して終わり
            }

 EDIT

記述が不親切だったのでコード例を記載します。

            recData += System.Text.Encoding.ASCII.GetString(buffer);
            var temporary = Sendbuffa + recData;
            while (true)
            {
                newline = temporary.IndexOf(Environment.NewLine);
                if (newline >= 0)
                {
                    string[] lines = temporary.Split(
                        new[] { Environment.NewLine },
                        StringSplitOptions.None
                    );
                    logText(lines[0], ComPort);

                    if (lines.Length > 1)
                    {
                        // temporaryに残りを詰めなおす
                        temporary = temporary.Substring(newline + 2);
                    }
                }
                else
                {
                    Sendbuffa = temporary;       //データをいったん保存
                    break;
                }
            }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/13 12:05

    横から失礼します。
    Sendbuffa += temporary;
    だと、詰め直したデータが消えませんか?
    まぁ、元ソースがそうなっているんですけど。

    また、データ詰め直しの部分の
    if (lines[1] == "\r\n") break;
    の部分は、必要ないのではないでしょうか?

    キャンセル

  • 2018/04/13 12:12

    > だと、詰め直したデータが消えませんか?
    └ 確かに消えます

    > の部分は、必要ないのではないでしょうか?
    └ 確かになくても問題ないですね

    修正します。
    けど、y_waiwaiさんの回答を採用するのがスマートですね

    キャンセル

  • 2018/04/13 12:50

    皆さんコメントありがとうございます。

    今回最終的にはバイナリデータに対応したいのでこのような質問内容にさせていただきました。
    教えていただいたこと試してみます!

    キャンセル

+2

文字列を流すのを前提とするなら、素直にReadline メソッドを使うなりしたほうがいいと思いますねー

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/13 12:46

    y_waiwai 様
    いつもコメントありがとうございます。 
    今は文字列のテストですがバイナリデータを扱えるものにしたいのでこのようにしております。

    キャンセル

  • 2018/04/13 13:00

    シリアルに限らず、TCPとかUDPとかの通信も含め、XXXバイト送信したのに、受信ではXXバイトとXXバイトに分けて受信される、XXバイトとXXバイトの2回送ったのにXXXXバイトにまとめて受信されてしまった
    というのはしごく当然の話となります。
    それを前提としてコードを組む必要があります

    一回の受信で改行が2つ受信されてしまったらどうしよう、というのではなく、受信イベントではとにかく今まで受信したデータを貯めていく、そして、その中から改行があればそこまでを1行として取り出す、という考え方で行かないとあとで苦労することになろうかと思います

    で、ReadLineメソッドというのは、上記の処理を実装しているんですね

    キャンセル

  • 2018/04/13 16:49

    >>受信イベントではとにかく今まで受信したデータを貯めていく
    なるほどです。ReadLineのことを認識違いしていました。
    これからなにかを受信するときは心がけるようにします!!
    ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる