TypeScript Sampleをやってみるその８


## はじめに
[tcp](https://ja.wikipedia.org/wiki/Transmission_Control_Protocol)と[http](https://ja.wikipedia.org/wiki/Hypertext_Transfer_Protocol)の簡易サーバのサンプルの備忘録を投稿します。tcpサーバは少し手を加えて、tcpクライアントからの入力をサーバに送信します。Webサーバの基本の作りを体験してみましょう。


## 前提
* [Visual Studio Code](https://code.visualstudio.com/download)がインストール済み
* [Node.js/npm](https://qiita.com/taiponrock/items/9001ae194571feb63a5e)がインストール済み
* gitから[Sampleをダウンロードして](https://github.com/Microsoft/TypeScriptSamples/archive/master.zip)任意のディレクトリに解凍済み
* OSはWindows7/10を想定

## サンプル node
tcpサーバとhttpサーバのサンプル

## ファイル構成
|パス|ファイル名|説明|
|:--|:--|:--| 
|.|HttpServer.js|HttpServer.tsのコンパイルにて作成されたJavaScriptファイル|
|.|HttpServer.js.map|HttpServer.tsのコンパイルにて作成されたmapファイル|
|.|HttpServer.ts|httpサーバサンプルTypeScriptファイル|
|.|node.d.ts.bk|node.d.tsファイルをリネーム|
|.|package-lock.json|パッケージ依存関係ロックファイル(json)|
|.|README.md|このサンプルの説明と使用方法|
|.|TcpClient.js|TcpClient.tsのコンパイルにて作成されたJavaScriptファイル|
|.|TcpClient.js.map|TcpClient.tsのコンパイルにて作成されたmapファイル|
|.|tcpClient.ts|TcpクライアントサンプルTypeScriptファイル|
|.|TcpServer.js|TcpServer.tsのコンパイルにて作成されたJavaScriptファイル|
|.|TcpServer.js.map|TcpServer.tsのコンパイルにて作成されたmapファイル|
|.|tcpServer.ts|TcpサーバサンプルTypeScriptファイル|
|.|tsconfig.json|TypeScriptのコンパイルコンフィグファイル|

## Tcpサーバ
### tcpServer.ts 改修
#### 元ソース

``` TypeScript
///<reference path='node.d.ts'/>
import net = require('net');

var server = net.createServer(function (socket) {
    socket.write('Echo server\r\n');
    socket.pipe(socket);
});

server.listen(1337, '127.0.0.1');
```
#### 改修ソース

``` TypeScript
import net = require('net');

var server = net.createServer(function(socket) {
	// クライアント接続
    console.log('Connected : ' + socket.remoteAddress +':'+ socket.remotePort);
    // データ受信
    socket.on('data', function(data) {
        console.log('Recive Data : ' + data );
        socket.write('Server Recived');
    });
    // close受信
    socket.on('close', function(had_error) {
        console.log('Closed : ' + had_error + '\r\n');
    });
    // error受信
    socket.on('error', function(err) {
        console.log('Error : ' + err.stack + '\r\n');
    });
});

server.listen(1337, '127.0.0.1');
console.log('Server listen :' + '127.0.0.1' +':'+ '1337');
```
>
[ 修正内容 ]
* socket.pipeをsocket.onに変更
* クライアントからのdata、close、errorイベントに対応するコーディング追加
* consoleを追加。コンソールに受信内容を出力
>

### TcpClient.ts 新規追加

``` TypeScript
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 1337;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
    console.log('Connected : ' + HOST + ':' + PORT);
    console.log(process.argv[2]);
    client.write(process.argv[2]);
});
client.on('data', function(data) {
    console.log('Send Data : ' + data);
    client.destroy();
});
client.on('close', function() {
    console.log('Connection closed');
});
```
>
[ 仕様 ]
* 起動引数を送信する。
* Connectイベントで起動引数を送信バッファに書き込む
* dataイベント後、クライアントインスタンス破棄
* closeイベント
>

### シーケンス図
![TS_034.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/a09aa51a-e323-79d3-6939-e6000f663e5d.jpeg)

### 実行
tcpサーバ
ソース改修でコンパイルエラーが出力されます。
TypeScriptの参考が少ないため悪戦苦闘が始まる所以です。

```
識別子 'WritableStream' が重複しています。
識別子 'ReadableStream' が重複しています。
・・・・
```
>
node.d.tsで上記のエラーが検知されます。
これは、node.jsのライブラリーes5/es6のどちらかを選択することで解決できます。
node.d.tsを使用しないようにするため、名前を変更します。node.d.ts.bkなどへ。
`識別子　○○が重複しています。`はコンパイルオプションに `--lib es5` を追加します。
コンパイル結果、以下のエラーメッセージが出力されます。
>

```
TcpServer.ts(1,22): error TS2307: Cannot find module 'net'.
TcpServer.ts(5,5): error TS2304: Cannot find name 'console'.
TcpServer.ts(8,9): error TS2304: Cannot find name 'console'.
TcpServer.ts(13,9): error TS2304: Cannot find name 'console'.
TcpServer.ts(17,9): error TS2304: Cannot find name 'console'.
TcpServer.ts(22,1): error TS2304: Cannot find name 'console'.
```
>
node.jsのライブラリを指定しているのになんで？と思うでしょう。
netが出力されたのは、node.d.tsのファイル名を変更したため、netの定義が見つからないエラー。
consoleはそもそもnode.d.tsに定義がないので、いずれにしてもエラーになります。
`Cannot find name ○○`は@Typesの定義をインストールで解決できます。
この場合は@Types/nodeをインストールします。
これらを加味するとTcpServerのコンパイルから実行手順は以下のようになります。

```
cd .node
npm install @types/node
tsc --sourcemap --lib es5 TcpServer.ts
node TcpServer.js
```

#### tcpクライアント
"Hello world!"を送信してみます。
@Types/nodeはインストール済みなので、--lib es5をコンパイルオプションに追加します。

```
cd .node
tsc --sourcemap --lib es5 TcpClient.ts
node TcpClient.js "Hello world"
```

### 結果
#### Tcpサーバ起動
![TS_035.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/7c23650d-6778-915a-cefc-901828d125e9.jpeg)

#### Tcpクライアント"Hello world"送信
![TS_036.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/3b009021-2bc9-8947-24a9-4b61b0cd140a.jpeg)

#### Tcpサーバの受信
![TS_037.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/0a02acd5-2227-8525-27fa-4bf180ef7324.jpeg)

## Httpサーバ
### HttpServer.ts改修
node.d.tsの名前を変更したためコンパイルエラーが出力されます。
![TS_038.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/b8621c53-b39c-287b-dd60-ed3632a52ea0.jpeg)
@Types/nodeのインストールにより、ライブラリ定義済みのため、httpServer.tsの1行目のnode.d.tsの行をコメントします。

``` TypeScript
/* ///<reference path='node.d.ts'/> */
import http = require("http");

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');
```
### 実行

```
tsc --sourcemap HttpServer.ts
node HttpServer.js
```
ブラウザでURL ` http:/127.0.0.1:1337/ ` を指定します。

### 結果
#### HttpServer起動
![TS_040.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/7c270784-982a-b9b3-b380-22e1d6dce904.jpeg)
#### ブラウザ出力
![TS_041.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/272b73f3-0142-d91d-2ed6-0244873e9728.jpeg)


## ポイント
Tcpクライアントを追加してTcpサーバとの通信を試してみました。
元のサンプルと対向のやりとりができるように改修しました。
ローカルホスト(127.0.0.1)を指定していますが、これがグローバルなアドレスであれば、地球の裏側からでも送受信できます。Tcp通信のすごい機能はここにあります。
ちなみにTelnetからでもTcpサーバに接続できます。

{ Telnet接続 ]
Tcpサーバ起動
![TS_042.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/2c9f60b5-6cdc-4123-dd63-bf01147a04dc.jpeg)
ターミナルにエコーバックしませんが、キーボードから"Hello world"をタイプして<kbd>Enter</kbd>
![TS_043.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/53ff2877-3836-dd93-b110-5ac16d232e5a.jpeg)
Tcpサーバのターミナルログ結果
![TS_044.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/d2ce9b88-9d98-c5ae-08f0-5eb324274c7e.jpeg)

## ノート
TypeScriptのコンパイルエラーパターン２つ。

* 重複エラー(Duplicate Error)　→ tscに --lib es5 または --lib es6 を指定
* 未定義エラー(Cannot find module) → npm install @Types/必要な定義 

## まとめ
アプリ開発で通信プログラムのサーバプログラムを作ることは稀です。
どちらかというとクライアント側のアプリを作ることになると思います。
通信の基本は人間の会話と同じでサーバとクライアントのキャッチボールで成り立っています。
シーケンス図にあるとおり、登場人物（アクター）はサーバとクライアントです。

以上、おそまつ