WebSocket을 파헤치기 전에 먼저 HTTP 프로토콜에 대해 알아보자!
HTTP 프로토콜은 단방향 통신 프로토콜입니다.
클라이언트의 요청이 있을 때 서버가 응답하는 방식을 의미합니다.
HTTP polling
HTTP streaming
server-sent events
소켓 통신이란 서버와 클라이언트 양방향 연결이 이루어지는 통신으로, 클라이언트와 서버 양쪽에서 서로에게 데이터 전달을 하는 방식의 양방향 통신이 가능합니다.
양방햔 통신을 하기위해 웹소켓 기술을 활용하는 라이브러리입니다.
Guillermo Rauch가 만든 Socket.io는 WebSocket, FlashSocket, AJAX Long Polling, AJAX Multi part Streaming, IFrame, JSONP Polling을 하나의 API로 추상화한 것이다.
설치
npm init
npm install --save websocket http
// webSocket 서버 포트를 8000번으로 정의하고 webSocket과 HTTP를 가져온다.
const webSocketsServerPort = 8000;
const webSocketServer = require('websocket').server;
const http = require('http');
// HTTP모듈을 사용하여 HTTP 서버를 만든다.
// 이 작업이 완료되면 포트 8000번을 수신하도록 한다.
const server = http.createServer();
server.listen(webSocketsServerPort);
console.log('listening on port 8000');
// 방금 생성한 HTTP 서버의 인스턴스를 사용하여 WebSocket 서버를 생성한다.
const wsServer = new webSocketServer({
httpServer: server
});
// 클라이언트 객체를 생성한다
const clients = {};
// This code generates unique userid for everyuser.
const getUniqueID = () => {
const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
return s4() + s4() + '-' + s4();
};
//여기에 연결된 모든 클라리언트를 우리가 정의해야하는 요청 메서드에 저장할 것이다.
//서버가 여기에서 요청을 받으면 발생합니다.
wsServer.on('request', function (request) {
var userID = getUniqueID();
console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');
// You can rewrite this part of the code to accept only the requests from allowed origin
const connection = request.accept(null, request.origin);
clients[userID] = connection;
console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients));
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ', message.utf8Data);
// broadcasting message to all connected clients
for(let key in clients) {
clients[key].sendUTF(message.utf8Data);
console.log('sent Message to: ', clients[key]);
}
}
})
});
설치
npm init
npm install --save websocket react react-dom react-script antd
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="chat app created using websockets" />
<title>Socket chat</title>
</head>
<body>
<div id="root"></div>
</body>
import ReactDOM from 'react-dom';
import React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";
import { Card, Avatar, Input, Typography } from 'antd';
import 'antd/dist/antd.css';
import './index.css'
const { Search } = Input;
const { Text } = Typography;
const { Meta } = Card;
const client = new W3CWebSocket('ws://127.0.0.1:8000');
export default class App extends Component {
state ={
userName: '',
isLoggedIn: false,
messages: []
}
onButtonClicked = (value) => {
client.send(JSON.stringify({
type: "message",
msg: value,
user: this.state.userName
}));
this.setState({ searchVal: '' })
}
componentDidMount() {
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage = (message) => {
const dataFromServer = JSON.parse(message.data);
console.log('got reply! ', dataFromServer);
if (dataFromServer.type === "message") {
this.setState((state) =>
({
messages: [...state.messages,
{
msg: dataFromServer.msg,
user: dataFromServer.user
}]
})
);
}
};
}
render() {
return (
<div className="main" id='wrapper'>
{this.state.isLoggedIn ?
<div>
<div className="title">
<Text id="main-heading" type="secondary" style={{ fontSize: '36px' }}>Websocket Chat: {this.state.userName}</Text>
</div>
<div style={{ display: 'flex', flexDirection: 'column', paddingBottom: 50 }} id="messages">
{this.state.messages.map(message =>
<Card key={message.msg} style={{ width: 300, margin: '16px 4px 0 4px', alignSelf: this.state.userName === message.user ? 'flex-end' : 'flex-start' }} loading={false}>
<Meta
avatar={
<Avatar style={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>{message.user[0].toUpperCase()}</Avatar>
}
title={message.user+":"}
description={message.msg}
/>
</Card>
)}
</div>
<div className="bottom">
<Search
placeholder="input message and send"
enterButton="Send"
value={this.state.searchVal}
size="large"
onChange={(e) => this.setState({ searchVal: e.target.value })}
onSearch={value => this.onButtonClicked(value)}
/>
</div>
</div>
:
<div style={{ padding: '200px 40px' }}>
<Search
placeholder="Enter Username"
enterButton="Login"
size="large"
onSearch={value => this.setState({ isLoggedIn: true, userName: value })}
/>
</div>
}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
body {
background-color: #bae7ff;
}
.main {
height: 100vh;
width: 100vw;
background-color: #bae7ff;
}
.title {
text-align: center;
width: 100%;
padding: 2%;
background-color: #91d5ff;
color: #efefef;
}
.bottom {
position: fixed;
width: 100%;
left: 0;
bottom: 0;
}