iOS 端末で Let's ソケットプログラミング！ 

ネットワークの基礎となる TCP/IP を理解するために、私はソケットプログラミングによる試行錯誤（Trial and Error）とネットワークトレースのパケット解析を避けては通れないと思っています。

本稿では、ソケットプログラミングをシミュレートする iOS アプリ **SocTest** を紹介します。

## SocTest とは

[Developer site](https://manabapp.github.io/SocTest/index_ja.html) より抜粋：

>SocTest は、カーネルに対してシステムコールを発行し、インターネットや組織内のイントラネット上の様々なサーバーとソケット通信を行うモバイルアプリです。
>SocTest のネットワーク操作は POSIX C API で実装されています。高度に抽象化された API を挟まず、必要以上にエラー判定せず、可能なかぎりカーネルに処理を委ねるように設計されています。

アプリは、[App Store](https://itunes.apple.com/jp/app/id1551906028?mt=8) で無料で公開されています。


## SocTest の使い方

具体例として、Wi-Fi 接続した状態で Safari ブラウザで ifconfig.io へアクセスする操作をシミュレートしてみます。

[参考]
ifconfig.io とは、接続元 IP アドレスを返してくれる WEB サービスです。
<img alt="QiitaImage.001.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/89038710-d13e-0510-2287-8153fabef922.jpeg" width="600">

この場合、Safari で http://ifconfig.io/ip にアクセスすると、ifconfig.io が接続元であるグローバル IP アドレス「106.171.xx.97」を返します。
<img alt="safari.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/65ecc788-53bd-4039-edd9-8ade658de8d1.jpeg" width="200">


## 使用例①
Safari で実施しているソケット操作は、図に示したシステムコールを順番に発行していると考えられます。
<img alt="QiitaImage.002.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/c9cadcdf-609f-251f-4c37-3057c6631e68.jpeg" width="600">

では、SocTest アプリを起動して実際にシミュレートしてみます。

#### 1-1. (事前準備) Address タブで ifconfig.io のアドレスを作成します
![simulate1.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/ae1682dd-6e4f-4d75-9b09-0e44347a262d.jpeg)

#### 1-2. (事前準備) Data タブで HTTP リクエストのデータをバイナリエディタで作成します
![simulate1.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/c363933e-b902-fe0e-17a2-ce6142d1a463.jpeg)

HTTP の規約で、末尾は改行コード **CRLF** が2つ連続している必要があります。

```http:リクエストデータ
GET /ip HTTP/1.1
Host: ifconfig.io
User-Agent: Mozilla/5.0 (SocTest; iOS 14.4)

```

#### 1-3. Socket タブでソケットを作成し、ifconfig.io に接続します
![simulate1.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/290e332a-d11e-de46-9978-7c3f0d97a5cf.jpeg)

#### 1-4. HTTP リクエストを送信します
![simulate1.004.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/c52144e9-983b-58e6-36a1-ca9f426344e8.jpeg)

#### 1-5. HTTP レスポンスを受信します
![simulate1.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/7e8479c2-0d50-86d3-23ec-c2b6ab7acaf0.jpeg)

```http:レスポンスデータ
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2021 07:08:28 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 14
Connection: keep-alive
Set-Cookie: __cfduid=d526f31d8fba79e413589646c00846ad81612505125; expires=Sun, 07-Mar-21 07:08:28 GMT; path=/; domain=.ifconfig.io; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
cf-request-id: 0811f0af920000f8a7b5a18000000001
Report-To: {"max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=dRvaw9S5bWhoae65VS1Spu6gT0I7LC4gdroUsse1iHMn%2By5yKxRkaJFyT%2F8zspLQP4sG64nvciiLQstZ15ILuG0bs204eSI5A%3D%3D"}],"group":"cf-nel"}
NEL: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 61c9b6f8efcaf8a7-NRT
alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400

106.171.xx.97
```

Safari と同様の結果「**106.171.xx.97**」が得られたことが確認できましたね。

#### 1-6. (後始末) ソケットをクローズします
![simulate1.006.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/54ca346f-f9b0-f4b0-9dad-273473da61e4.jpeg)

#### 1-7. (おまけ) ソケットシステムコールの発行内容を確認します

SocTest には、Linux の strace 相当のシステムコールトレース機能が搭載されています。
上記手順でどのようなシステムコールが発行されたのか確認できます。
![simulate1.007.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/69a2919b-1bad-3866-a915-e207fcaeaa91.jpeg)

```console:トレース内容
16:08:18.446 Feb 05, 2021 - Asia/Tokyo (current)
16:08:18.446 Start (pid:2674)
16:08:20.899 [0.000098] socket(PF_INET, SOCK_STREAM, 0) = 3
16:08:24.676 [0.017037] connect(3, {sin_family=AF_INET, sin_port=80, sin_addr="104.21.65.79"}, 16) = 0
16:08:28.611 [0.000234] send(3, "GET /ip HTTP/1.1."..., 84, 0) = 84
16:08:35.069 [0.000066] recv(3, "HTTP/1.1 200 OK.."..., 4096, 0) = 787
16:08:45.045 [0.000150] close(3) = 0
```


## 使用例②
iOS では、モバイル通信と Wi-Fi が同時に接続されている状態では、Wi-Fi が優先して使用されます。
今度は、この状態であえてモバイル通信側（4G回線）を使用して ifconfig.io にアクセスできるか試してみます。
<img alt="QiitaImage.003.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/41f6581e-6fad-885b-c047-3d57049b8df7.jpeg" width="600">

では、SocTest アプリを起動して実際にシミュレートしてみます。

#### 2-1. (任意) アプリの動作設定で AUTO MONITORING を有効化します
このパラメータを有効にすると、着信データの有無や接続状態を監視するモードで動作します。
今回は、このモードを使ってみます。
![simulate2.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/2bccc096-75de-f4dd-416f-faa1dfbb500f.jpeg)

#### 2-2. モバイル通信のインターフェース（自分自身）のアドレスを確認して、ソケットに割り当てます
![simulate2.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/32c3ebca-1f0d-840d-15e0-530cad93f679.jpeg)

[参考]
ここでは、未使用の 60000 という適当なポート番号を指定しました。
0 を指定すると接続（connect）時に、カーネルによって自動的に採番されます。

#### 2-3. ソケットを接続し、HTTP リクエスト/レスポンスの送受信を行います
![simulate2.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844861/1cb7546d-cdb1-776d-67cd-98ab6aaa299c.jpeg)

```http:レスポンスデータ
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2021 07:59:24 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 15
Connection: keep-alive
Set-Cookie: __cfduid=de405e0a328b95a506f1099afe86f66cc1612508698; expires=Sun, 07-Mar-21 07:59:24 GMT; path=/; domain=.ifconfig.io; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
cf-request-id: 08129b30e60000d635e4917000000001
Report-To: {"max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=G%2FxZA%2FLfDXM9dmMBsjZvSZ81B2MvsomKic0bDswtuWFZ3SNKEkBIKrAModd2oaTqM2vG4GD35c%2Bbfw2%2BdzWTQet8s4Vk%2BeCS6V4SDA%3D%3D"}],"group":"cf-nel"}
NEL: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 61cac7c7d8aad635-NRT
alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400

153.147.xx.155
```

ifconfig.io によって、モバイル通信のアドレス「**153.147.xx.155**」が返されました。
つまり、Wi-Fi ではなく、モバイル通信側のネットワークが使用されたことが確認できました。

## さいごに

SocTest では、TCP / UDP（ICMP も対応予定）のソケットをサポートしています。
60 個のソケットオプションを使用でき、Keep-Alive や Broadcast などのテストもできたりします。
sendmsg/recvmsg システムコールにより制御メッセージを使用した細かな制御もできたりします。 


ネットワークに興味がある方、「ソケットプログラミングは敷居が高い」と敬遠されている方は、SocTest でソケット通信を体験してみてください！

おわり。