はじめに
本記事は自分の学習記録&技術力向上を目的としており、ほぼすべてが以下のページからの抜粋となっております。営利目的ではありません。詳しいことを学びたい方は以下ページを参照したほうがよいです。
引用元:
netstatとは
Windowsのコマンド。実行ファイル名はnetstat.exe。
現在アクティブになっているTCPコネクションの状態を表示することができる。
TCPの通信モデル
以下のようにIPアドレスとポート番号を指定してTCPコネクションを確立する。
例えばWebブラウザを使ってWebサーバにアクセスする場合、実際にはHTTP(Hypertext Transfer Protocol)というプロトコルが利用される。WebサーバのHTTPポート(TCPの80番ポート)とWebブラウザの間でTCPコネクションが開設され(クライアント側のポート番号は任意)、Webサーバへコマンドを送ったり、Webサーバからの応答メッセージを受信したりする。
TCPの通信モデル
TCPは、2つのコンピュータ間(アプリケーション間)で通信路(コネクション)を開設し、お互いにデータをやりとりするための機能である。例えばWebサーバへ接続する場合、TCPを使ってWebサーバ(の80番ポート)とWebブラウザの間で通信路を作成する。TCPでは、それぞれのポート番号とIPアドレスの組み合わせでコネクションを識別する。
引用元:https://atmarkit.itmedia.co.jp/ait/articles/0207/20/news003.html
netstatコマンド実行
引数なしでnetstatコマンドを実行する
netstatコマンドにオプションなどの引数を付けずに実行すると、現在アクティブなTCPコネクションの一覧が表示される。
何もオプション引数を付けずにnetstatを実行すると、現在アクティブなTCPコネクションの状態が表示される(後述する「状態」が「LISTENING」ではないコネクションが表示される)。
引用元:https://atmarkit.itmedia.co.jp/ait/articles/0207/20/news003.html
各列の説明
- 「プロトコル」列
「プロトコル」列には使用中のネットワークプロトコルの種類が表示される。後述する「-a」オプションを付けていない場合は、常に「TCP」と表示されているはずである。UDPにはコネクションを確立するという概念がないので(UDPではデータを単発的に送るだけなので)、このコマンドではUDPの通信状態は表示されない。
- 「ローカルアドレス」「外部アドレス」列
コンピュータ名(もしくはIPアドレス)と使用中のTCPのポート番号が表示。
「ローカル アドレス」は、ローカル側のコンピュータ名(もしくはIPアドレス)と使用中のTCPのポート番号である。「外部アドレス」は、通信の相手となっているマシンのコンピュータ名(もしくはIPアドレス)とポート番号である。
同じマシン同士でもコネクションを確立できるので、「外部アドレス」が自分のIPアドレスの場合もある。
「ローカル アドレス」に表示されるIPアドレスは固定的ではない。1台のマシンには複数のIPアドレスを付けることができるし、ローカルのループバックアドレス(IPv4なら「127.0.0.1」、IPv6なら「[::1]」など)が使われていることもあるからだ。また同じマシン同士でもコネクションを確立できるので、「外部アドレス」が自分のIPアドレスの場合もある。
- 「状態」列
「状態」は、その名の通りTCPコネクションの「状態(ステート)」を表すものだ。例えば「LISTENING」は待ち受け状態、「ESTABLISHED」は通信が確立した状態、「TIME_WAIT」はコネクションの終了待ち状態をそれぞれ表していて、通信中にはこれらの状態から別の状態に移る「状態遷移」がひんぱんに生じる
TCPはステート(状態)を持つプロトコルであり、コネクションの開始や終了、通信方法などに関して細かい手順が決められている。サーバとクライアントのどちらの側から先に通信を開始するのか、データの送受信はどうするのか、終了する場合はどうするのか、などが厳密に決まっている。
また通信速度は有限なので、送信したコマンドが相手側へ届くまでにはタイムラグがある。ローカル側から先に終了コマンドを送ったとしても、それが相手側に届く前に、相手側から先に終了要求が到着することがある。そのような状況でも、破綻のないように処理手順が決められている。
次の図はTCPの状態遷移を大まかに表している。この図中の白い四角の中に書いてある「SYN_SENT」や「ESTABLISHED」などが、netstatコマンドの「状態」列に表示される。
TCPの状態遷移図
TCPの規格書(RFC793)に記載の状態遷移図を簡略化して、分かりやすくしたものだ。四角の中に書かれているのがTCPの状態名。矢印は可能な遷移の経路。矢印のそばにある文字は「トリガ(アクション)」を表しており、ある「トリガ」となるイベントが発生すると指定された「アクション」を実行して、次の状態へ遷移する。各状態の詳細は下表で説明する。
TCPコネクションには「アクティブ」側と「パッシブ」側がある。
CLOSEDは表示されない。
「-a」オプションをつけるとLISTENING状態のTCPコネクションが出てくる。
SYN_SENTはSYNに対する応答ACKを受信していない状態。送信相手が無応答の時もこの状態になる。
細かい状態(図中の白い四角)の意味は次の通りである。これを調べることにより、コネクションの状態がどうなっているか、開始しようとしているのか、待ち受け中なのか、終了しようとしているのか、などが分かる。
「LISTENING」状態とはどういうことか
「LISTENING」状態とは、プロセスが聞く準備ができている状態のことをいう。
ポートが開いていたとしてもプロセスが死んでいると通信できない(netstatでLISTENING状態か確認)。
プロセスが聞く準備ができている状態でもポートが空いていないと通信できない(ポートが別プロセスに使用されていないか、ファイアーウォール機能で閉じられていないか確認)。
プロセスとコネクションの関連を調べるには(「-o」、「-b」オプション)
「-o」オプションを付けてnetstatを実行すると、そのコネクションを所有しているプロセスのID(PID)も表示される。
netstatの「-o」オプションでコネクションを所有しているプロセスを調べる
「-o」オプションを付けると、そのコネクションを所有しているプロセスのID番号が表示される。
「-o」ではなく「-b」オプションを付けると、実際のプロセス名が表示される。これを実行するには、コマンドプロンプトを管理者権限で起動する必要がある。
TCPListener.ps1を実行すると、LISTEN状態になる。
PowerShellでTCPコネクションを張ってみた
# ポート番号を変数で指定
$port = 5000
# .NETの TcpListener クラスを使い、指定ポートを待ち受け
$listener = New-Object System.Net.Sockets.TcpListener([IPAddress]::Any, $port)
$listener.Start()
Write-Host "Listening on port $port ... (press Ctrl+C to stop)"
try {
while ($true) {
# クライアントからの接続があるまで Accept で待機
$client = $listener.AcceptTcpClient()
# ここから先は好きなようにデータを読み書きできます
# 簡易的には、何もしないで即切断でもOK
Write-Host "Connection accepted from" $client.Client.RemoteEndPoint
# 必要に応じて処理...
# 例: $stream = $client.GetStream(); ...; $stream.Close()
# セッションを張り続けたいのでコメントアウト
# $client.Close()
}
}
finally {
$listener.Stop()
}
接続させたいPCのIPアドレスに変更し、TCPClient.ps1を実行する。FireWall設定でポートは開けておくこと。
# 接続先サーバーIPまたはホスト名
$serverIp = "127.0.0.1"
# 接続先ポート (サーバー側の Listener が待ち受けているポート)
$port = 5000
Write-Host "Attempting to connect to $serverIp:$port..."
# .NET の TcpClient クラスを使用してサーバーに接続
$client = New-Object System.Net.Sockets.TcpClient
$client.Connect($serverIp, $port)
Write-Host "Connected to $serverIp:$port. (press Ctrl+C to stop)"
# Stream を取得 (サーバーとの送受信用)
$stream = $client.GetStream()
try {
while ($true) {
# ここに、任意の処理(読み書き等)を入れることができる
#
# 例: サーバーからデータ受信
# if ($stream.DataAvailable) {
# $buffer = New-Object Byte[] 1024
# $readBytes = $stream.Read($buffer, 0, $buffer.Length)
# if ($readBytes -gt 0) {
# $receivedText = [System.Text.Encoding]::UTF8.GetString($buffer, 0, $readBytes)
# Write-Host "Received: $receivedText"
# }
# }
#
# 例: サーバーに定期的にメッセージを送る
# $message = "Hello from client."
# $bytes = [System.Text.Encoding]::UTF8.GetBytes($message)
# $stream.Write($bytes, 0, $bytes.Length)
#
# 何もしないで Idle 状態を維持したい場合は Sleep だけでOK
Start-Sleep -Seconds 1
}
}
finally {
Write-Host "Closing connection..."
$stream.Close()
$client.Close()
}
Test-NetConnectionコマンドは内部で何をしている?
指定ポートが空いているかを確認するためのコマンド。長期でTCPコネクションを張り続けてくれるものではない。
3WayHandShakeしてコネクションを切る(約0.03秒で)