WebSocket とは
- クライアントとリモートホストの間で双方向通信を可能とするプロトコル。
- セキュリティモデルはウェブブラウザでよく用いられるオリジンをベースとしたモデル。
-
XMLHttpRequest
やロングポーリングに頼らずにサーバとの双方向通信を必要とするブラウザアプリケーションのためのメカニズムを提供する。 - クライアント・サーバ間の双方向のトラフィックのためにTCPコネクションを一つだけ張る。
プロトコルの概要
プロトコルはハンドシェイクとデータ転送の2つの部分からなる。
ハンドシェイク
クライアント -> サーバ
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
サーバ -> クライアント
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
ハンドシェイクが成功すると、クライアントとサーバは1つ以上のフレームからなるメッセージをお互いに送出し合うことになる。
Node.js でシンプルな WebSocket 通信
Node.js では、ws というWebSocketライブラリを用いてシンプルな WebSocket 通信ができる(Socket.IO でも可能)。
サーバ側のコード
const WebSocket = require('ws')
const server = new WebSocket.Server({ port: 3000 })
server.on('connection', ws => {
ws.send('connected!')
ws.on('message', message => {
console.log('received: %s', message)
})
})
クライアント側のコード
const ws = require('ws')
const client = new ws('ws://localhost:3000')
client.on('open', () => {
client.send('hello')
})
client.on('message', message => {
console.log(message)
})
node server.js
でWebSocketサーバを立ち上げた後 node client.js
を実行すると、サーバ側では
received: hello
と出力され、クライアント側では
connected!
と出力される。
WebSocket でチャットアプリを作る
blessed というクールな CUI を作れるライブラリを使って、超シンプルなチャットアプリ(もどき)を作ってみる。
サーバ側
const WebSocket = require('ws')
const server = new WebSocket.Server({ port: 3000 })
const sockets = new Map()
const broadcast = message => {
console.log(message)
server.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(message)
}
})
}
server.on('connection', socket => {
socket.on('message', message => {
if (sockets.has(socket)) {
const name = sockets.get(socket)
broadcast(`${name}: ${message}`)
} else {
sockets.set(socket, message)
broadcast(`${message} entered the room.`)
}
})
socket.on('close', () => {
const name = sockets.get(socket)
if (name) {
sockets.delete(socket)
broadcast(`${name} left the room.`)
}
})
})
クライアント側
const ws = require('ws')
const blessed = require('blessed')
const screen = blessed.screen({ smartCSR: true })
screen.title = 'chat'
const chatList = blessed.list({ border: { type: 'line' } })
const textbox = blessed.textbox({
height: '10%',
bottom: 0,
input: true,
inputOnFocus: true,
border: {
type: 'line'
}
})
const client = new ws('ws://localhost:3000')
client.on('open', async () => {
textbox.setText('(Please enter your name)')
textbox.focus()
screen.render()
})
client.on('message', message => {
chatList.insertLine(0, message)
screen.render()
})
textbox.key(['enter'], () => {
client.send(textbox.getText())
textbox.clearValue()
chatList.render()
textbox.focus()
})
const EXIT_COMMANDS = ['escape', 'C-c']
const exit = () => process.exit(0)
screen.key(EXIT_COMMANDS, exit)
chatList.key(EXIT_COMMANDS, exit)
textbox.key(EXIT_COMMANDS, exit)
screen.append(chatList)
screen.append(textbox)
screen.render()
実際の動作
それっぽくは動く。
所感
- blessed の API がよくわからない
- ブラウザを UI としたチャットアプリを作ってみたい
- 随時更新する