웹은 기본적으로 요청을 하고 응답을 받고 끊게 되어 있음.
But, 실시간 메시지는 연결을 끊지 않고 유지하는 것을 기본으로 함.
웹 서버는 연결 유지가 잘 안된다는 문제가 생김.
So, 웹 서버를 이용하지 않고 Soket서버를 사용(Websocket프로토콜). Soket.IO는 웹 Soket을 편리하게 사용할 수 있게 만들어준 라이브러리. Node.js기반이고 멀티 디바이스(web,android,ios,windows)를 지원하며 websocket을 지원하지 않는 browser도 지원함.
% npm install socket.io --save
% npm install cors --save
CORS도 사용해야 함. --> 다른 웹서버도 접근할 수 있게 해줌.
두 개다 외부 모듈이라 이걸 사용해줄 수 있게 코드 작성.
// socket.io모듈 불러들이기
var socketio = require('socket.io');
// cors사용 - 클라이언트에서 ajax로 요청하면 CORS지원
var cors = require('cors');
...
//cors를 미들웨어로 사용하도록 등록
app.use(cors());
...
//socket.io 서버 시작
var io = socketio.listen(server);
//웹 서버 위에서 websocket으로 들어오는 요청을 받아서 처리할 수 있는 준비가 됨
console.log('socket.io 요청을 받아들일 준비가 되었습니다.');
[클라이언트] ---웹소켓 요청---> 웹서버(3000port)
웹 브라우저의 클라이언트가 웹소켓을 통해서 요청을 보내면 웹 서버 위에서 똑같은 3000번 port라 하더라도 프로토콜이 다름(왔다 갔다 하는 데이터 형식). 데이터를 받아서 Socket.io모듈에서 처리해줄 준비가 됨(이게 listen()메소드, attach메소드도 있음)
socket.io는 이벤트기반으로 처리함
클라이언트가 서버로 보낼 때 : init
클라이언트가 보내온 이벤트를 받을 때 on이 됨
io.sockets.on('connection', function(socket){
console.log('connection info -> ' + socket.request.connection._peername);
socket.remoteAddress = socket.request.connection._peername.address;
socket.remotePort = socket.request.connection_peername.port;
});
socket이 on이라는 메소드를 가지고 있음
connection이라는 이벤트를 가짐. 함수가 socket이라는 파라미터를 가짐
클라이언트가 어떤 IP주소와 어떤 PORT에서 접속했는지 알고 싶을 때 사용하기 위해 socket객체에 속성을 추가해둠 (socket.remoteAddress, socket.remotePort)
연결을 맺어줄려면 접속이 가능한 클라이언트가 필요 → html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>채팅 클라이언트 01</title>
<script src="./jquery-3.1.1.min.js"></script>
<script src="socket.io.js"></script>
<script>
$(function(){
});
function connect(){
var host = $('#hostInput').val();
var port = $('#portInput').val();
connectToServer(host, port);
}
function connectToServer(host, port){
var url = 'http://' + host + ':' + port;
var options = {
forceNew:true
};
var socket = io.connect(url, options);
socket.on('connect', function(){
println('웹소켓 서버에 연결됨 -> ' + url);
});
socket.on('disconnect', function(){
println('웹소켓 연결 종료됨.');
});
}
function println(data){
console.log(data);
$('#results').append('<p>' + data + '</p>');
}
</script>
</head>
<body>
<h3>채팅클라이언트 01</h3>
<br>
<div>
<input type="text" id="hostInput" value="localhost">
<input type="text" id="portInput" value="3000">
<input type="button" id="connectButton" value="연결하기" onclick="connect()">
</div>
<hr>
<p>결과</p>
<div id="results"></div>
</body>
</html>
서버와 연결이 되었음을 확인.
▼▽구조▽▼
emit()으로 보내고 on()으로 받음
// 'message' 이벤트를 받았을 때의 처리
socket.on('message', function(message) {
console.log('message 이벤트를 받았습니다.' + JSON.stringify(message));
if(message.recepient =='ALL') {
// 나를 포함한 모든 클라이언트에게 메시지 전달
console.log('나를 포함한 모든 클라이언트에게 message 이벤트를 전송합니다.')
io.sockets.emit('message', message);
}
});
io.sockets.emit() 은 나를 포함한 모든 클라이언트에게 전송하고,
socket.broadcast.emit()은 나를 제외한 모든 클라이언트에게 전송함.
진행단계
내가 지정한 사람한테 메세지를 보내는 것 = 메시지를 보낼 때 부터 받는 사람을 지정
→ 받는 사람의 ID가 필요
→ 로그인 기능 추가
로그인 기능 추가하기
// 'login' 이벤트를 받았을 때의 처리
socket.on('login', function(input) {
console.log('login 이벤트를 받았습니다.' + JSON.stringify(input));
// 기존 클라이언트 ID가 없으면 클라이언트 ID를 맵에 추가
login_ids[input.id] = socket.id;
socket.login_id = input.id;
// 응답 메시지 전송
sendResponse(socket, 'login', '200', '로그인되었습니다.');
});
일대일 채팅 대상에게 메시지 전달
// 'message' 이벤트를 받았을 때의 처리
socket.on('message', function(message) {
console.log('message 이벤트를 받았습니다.' + JSON.stringify(message));
if (message.recepient =='ALL') {
// 나를 포함한 모든 클라이언트에게 메시지 전달
console.log('나를 포함한 모든 클라이언트에게 message 이벤트를 전송합니다.')
io.sockets.emit('message', message);
} else {
// 일대일 채팅 대상에게 메시지 전달
if (login_ids[message.recepient]) {
io.sockets.connected[login_ids[message.recepient]].emit('message', message);
// 응답 메시지 전송
sendResponse(socket, 'message', '200', '메시지를 전송했습니다.');
} else {
// 응답 메시지 전송
sendResponse(socket, 'message', '404', '상대방의 로그인 ID를 찾을 수 없습니다.');
}
}
});
결과창