実現したいこと
ローカルネットワークでhttp-serverを実装しています。
基本的にhttp-serverのプロセスは常時起動したままですが、
serverプロセスに不具合が発生した際にユーザーさん側からサーバーの再起動ができるようにしたいです。
前提
C# のWindowsフォームアプリケーションを使用しています。
プロセスに異常が発生してアクセス不能になった場合等に、ユーザーさんがプロセスを再起動できるようにしたいです。
ポート番号8080を使用しているserverプロセスを特定して
①起動しているならkill⇒プロセス起動
②していないならプロセス起動
としたいです。
発生している問題・エラーメッセージ
プロセスが特定できない。
エラーメッセージ http-serverプロセスは見つかりませんでした。 というメッセージが出る。
該当のソースコード
C#
1//* 2//* http-serverの起動 3//* 4 // http-serverの起動コマンドを作成 5 string httpServerCommand = @"http-server C:\\test_http -p 8080 -d=false"; 6 7 // プロセス情報を設定 8 ProcessStartInfo startInfo = new ProcessStartInfo(); 9 startInfo.FileName = "cmd.exe"; 10 startInfo.Arguments = $"/c {httpServerCommand}"; 11 startInfo.UseShellExecute = false; 12 startInfo.CreateNoWindow = true; 13 14 // 外部プロセスを起動 15 httpServerProcess = new Process(); 16 httpServerProcess.StartInfo = startInfo; 17 httpServerProcess.EnableRaisingEvents = true; 18 httpServerProcess.Start(); 19 20//* 21//*http-serverを8080ポートで検索して停止させる 22//* 23private void StopHttpServerProcess() 24{ 25 int portNumber = 8080; 26 Process existingProcess = GetProcessByPort(portNumber); 27 if (existingProcess != null) 28 { 29 existingProcess.Kill(); 30 existingProcess.WaitForExit(); 31 32 Console.WriteLine("http-serverプロセスを停止しました。"); 33 } 34 else 35 { 36 Console.WriteLine("http-serverプロセスは見つかりませんでした。"); 37 } 38} 39 40private Process GetProcessByPort(int port) 41{ 42 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); 43 IPEndPoint[] tcpEndpoints = ipProperties.GetActiveTcpListeners(); 44 45 foreach (IPEndPoint endpoint in tcpEndpoints) 46 { 47 if (endpoint.Port == port) 48 { 49 int processId = GetProcessIdByPort(endpoint); 50 if (processId != -1) 51 { 52 return Process.GetProcessById(processId); 53 } 54 } 55 } 56 57 return null; 58} 59 60private int GetProcessIdByPort(IPEndPoint endpoint) 61{ 62 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); 63 TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections(); 64 65 foreach (TcpConnectionInformation connection in tcpConnections) 66 { 67 if (connection.LocalEndPoint.Equals(endpoint)) 68 { 69 return connection.RemoteEndPoint.Port; 70 } 71 } 72 73 return -1; 74}
試したこと
1.cmd.exeで起動する際に識別子をつけてみる
⇒識別子の取得が出来ず、判定できない
2.port番号を指定して検索
⇒見つからない
補足情報(FW/ツールのバージョンなど)
・Visual Studio 2019
ここにより詳細な情報を記載してください。
http-serverが複数起動しないならプロセス名で探してもいいのでは?
自作サーバーなら、特定のリクエストを受け付けた時に終了するように作っておけば良いかと。
>>http-serverが複数起動しないならプロセス名で探してもいいのでは?
回答ありがとうございます。その方法も試してみたのですがprocess.ProcessNameやprocess.MainModule.FileNameが上手く参照できないんですよね…。アクティブなプロセスIDを取得してGetProcessByIdで引っ張て来ているだけなんですけど…。
>>自作サーバーなら、特定のリクエストを受け付けた時に終了するように作っておけば良いかと。
回答ありがとうございます。なんらかの異常でhttp-serverにアクセスやリクエストが飛ばない状態になった時にプロセスを消す手段が欲しかったんです。遠方のユーザーさんにローカル環境で使用してもらうプログラムなので、自分が十分にサポートできそうにないんですよね…。PC再起動すればプロセス切れるんですけど、アプリ上からもプロセスの再起動ができればユーザーフレンドリーかなと思いまして…。
プロセス名で探すのがありならProcess.GetProcessesByNameで引っ張ればいいのでは?
GetProcessIdByPort()の実装がプロセスIDを返しておらず、クライアントのポート番号を返しています。
WindowsならIPHelperのGetExtendedTcpTable()等を使って探すのがご質問の答えだと思いますが
この目的なら皆さんが言われるようにプロセス名を探すほうが速いと思います。
kill を仕様にしてしまうと、ファイルの書き込みとかできなくなるのであまり好ましくないです。
サービスとして作成すれば、名前を指定して停止することも、停止前に何かの処理をすることもできるので、そちらをおすすめします。
サービスにできない時は、名前付きパイプなどを使ってプロセス間通信し、自分で停止させるのがおすすめです。
>>YAmaGNZさん
>>hqf00342さん
回答ありがとうございます。
上記のプログラムの通り
// プロセス情報を設定
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = $"/c {httpServerCommand}";
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
のようにしてプロセスを開始しています。
プロセス名で検索するのがありというか、http-serverのプロセスを特定できればどのような方法でも構いません。
この場合プロセス名はcmdになると思うのですが、この認識であっていますでしょうか?
cmdで探した場合他のプロセスも停止させることになってしまうため、識別子付きで起動してhttp-serverだけ停止しようとしたのですが、識別子をうまく取得できずに断念しました。
>>Zuishinさん
サービスとして登録ですか…
サービスとして登録する方法で実装できるか試してみます。回答ありがとうございます。
cmd経由で起動してもプロセスはあるはずです。
http-serverが動いている環境でタスクマネージャーや簡単なテストコードでみるとよいと思います。
var plist = Process.GetProcesses();
//又は var plist = Process.GetProcessesByName("http-server");
foreach(var p in plist)
{
Console.WriteLine($"{p.Id} {p.ProcessName}");
}
ただ、親cmdも一緒に消したいとなると面倒ですね。
cmd経由でプロセス開始する必要がなければ直接起動したほうが良い気もします。
>>hqf00342さん
回答ありがとうございます。
http-serverの起動前と後のすべてのプロセスを見比べると以下のプロセスが追加されていました。
nodeというのがおそらくhttp-serverのプロセスです。
cmd経由せずに起動が難しそうなのでどうするべきか迷っています…。
Process ID: 4292, Name: cmd
Process ID: 8044, Name: conhost
Process ID: 8896, Name: SearchProtocolHost
Process ID: 9080, Name: ServiceHub.TestWindowStoreHost
Process ID: 9804, Name: node
http-server周りの情報がないので不確かですがnodeをkillすればよいと思います。
nodeが複数あって特定できなかったり、親プロセスのcmdもkillしたいのであればwmiを利用して探すことになると思います。
WMIで親プロセスIDとコマンドラインを表示するサンプルを書いておきます(System.Managementの参照、またはnugetからインストールが必要)
using System.Management;
var mos = new ManagementObjectSearcher();
mos.Query.QueryString = "select * from win32_process";
var result = mos.Get();
foreach (var obj in result)
{
Console.WriteLine($"pid={obj["ProcessId"],5} ppid={obj["ParentProcessId"],5} name={obj["Name"],16} cmd={obj["commandline"]}");
}
プロセス名が分かるならこれらでプロセス特定すれば十分だと思いますが、必要であらばLISTENポート番号からプロセスIDを探す部分を回答に書きます(長いです)。
返信が遅れ申し訳ございません。
こちらで確認してみたところ、nodeのプロセスをkillすると親プロセスも消えているようです。
とりあえずこれで動かしてみます。
今回の自分の環境では大丈夫ですが、この方法の場合他のnodeで動いているプログラムも削除してしまうため後ほど技術的負債になりそうです。
他の方がおっしゃられていたサービスを使う方法も含めてもう一度考えてみたいと思います。
対応ありがとうございました。
回答2件
あなたの回答
tips
プレビュー