4D InternetCommandsプラグイン廃止により、低レベルTCP/IP通信機能を自力で実装しないといけなくなった話 #2（サーバー）

# はじめに
[前回の記事](https://qiita.com/KarteMaker/items/4d0ab967b65e080faf15)の続きです。
前回は肝心の通信機能ではなく、その前のXcodeをどう使うかという前段階の前段階のお話で終わってしまいました。
今回はちゃんと通信のお話です。
前回紹介した書籍や調べたwebサイトの情報を元にとりあえず動作確認という感じのコードを書いてみます。

# サーバー
サーバー側の手順は大まかにいうと

　・ソケットの作成（socket関数）
　　　　↓
　・ポートの割り当て（bind関数）
　　　　↓
　・通信の受け入れ開始（listen関数）
　　　　↓
　・接続の確立（accept関数）
　　　　↓
　・データの送受信（recv関数、send関数）
　　　　↓
　・ソケットを閉じる（close関数）

 というような流れになります。
 実際に書いたコードはこんな感じ
 ```c:ts.c
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE     32768
#define PORT        3456

int main(int argc, const char * argv[]) {
    
    int s0;
    int s;
    struct sockaddr_in server;
    struct sockaddr_in client;
    unsigned int len;

    long rn;
    char recv_buf[BUFSIZE];
    
    
    //ソケットの作成
    if ((s0 = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    
    
    //ポートの割り当て
    memset(&server, 0, sizeof server);
    server.sin_family       = AF_INET;
    server.sin_addr.s_addr  = htonl(INADDR_ANY);
    server.sin_port         = htons(PORT);
    
    if (bind(s0, (struct sockaddr *) &server, sizeof server) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    
    
    //接続キューの割り当て。通信の受け入れ開始
    if (listen(s0, 5) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    
    //接続の確立
    len = sizeof client;
    if ((s = accept(s0, (struct sockaddr *) &client, &len)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    
    
    //データの取得
    if ((rn = recv(s, recv_buf, BUFSIZE - 1, 0)) >= 0) {
        
        recv_buf[rn] = '\0';
        printf("receive from '%s'\n", inet_ntoa(client.sin_addr));
        printf("receive data '%s'\n", recv_buf);
        
    }
    
    close(s);
    
    close(s0);
    
    
    return 0;
}
```

コマンドラインの解析も面倒なので受け入れポートは固定、一応各関数はif文で覆ってどんなエラーが発生するかを確認できるようにした最低限のコードです。

参考にしたのは、
書籍：[基礎からわかるTCP/IPネットワ-ク実験プログラミング: Linux/FreeBSD対応](https://amzn.asia/d/5MV06Cl)

書籍：[Linuxネットワークプログラミングバイブル](https://amzn.asia/d/dNvSv1e)

https://qiita.com/tajima_taso/items/2f0606db7764580cf295

https://bombrary.github.io/blog/posts/socket01-file-tcp/

https://chibash.github.io/lecture/web/web01.html

https://docs.oracle.com/cd/E19455-01/806-2730/6jbu725kj/index.html

https://learn.microsoft.com/ja-jp/windows/win32/winsock/windows-sockets-start-page-2

等の書籍、サイトです。ありがとうございます。

### socket() 関数
socketは新規に通信用のソケットを作り、それを識別するID番号（ディスクリプタ）を返す関数です。
どんなソケットを作るかを３つの引数で決定します。
TCP/IPなら、第１引数は **AF_INET** 、第２引数は **SOCK_STREAM** 、第3引数は **0** です。
ちなみに第１引数のAF_はアドレス・ファミリーの頭文字でWin32で使われます。同じ定数でもBSD系の場合はプロトコル・ファミリーなので、PF_で始まります。
4Dのプラグイン用ですので、Win用とMac用の２つのプラグインが必要なので、ここはソースの移植性重視でAF_の方を採用します。
作成に失敗した時は-1を返すのでif文でそれをトラップしてエラー処理をします。他の関数も戻り値が-1だとエラーなので同じ構文でエラー処理をしています。


この関数の詳細は以下のサイトで
　[socket - システムコールの説明](https://kazmax.zpp.jp/cmd/s/socket.2.html)
　[socket 関数 (winsock2.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-socket)
　[ちょくと：socket](https://chokuto.ifdef.jp/advanced/function/socket.html)

 ### bind() 関数
 bind関数はsocket関数で作ったソケットにIPアドレスとポートを結びつける関数です。
 第１引数には、先ほど作成したソケットのディスクリプタ
 第２引数はIPアドレスとポート情報を設定した`sockaddr_in`型の構造体。渡す時は`sockaddr`型ポインタにキャストして参照渡しします。
 第３引数は`sockaddr_in`構造体のサイズです。


この関数の詳細は以下のサイト
 　[bind - システムコールの説明](https://kazmax.zpp.jp/cmd/b/bind.2.html)
 　[bind 関数 (winsock.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/nf-winsock-bind)
　[ちょくと：bind](https://chokuto.ifdef.jp/advanced/function/bind.html)

 ### sockaddr_in構造体
 通信に使うアドレスやポートを指定するデータを納めた構造体です。プロトコルに応じて中の構造が変化する構造体ですのでCでは共用体とマクロ定義で宣言して使いやすい形になってます。
　　sockaddr　（元の型）
　　sockaddr_in　（IPv4）
　　sockaddr_in6　（IPv6）
今回はIPv4ですので、`sockaddr_in`構造体を使います。

**sin_familyフィールド** には`select()`関数のところでも出てきたアドレスファミリーを指定します。TCP/IPなので、 **AF_INET** を指定します。

**sin_addr.s_addrフィールド** にはIPアドレス。32bit(4byte)の数値をいれます。ここの入れる数値はネットワークバイトオーダー（ビッグエンディアン）になっている必要があるので、 **htonl()関数** でホストバイトオーダーからネットワークバイトオーダーに変換します。この関数を噛ませておけば、ホストコンピュータのバイトオーダーを気にすることがなくなりますので移植性が向上します。
`bind()`関数でのこのフィールドで指定するIPアドレスは、通信を受け入れるホストコンピュータのIPアドレスです。複数のNICがある時に特定のNICからの通信だけを受け入れる場合は、このフィールドでIPアドレスを指定します。
受け入れIPアドレスを指定しない場合は、 **INADDR_ANY** とします。

**sin_portフィールド** には通信を受け入れるポート番号を指定します。このフィールドもネットワークバイトオーダーですので、 `htons()`関数でホストバイトオーダーからネットワークバイトオーダーに変換します。

　[ちょくと：sockaddr](https://chokuto.ifdef.jp/advanced/struct/sockaddr.html)
　[ちょくと：htonl](https://chokuto.ifdef.jp/advanced/function/htonl.html)

 ### listen()関数
 ディスクリプタで指定したソケットへの接続の受け入れを開始します。
 第１引数にはsocket()関数で取得したディスクリプタ
 第２引数には接続要求を待機させるためのキューのサイズを渡します。キューを最大にするには **SOMAXCONN定数** をいれます。
 今回のテストプログラムではキューの挙動の検証のため **5** に設定してます。
 
 `listen()`を実行前は、クライアントからの接続要求は`connect()`関数実行時に **Connection refused** エラーを出して失敗します。
`listen()`関数は接続待機のためのキューを設定し、接続を待つことなく即座に戻りますが、ホストに対する接続は受け入れ状態になりクライアントからの接続が可能になります。
キューがいっぱいになるまで、クライアントからは、接続が確立し`send()`関数なども正常にデータを送信したように見えます。
キューがいっぱいになると、クライアントはキューが空くまで`connect()`関数が実行をブロックします。キューが空くとブロックが解除されて処理が再開します。（実際の実行結果では）
キューがいっぱいなのかどうか、キューに待機中の接続要求が存在するのかなどの情報を取得する方法はありません。

 　[listen - システムコールの説明](https://kazmax.zpp.jp/cmd/l/listen.2.html)
 　[listen 関数 (winsock2.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-listen)
 　[ちょくと：liten](https://chokuto.ifdef.jp/advanced/function/listen.html)

### accept()関数

第１引数のディスクリプタで指定されたソケットの接続待ちキューの中から最初の待機中の接続を取り出して接続を確立し、新しいソケットを作ってそれを返す関数です。
戻り値は、この通信のための新しいソケットのディスクリプタです。`socket()`関数で得たディスクリプタとは違う独立したソケットのディスクリプタなので、`socket()`関数でのソケットを閉じても、このディスクリプタ（ソケット）は有効で接続を維持します。
新しいソケットのためのポートは`bind()`で割当てたポートではなく、空いている別のポートが割り当てられます。そのため、`bind()`で割当てたポートはそのまま接続待ち状態を維持します。

第２引数は、接続してきたクライアントの情報（IPアドレス、ポート番号）を戻すためのバッファへのポインタを渡します。バッファの構造はbind()の時に解説した`sockaddr`構造体です。
第３引数は、このバッファのサイズなのですが、`bind()`とは違い、実際に接続してきたクライアントの状況によって実際のサイズが変わるため、参照渡しで設定します。正常に完了するとここに実際のサイズが戻ってきます。呼び出し時には、用意したバッファのサイズ（`sockaddr_in`のサイズ）を入力しておきます。

デフォルトでは、この関数は **ブロッキングモード** で動作するので、キューの中に待機している接続がない場合、クライアントが接続要求を出すまでプログラムの実行をブロックし続けます。

　[accept - システムコールの説明](https://kazmax.zpp.jp/cmd/a/accept.2.html)
　[accept 関数 (winsock2.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-accept)
　[ちょくと：accept](https://chokuto.ifdef.jp/advanced/function/accept.html)

### recv()関数

ソケットを通してデータを受信する関数です。
第１引数は受信するソケット（今回なら`accept()`で得たソケット）のディスクリプタ
第２引数は受信するバッファのポインタ
第３引数はそのバッファのサイズ（１回の呼び出しで受信する最大バイト数）
第４引数はフラグでrecv()関数の動作を修飾します。単純に受信するだけなら **0** を設定。
戻り値は実際に受信したデータのバイト数です。
`recv()`関数はデフォルトで**ブロッキングモード**で動作します。接続が維持されていてクライアントからデータが届かない限りプログラムの実行をブロックし続けます。
データが届くと戻り、実際にバッファに読み込んだバイト数を戻り値に戻します。
１回の実行で届いたデータをすべて読み込む保証はありません。必要なら数回実行しデータを読み込みます。
クライアントが`close()`等を実行して接続が切れると、戻り値が **0** で即座に戻ります。これを利用してデータの読み込み完了を判断しもよいのですが、通常はHTTPのようにプロトコルで通信の完了等を判断するようにします。

続く処理は、バッファの最後に`\0`を入れてC文字列にして、ターミナルに出力します。　ついでに、`accept()`関数で戻ってきた`sockaddr_in`構造体からリモートホストのIPアドレスを取得して出力してます。

　[recv - システムコールの説明](https://kazmax.zpp.jp/cmd/r/recv.2.html)
　[recv 関数 (winsock2.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-recv)
　[ちょくと：recv](https://chokuto.ifdef.jp/advanced/function/recv.html)
 
### close()関数

通信を切断しソケットを閉じ、関連するリソースを解放する関数です。
第１引数は閉じるソケットのディスクリプタです。

`socket()`関数で作ったソケットと`accept()`関数で得たソケットは別物ですのでそれぞれcloseが必要です。

　[close - システムコールの説明](https://kazmax.zpp.jp/cmd/c/close.2.html)

Winの場合、`closesocket()`関数を使うようですが、詳細はまだ調べてません。移植の際には要確認です。

　[ちょくと：closesocket](http://chokuto.ifdef.jp/advanced/function/closesocket.html)
　[closesocket 関数 (winsock2.h)](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-closesocket)

### 続く...