質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

90.48%

  • C#

    9215questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Arduino

    689questions

    Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

TCPサーバー(ESP32)がクライアント(C#)の要求に対して応答しなくなる

受付中

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 112

kawauso

score 39

お世話になります。

行いたいこと
サーバー: ESP32
クライアント: C#プログラム
上記のような構成でTCP通信したいと考えています。
それぞれは同一のルーターに接続さてています。

問題点
クライアントであるC#プログラムのTcpClientのインスタンスを生成するところで下記のようなエラーが出力されることがあります。
エラーが出力されない場合は、問題なく通信できています。
出力されるタイミングはまちまちです。電源投入後10分ほどでエラーが出力されることもあれば、1時間以上大丈夫なこともあります。
▼エラー文
System.Net.Sockets.SocketException: '接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。 または接続済みのホストが応答しなかったため、確立された接続は失敗しました。'

質問
プログラムを記載します。原因・解決策などご存知の方は教示いただければ幸です。
ESP32の電源に関しては、PCのUSB、乾電池(直列3V)、直流安定化電源(3.3V)を試しました。

プログラム

// サーバー
#include <WiFi.h>

const char* ssid = "*****";
const char* password = "*****";
IPAddress ip(192, 168, 11, 100);
IPAddress gateway(192, 168, 2, 1);
IPAddress subnet(255, 255, 255, 0);

WiFiServer server(80);

void setup(void) {
  Serial.begin(9600);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  String cmd;
  cmd = rcvCommand();

  if (cmd != "") {
    Serial.print("cmd: ");
    Serial.println(cmd);
  }

  delay(100);
}

String rcvCommand() {
  WiFiClient client = server.available();

  String rstr;
  if (client.connected()) {
    Serial.println("Connected to client");

    //コマンド文字列受信(文字列が来なければタイムアウトする)
    rstr = client.readStringUntil('\r');
    Serial.print("[");
    Serial.print(rstr);
    Serial.println("]");

    //応答送信
    client.print("OK\r");

    //接続をクローズ
    client.stop();
    Serial.println("Closed");
  }

  return rstr;
}
// クライアント
            string sendMsg = "hoge";
            string ipOrHost = "192.168.11.100";
            //string ipOrHost = "localhost";
            int port = 80;

            //TcpClientを作成し、サーバーと接続する
            // 【ここでエラーが出力されることがあります】
            System.Net.Sockets.TcpClient tcp =
                new System.Net.Sockets.TcpClient(ipOrHost, port);
            Console.WriteLine("サーバー({0}:{1})と接続しました({2}:{3})。",
                ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Address,
                ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Port,
                ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Address,
                ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Port);

            //NetworkStreamを取得する
            System.Net.Sockets.NetworkStream ns = tcp.GetStream();

            //読み取り、書き込みのタイムアウトを10秒にする
            //デフォルトはInfiniteで、タイムアウトしない
            //(.NET Framework 2.0以上が必要)
            ns.ReadTimeout = 10000;
            ns.WriteTimeout = 10000;

            //サーバーにデータを送信する
            //文字列をByte型配列に変換
            System.Text.Encoding enc = System.Text.Encoding.UTF8;
            byte[] sendBytes = enc.GetBytes(sendMsg);
            //データを送信する
            ns.Write(sendBytes, 0, sendBytes.Length);
            Console.WriteLine(sendMsg);

            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            byte[] resBytes = new byte[256];
            int resSize = 0;
            do
            {
                //データの一部を受信する
                resSize = ns.Read(resBytes, 0, resBytes.Length);
                //Readが0を返した時はサーバーが切断したと判断
                if (resSize == 0)
                {
                    Console.WriteLine("サーバーが切断しました。");
                    break;
                }
                //受信したデータを蓄積する
                ms.Write(resBytes, 0, resSize);
                //まだ読み取れるデータがあるか、データの最後が\nでない時は、
                // 受信を続ける
            } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');
            //受信したデータを文字列に変換
            string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
            ms.Close();
            //末尾の\nを削除
            resMsg = resMsg.TrimEnd('\n');
            Console.WriteLine(resMsg);
        }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

0

ESP32側でシリアルに動作ログをずっと流しておいてPCでモニタして、そのエラーが出るときになにが起こってるのか見てみることですね。

まあ、考えつくこととしては、ESP32の消費電流が大きくて、電源の電圧ドロップしてリセットかかってしまうってのだけど。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/11 14:22

    回答ありがとうございます。
    直流安定化電源(A&D AD-8724D)を使用していますが、電圧降下するものなのでしょうか。
    電圧降下しないようにする対策などはあるのでしょうか。

    キャンセル

  • 2019/06/11 14:28

    接続状況にもよります
    3.3Vを直接供給してる場合、0.2Vほどのスパイクでもリセットが働きます

    が、まずはログを確認して、エラーが起きるときにリセットがかかってるのかを調べてみては

    キャンセル

  • 2019/06/11 14:38

    回答ありがとうございます。
    リセットが掛かったかどうかのログはどのように記述すればよいでしょうか。
    エラーの際にsetupのprint文が実行される=リセットがかかったとういことでしょうか。

    キャンセル

  • 2019/06/11 14:48

    ああ、エラーのときにsetupが実行されてるならリセットがかかってますね
    なら電源周りを強化しないといけないですね
    いくら頑強な電源を用意してても、長い線でつないでたら電流の変化で電圧ドロップします

    ESP32の直近に3.3Vの3端子レギュレータをつけて、5V程度以上の電源で駆動すればいいです

    キャンセル

  • 2019/06/11 15:39 編集

    すみません。書き方が悪かったようです。
    上記のものは「このようになれば、リセットが掛かったことになりますか?」という意味で書きました。

    実際にエラー時にリセットがかかって再度setupが実行するかを確認しましたが、setupのprint文は実行されませんでした。
    この状態でESP32に対してpingを打ってみましたが要求はタイムアウトされました。

    キャンセル

  • 2019/06/12 16:32

    Core Debug levelを"Verbose"にしてスケッチを書き込むとESP32が詳細なログを吐くようになります
    これで確認してみてはどうでしょう。

    キャンセル

  • 2019/06/12 16:49

    回答ありがとうございます。
    Core Debug levelを"Verbose"にしてスケッチを書き込んでシリアルモニタで監視しましたが、エラー時に何の出力もありませんでした。
    スケッチ自体に変更を加える必要はあるのでしょうか。

    キャンセル

0

ESP側の
rstr = client.readStringUntil('\r');
に対して、C#側では'\r'を送信しているところが無いような気がします。単に略されてしまっているだけだとは思いますが。もし本当に送っていないならreadStringUntil()がメモリを食い尽くしている、なんていうこともありそうですが、その辺どうなんでしょう。
逆に、ESP側では'\r'を送っているのにC#側では'\n'を終端とみていたり、ちょっと疑問に思えます。

それと、TCPにおいては、データの単位は1バイト以上でも以下でもないので、本来は「データの塊」というものはなく、「データの最後が\nでない時は受信を続ける」という考え方は上手く動くように見えるかも知れませんがしかし危険な気がします。'\n'を受信したらそこまでのデータを一塊と認識する、ならアリだと思いますけれど、'\n'が最後になる保証はなにもないので。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/12 16:12

    回答ありがとうございます。
    ご指摘いただいたサーバーとクライアント側で終端をそれぞれ「\n」と「\r」で
    揃えて試してみましたが、同様のエラーが出力されました。
    ただloop内でrcvCommand()を呼ばなければ、安定して動いていることは確認できました。

    キャンセル

同じタグがついた質問を見る

  • C#

    9215questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Arduino

    689questions

    Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。