WindowsTCP/IPネットワーク

# はじめに
本記事は自分の学習記録&技術力向上を目的としており、ほぼすべてが以下のページからの抜粋となっております。営利目的ではありません。詳しいことを学びたい方は以下ページを参照したほうがよいです。

引用元：

https://atmarkit.itmedia.co.jp/ait/articles/0207/20/news003.html




https://qiita.com/h3pei/items/196124b9fb36578e5c80

# netstatとは
Windowsのコマンド。実行ファイル名はnetstat.exe。
現在アクティブになっているTCPコネクションの状態を表示することができる。


# TCPの通信モデル
以下のようにIPアドレスとポート番号を指定してTCPコネクションを確立する。
>例えばWebブラウザを使ってWebサーバにアクセスする場合、実際にはHTTP（Hypertext Transfer Protocol）というプロトコルが利用される。WebサーバのHTTPポート（TCPの80番ポート）とWebブラウザの間でTCPコネクションが開設され（クライアント側のポート番号は任意）、Webサーバへコマンドを送ったり、Webサーバからの応答メッセージを受信したりする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/904f9213-3271-4630-8d14-9abb18370ac6.png)
TCPの通信モデル
TCPは、2つのコンピュータ間（アプリケーション間）で通信路（コネクション）を開設し、お互いにデータをやりとりするための機能である。例えばWebサーバへ接続する場合、TCPを使ってWebサーバ（の80番ポート）とWebブラウザの間で通信路を作成する。TCPでは、それぞれのポート番号とIPアドレスの組み合わせでコネクションを識別する。

引用元：https://atmarkit.itmedia.co.jp/ait/articles/0207/20/news003.html

# netstatコマンド実行

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/56c85dad-0610-4f26-8465-de8e0175fb0f.png)
引数なしで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コマンドの「状態」列に表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/eb71c36a-6bb9-42dc-bc6a-4adf7b53b164.png)
TCPの状態遷移図
TCPの規格書（RFC793）に記載の状態遷移図を簡略化して、分かりやすくしたものだ。四角の中に書かれているのがTCPの状態名。矢印は可能な遷移の経路。矢印のそばにある文字は「トリガ（アクション）」を表しており、ある「トリガ」となるイベントが発生すると指定された「アクション」を実行して、次の状態へ遷移する。各状態の詳細は下表で説明する。

TCPコネクションには「アクティブ」側と「パッシブ」側がある。
>上図で色分けされたブロックは、TCPの状態を大まかに分類したものだ。それぞれの意味を下表に記す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/9d32b2b3-7781-4155-acaf-4861e7ff22f4.png)

CLOSEDは表示されない。
「-a」オプションをつけるとLISTENING状態のTCPコネクションが出てくる。
SYN_SENTはSYNに対する応答ACKを受信していない状態。送信相手が無応答の時もこの状態になる。
>　細かい状態（図中の白い四角）の意味は次の通りである。これを調べることにより、コネクションの状態がどうなっているか、開始しようとしているのか、待ち受け中なのか、終了しようとしているのか、などが分かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/1ecc806d-d8af-4c9a-a09e-2e18296f9395.png)

# 「LISTENING」状態とはどういうことか
「LISTENING」状態とは、プロセスが聞く準備ができている状態のことをいう。
ポートが開いていたとしてもプロセスが死んでいると通信できない(netstatでLISTENING状態か確認)。
プロセスが聞く準備ができている状態でもポートが空いていないと通信できない(ポートが別プロセスに使用されていないか、ファイアーウォール機能で閉じられていないか確認)。

# プロセスとコネクションの関連を調べるには（「-o」、「-b」オプション）
>「-o」オプションを付けてnetstatを実行すると、そのコネクションを所有しているプロセスのID（PID）も表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/f0411104-8425-4b50-b083-ef655ad2424a.png)
netstatの「-o」オプションでコネクションを所有しているプロセスを調べる
「-o」オプションを付けると、そのコネクションを所有しているプロセスのID番号が表示される。

>「-o」ではなく「-b」オプションを付けると、実際のプロセス名が表示される。これを実行するには、コマンドプロンプトを管理者権限で起動する必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/9b085cb0-7321-429c-84ae-15e0df465152.png)

TCPListener.ps1を実行すると、LISTEN状態になる。
# PowerShellでTCPコネクションを張ってみた
```powershell:TCPListener.ps1
# ポート番号を変数で指定
$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設定でポートは開けておくこと。
```powershell:TCPClient.ps1
# 接続先サーバー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秒で)