1

HTTP Put リクエストを 60 秒ごとに RoR Web サイト/アプリに送信する必要があるクライアント側コードに奇妙な問題があります。

問題は、クライアント側のアプリがいくつかのリクエストを Web サイトにプッシュすることです (2 から 9 のリクエストの任意の場所)。その後、クライアント側のコードは、最初の数回のプッシュが成功した後、http put リクエストの送信を停止します。

情報: クライアント側アプリは C# Windows アプリケーションです。Web サイトは RoR 3.2 を実行しています。

http putを送信するc#コード

    static void HttpPutRequest(string Json)
    {
        Logger("Sending JSON: " + Json);
        try
        {
            var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://miningmonitor.herokuapp.com/workers/update");
            httpWebRequest.ContentType = "application/json";
            httpWebRequest.Method = "PUT";
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                Logger("To URL: " + httpWebRequest.Address.ToString());
                streamWriter.WriteLine(Json);
                streamWriter.Flush();
                streamWriter.Close();
                try
                {
                    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
                    var wRespStatusCode = httpResponse.StatusCode;
                    Logger("Website return code: " + wRespStatusCode.ToString());
                }
                catch (WebException we)
                {
                    var wRespStatusCode = ((HttpWebResponse)we.Response).StatusCode;
                    Logger(" Exception and Website return code: " + wRespStatusCode.ToString());
                }
            }
        }
        catch (WebException we2)
        {
            var GetRequestStreamExp = ((HttpWebResponse)we2.Response).StatusCode;
            Logger(" Exception trying to setup httpWebRequest.GetRequestStream: " + GetRequestStreamExp.ToString());
        }
    }

クライアント側の C# ロギング ステートメント

Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5953","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5956","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.62"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5964","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5970","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5978","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5989","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5995","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5999","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.63","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6009","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6015","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.63","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6021","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6029","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.64"]}

その時点から、そのjsonを作成するだけで送信されません。

put リクエストの送信を示すサーバー ログ

 Aug 13 10:31:52 miningmonitor heroku/router: at=info method=PUT path=/workers/update host=miningmonitor.herokuapp.com fwd="198.244.99.204" dyno=web.1 connect=8ms service=101ms status=200 bytes=2005 

これを 60 秒ごとに実行する方法は、C# でタイマーを使用することです。しかし、コードが using ステートメントの入力を停止し、JSON 文字列の送信を停止する理由がわかりません。これは、C# コードのログ ステートメントで確認できます。using ステートメントがどのように機能するのかよくわかりません。オンラインで見つけた例からそのステートメントを変更して、http 要求を送信しました。したがって、誰かが using ステートメントについても説明できれば、それは素晴らしいことです。

タイマーコードを追加します。

    public void update(string user_worker)
    {

      System.Timers.Timer timer = new System.Timers.Timer(60000);
      timer.Elapsed += (sender, e) =>
      {
          //query the miner for summary and gpucount information
          String SummaryQuery = QueryMiner("summary");
          String gpuNum = FindKey(QueryMiner("gpucount"), "Count");
          //String PoolQuery = QueryMiner("pools");
          int numgpus = Convert.ToInt32(gpuNum);
          //Array of strings to hold each gpu query
          String[] gpuQueries = new String[numgpus];
          //add the GPU queries into the array
          for (int i = 0; i < numgpus; i++)
              gpuQueries[i] = QueryMiner("gpu|" + i);

          //now add information specific to each gpu to a list
          List<string> gpuList = new List<string>();
          for (int i = 0; i + 1 <= gpuQueries.Length; i++)
          {
              gpuList.Add(FindKey(gpuQueries[i], "Temperature"));
              gpuList.Add(FindKey(gpuQueries[i], "MHS 5s"));
          }
          //set all the values that we have gotten from the queries
          this.worker_user_name = user_worker;
          this.hashrate = FindKey(SummaryQuery, "MHS av");
          this.accepted = FindKey(SummaryQuery, "Accepted");
          this.rejected = FindKey(SummaryQuery, "Rejected");
          this.hw_errors = FindKey(SummaryQuery, "Hardware Errors");
          this.num_gpu = gpuNum;
          this.gpus = gpuList.ToArray();
          //create JSON from the workerUpdate object
          string JSON = JsonConvert.SerializeObject(this);
          //send to website
          HttpPutRequest(JSON);
      };
     timer.Start();
    }

ログで例外をキャッチしました。例外は以下です。しかし、それが何を意味するのかわかりません。

Exception: System.NullReferenceException: Object reference not set to an instance of an object.
 at MiningMonitorClientW.WorkerUpdate.HttpPutRequest(String Json)
 at MiningMonitorClientW.WorkerUpdate.<>c__DisplayClass1.<update>b__0(Object sender, ElapsedEventArgs e)

更新:さらにデバッグを行ったところ、エラーが発生すると、この行までコードが実行されることに気付きました

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))

おそらく、ストリーム書き込みを作成しようとしていて、ストリームを要求しているときに、null 応答が返されますか? わからない。ただし、例外は何かがnullであると言っています。

4

2 に答える 2

1

これで問題が解決するわけではありませんが、問題がどこにあるかを見つけるのに役立ちます。

イベント ハンドラーに try/catch を追加する必要があります。System.Timers.Timerそうしないと、例外が発生した場合、ハンドラーをエスケープする例外が抑制されるため、それを知ることはできません。以下のコードでは、既存のコードに try/catch を追加しただけです。

public void update(string user_worker)
{

  System.Timers.Timer timer = new System.Timers.Timer(60000);
  timer.Elapsed += (sender, e) =>
  {
    try
    {
      //query the miner for summary and gpucount information
      String SummaryQuery = QueryMiner("summary");
      String gpuNum = FindKey(QueryMiner("gpucount"), "Count");
      //String PoolQuery = QueryMiner("pools");
      int numgpus = Convert.ToInt32(gpuNum);
      //Array of strings to hold each gpu query
      String[] gpuQueries = new String[numgpus];
      //add the GPU queries into the array
      for (int i = 0; i < numgpus; i++)
          gpuQueries[i] = QueryMiner("gpu|" + i);

      //now add information specific to each gpu to a list
      List<string> gpuList = new List<string>();
      for (int i = 0; i + 1 <= gpuQueries.Length; i++)
      {
          gpuList.Add(FindKey(gpuQueries[i], "Temperature"));
          gpuList.Add(FindKey(gpuQueries[i], "MHS 5s"));
      }
      //set all the values that we have gotten from the queries
      this.worker_user_name = user_worker;
      this.hashrate = FindKey(SummaryQuery, "MHS av");
      this.accepted = FindKey(SummaryQuery, "Accepted");
      this.rejected = FindKey(SummaryQuery, "Rejected");
      this.hw_errors = FindKey(SummaryQuery, "Hardware Errors");
      this.num_gpu = gpuNum;
      this.gpus = gpuList.ToArray();
      //create JSON from the workerUpdate object
      string JSON = JsonConvert.SerializeObject(this);
      //send to website
      HttpPutRequest(JSON);
    }
    catch (Exception ex)
    {
        // handle exception here
    }
  };
 timer.Start();
}

を書くのは一般的に悪い習慣catch (Exception)ですが、 の設計はそれをSystem.Timers.Timer強制します。ドキュメントが言うように:

.NET Framework バージョン 2.0 以前では、Timer コンポーネントは、Elapsed イベントのイベント ハンドラーによってスローされたすべての例外をキャッチして抑制します。この動作は、.NET Framework の将来のリリースで変更される可能性があります。

その動作は、.NET 4.5 の時点で変更されていません。

例外をキャッチしない場合、タイマーは例外を抑制します。つまり、例外が発生したことを知ることはできません。

アップデート

ほとんどの場合、処理方法がわかっている特定の例外のみをキャッチする必要があります。たとえば、 and をキャッチIOExceptionしたい場合WebExceptionは、次のように記述します。

catch (WebException wex)
{
    // handle WebException
}
catch (IOException ioex)
{
    // handle IOException
}

上で述べたように、 catch は悪い習慣と見なされExceptionます。なぜなら、おそらくそれを処理する方法がわからず、未知のエラーに直面してやみくもに続行するよりも、処理されない例外でプログラムをクラッシュさせる方がおそらく良いからです。ただし、は例外を抑制するため、ログに記録する以外の理由がない場合System.Timers.Timerは、ハンドラーですべての例外をキャッチして、一体何が起こったのかを知る必要があります。Elapsedまたは、ハンドラーがメインスレッドに、物事がうまくいかず、シャットダウンする必要があることを通知する何らかの方法がある場合があります。したがって、Elapsedハンドラーでは、鼻をつまんで、通常はすべきでないことを行う必要があります。すべての特定の例外の後に、次を追加します。

catch (Exception ex)
{
    // handle all other exceptions
}
于 2013-08-13T22:12:19.410 に答える