Go言語でpingコマンドっぽい、でもpingコマンドじゃないpongコマンドを作ってみた

# はじめに

* Go 言語で pong コマンドを作成しました
* **PO**ng is **N**ot pin**G** ：「pong コマンドは ping コマンドではありません」
* 本コマンドは kurehajime 様の作った「 [Go言語でpongコマンドを実装する](https://qiita.com/kurehajime/items/f9d225fab5eaa736913b) 」に多大な影響を受けております

# pong コマンドとは？

まずは、以下の画像をご覧ください（下記 Qiita の記事より借用しております）。

:::note warn
注：この画像・コマンドは私の作ったものではありません。
:::

![pong.gif](https://qiita-image-store.s3.amazonaws.com/0/33890/12c873d0-de1d-bb19-ccb7-7134984c09cd.gif)

あるいは以下の記事、もしくはリポジトリをご覧ください。

https://qiita.com/kurehajime/items/f9d225fab5eaa736913b

https://github.com/kurehajime/pong-command

これは kurehajime 様の作った pong コマンドです。

非常に素晴らしいアイデアではないでしょうか。

`pong` と `ping` のキータイプは近いですし（QWERTY配列だと、`O` と `I` は隣り合っている）、`ls` コマンドに対する `sl` コマンドのキータイプ矯正効果に近いものを感じます。

https://ja.wikipedia.org/wiki/Sl_(UNIX)

上記の記事やコマンドのアイデアが素晴らしいと感じた方は、以降の本文を読む前に上記記事に「いいね」を、あるいはリポジトリにスターをしてくるとよいのではないかと思います。

ところで、私はこのコマンドを見たときに、「素晴らしいアイデアだ」と思うと同時に「別の方向性（可能性）もあるのでは？」と思いました。

そこで、まず pong ゲームと、ping コマンドについて整理します。

# pong ゲームとは？

いわゆる卓球(ping pong; ピンポン)のようなゲームです。有史以来、さまざまな pong ゲームが作られてきました。

https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%B3_(%E3%82%B2%E3%83%BC%E3%83%A0)

二人で対戦、あるいはユーザ対コンピュータでの対戦をするのが一般的です。
画面の左右（あるいは上下）に、パドル（ラケット）があり、お互いにパドルを操作してボールを打ち合います。

おそらく e スポーツのはしりといっても過言ではないと思います[^1]。

[^1]:多分、過言です

# ping コマンドとは？

ping コマンドは、IP ネットワークにおいて、ネットワーク上の特定の IP アドレスを持つ機器からの応答があるかを調べるコマンドです。

通常、ping コマンドはターゲットホスト(IP アドレス、あるいはホスト名)を指定して、実行されます。

実行された ping コマンドは、ICMP プロトコルの echo request パケットをターゲットに向けて送信します。
ターゲットホストは、ICMP の echo request パケットを受け取ると、送信元に対して echo reply パケットを返します（設定などにより返さないホストも存在します）。

ターゲットホストが同一ネットワークにない場合には、ネットワーク同士をつなぐ Gateway が echo request/reply パケットをリレーします。その際、パケットにある TTL (Time To Live) の値を 1 だけ減らし、もし減らした結果が 0 の場合にはパケットをリレーせず、破棄します。

ping コマンドは、ターゲットホストから ICMP の echo reply が返ってくること、あるいは返ってこないことで、ネットワーク到達性、応答時間などを記録します。

実行環境(ping コマンドの種類)によって異なりますが、規定の回数、あるいはユーザの停止操作(例： `CTRL+C` など)がされるまで ICMP の echo パケットを送受信して、最後にその結果をレポートします。

https://ja.wikipedia.org/wiki/Ping

# ぼくのかんがえたさいきょうの pong コマンド

そこで、私の考えた pong コマンドは以下のようになりました。

![yet-another-pong.gif](https://github.com/yoshi389111/pong-is-not-ping/raw/main/docs/pong.gif)

ターゲットホストを指定して pong コマンドを実行すると、ローカルホスト（画面の左端にあります）は、ICMP ECHO パケットを送信します。

ターゲットホスト（右側にあるあなたが操作するパドル）で、送信されてくる ICMP ECHO パケットを受け止めて、送信元のホスト（左端のローカルホスト）に送り返してください。

しかし、その間にある Gateway （画面左側のパドル。コンピューターが操作する）は、何を間違えたのか、ICMP ECHO パケットを受信すると、必ずターゲットホスト側に送り返してしまいます。
そこであなたは、Gateway をさけつつ、ICMP ECHO パケットをローカルホストに送り返す必要があります。
Gateway が送り返す際には TTL が減るので、TTL がなくなる前に送り返してください(デフォルトでは TTL は 64 からスタートします)。

また、何度も送り返していると、徐々にネットワークスピードが上がっていきます。注意してください。

デフォルトでは、ローカルホストは ICMP ECHO パケットを 4 回送信します。
最後のパケットを受信(あるいはロスト)した後、あるいはユーザが停止操作をした場合には、それまでのパケット応答状態の統計情報を表示します。

本 pong コマンドの起動方法は以下のようになります。

```
pong [options] <destination>
```

また、オプションとして以下のものが指定可能です。

```
Application Options:
  -h, --help                 print help and exit
  -v, --version              print version and exit
  -c, --count=<count>        stop after <count> replies (default: 4)
  -t, --ttl=<ttl>            define time to live (default: 64)
  -p, --padding=<pattern>    contents of padding byte

Arguments:
  <destination>:             dns name or ip address
```

ソースは以下のリポジトリに置いてあります。

https://github.com/yoshi389111/pong-is-not-ping

kurehajime 様の作った pong コマンド同様に、Go 言語で実装しています。

画面操作ライブラリは、最初 [termbox-go](https://github.com/nsf/termbox-go) を使用していましたが、作者が「もうこのライブラリはメンテナンスしていないから、たとえば [gdamore/tcell](https://github.com/gdamore/tcell) などをつかったらいいんじゃない」というので、gdamore/tcell を使うようにしてみました。と言っても termbox-go 互換機能があるのでそれを使っているだけですが。

:::note warn
注：章タイトルに含まれる「さいきょうの」には意味がありません。なんとなく書いてみただけです。「ぼくのかんがえた pong コマンド」と同じ意味です。
:::