超簡易的なチャットアプリを作ってWebSocketについての理解を深める

この記事では、HTTPについて触れながらWebSocketの使用についてチャットアプリを作って解説したいと思います。

# Websocketについて
Websocketの前にまず、HTTPについて触れたいと思います。
### HTTPとは
ハイパーテキスト転送プロトコル（HTTP）は、ウェブ上でのデータ転送を初めて可能にした一連のルールと情報システムです。クライアントとサーバーの間でリクエストとレスポンスのサイクルが行われます。
流れとしてはまず、リクエストは常にクライアントによって開始されます。
次に、クライアントからのリクエスト一回につき必ずサーバーから1回だけ応答があります。その後、クライアントとサーバーの間の接続は閉じられます。
なので、たとえばもしユーザーがチャットアプリで同じ相手に100通のメッセージを送ろうとした場合、HTTP1.0を使用していれば、100回のリクエストとレスポンスが繰り返されることになります。100通のメッセージを受け取った人は、ページを更新して、メッセージ来たかどうかを確認しなければなりません。サーバーとクライアントの間の接続は、リクエストとレスポンスのサイクルが終わるたびに失われるため、サーバーは通常、以前のリクエストやクライアントが見ているものの状態に関する情報を保持しません。したがって、HTTP はステートレスプロトコルとして知られています。しかし、チャットアプリのようにデータのやり取りをシームレスに行いたい場合が多々あります。そこで、登場したのが`WebSockets`です!

![Screen Shot 2022-12-19 at 12.43.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321545/05ec1e43-8d81-818f-0049-db3853f7babf.png)

上の図の通り、HTTPではサーバーがクライアントのリクエストに応答した後に接続を閉じていましたが、WebSocketはクライアントとサーバーの間の接続を開いたままにすることを可能にします。

## HTTP1.1とWebSocketのコネクション確立方法について
HTTP1.1では、WebSocket はクライアントとサーバー間のハンドシェイクによって確立され、両者は使用するバージョン、サブプロトコル、および拡張機能などに合意します。ハンドシェイクでは、HTTP キャッシュを回避するために、接続に固有のキーも交換されます。
このハンドシェイクの後に、クライアントからWebSocketに切り替えるためのUpgradeリクエスト(HTTP GETリクエスト)が送られ、これに対してサーバーから101ステータスとSwitching Protocolsというレスポンスを返します。すると、接続はWebSocketに切り替わります。


![Flow-of-WebSocket-protocol-Firstly-TCP-connection-is-established-through-the-three-way.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321545/822c8514-f9b9-a27d-5588-1e642a9e059a.png)


## 簡易的なチャットアプリを作ってみる
実際に手を動かして理解を深めていきましょう。
### 適当なディレクトリー下で`package.json`を作成
```
npm init -y
```

### index.jsを作成
以下のコードは、WebSocketServerを立てて、クライアントに対して5秒おきに`Hello, this is a message from the server`というメッセージを送る簡単なscriptになります。
```js
const http = require("http");
const WebSocketServer = require("websocket").server
let connection = null;

const httpserver = http.createServer((req, res) =>
                console.log("we have received a request"))


const websocket = new WebSocketServer({
    "httpServer": httpserver
})

httpserver.listen(8080, () => console.log("My server is listening on port 8080"))

websocket.on("request", request=> {
    connection = request.accept(null, request.origin)
    connection.on("message", message => {
        console.log(`Received message ${message.utf8Data}`)
    })

    sendevery5seconds();
})

function sendevery5seconds(){
    connection.send("Hello, this is a message from the server");
    setTimeout(sendevery5seconds, 5000);
}
```

### websocketをインストール

```
npm install websocket
```

### サーバーを起動
```
node index.js
```
以下のように標準出力されていれば接続完了です
```
My server is listening on port 8080
```

### WebSocketのテストクライアント
[こちら](https://chrome.google.com/webstore/detail/websocket-test-client/fgponpodhbmadfljofbimhhlengambbn?hl=en)のchrome拡張を入れて実際にメッセージのやり取りをしてみます。

URL入力欄に`ws://localhost:8080`を入れてopenをクリックすると接続が開始されます。

![Screen Shot 2022-12-20 at 13.56.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321545/92c512cf-e46b-8a0e-8ebe-b405fca73468.png)

試しに以下のようにメッセージを送ってみます。
![Screen Shot 2022-12-20 at 14.49.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321545/1ae17a1a-4121-2bed-22cc-ea32004f7e39.png)

ターミナルからクライアントからのメッセージが出力されているはずです
```
My server is listening on port 8080
Received message hey!
Received message hey2!
```


## まとめ
いままでフワッとしか理解できていなかったWebSocketについて、実際にコードを書いて試してみることでより深く学ぶことができました。
ドキュメントを読むだけだとなかなか理解できないので、興味があればぜひ試してみてください👏