목표
- 다른 websocket server와 연결하기
- 이를 통해 메세지 주고 받기
아래 websocket 연결 및 메세지 코드를 추가하기 전 블로그 가독성을 위해 httpServer.js 수정 코드를 먼저 확인하고 가자. 또한 네트워크설정 - 인바운드규칙에 6001포트를 추가해 주었다.
// 해당 코드 추가해주기
import { getPeers, connectionToPeer, sendMessage } from "./p2pServer.js";
// initHttpServer 안에 추가
// 소켓연결 확인하기
app.get("/peers", (req, res) => {
res.send(getPeers());
});
// 웹소켓 연결 ip추가
app.post("/addPeer", (req, res) => {
res.send(connectionToPeer(req.body.data)); // 여기서 보낸 data가 newPeer로 가는거지 매개변수로
});
// 웹소켓 메세지
app.post("/sendMessage", (req, res) => {
res.send(sendMessage(req.body.data));
});
initConnection() 함수를 잘 봐야한다. initP2PServer() 와 connectionToPeer() 에서 각각 호출 되고 있다. 구조를 좀더 보면, 실행될때마다 매개변수로 받은 ws를 sockets라는 배열에 추가하고 있다. 따라서 /peers에서 내가 상대를 연결하거나, 상대가 나를 연결할때마다 쌓인 ws(ip주소)를 확인할 수 있다. 그럼으로 내가 서버인 동시에 클라이언트가 될 수 있다.
또한 실행될때마다 받게되는 ws를 매개변수로 갖는 initMessageHandler() 함수가 실행되는데 이는 아래 메세지부분에서 좀 더 자세히 다루도록 하자.
initP2PServer() 에서는 상대방이 네 ip주소로 연결했을때 connection 이벤트가 발생하며 호출되는 구조를 가지고 있다.
connectionToPeer() 에서는 /addpeer에서 post방식으로 받아온 데이터가 매개변수로 담겨오고, open 이벤트 발생시 해당 ip주소를 매개변수로 받은 initConnection() 이 실행된다.
// 소켓 확인 하는거 추가
const getPeers = () => {
return sockets
}
// 연결되는걸 확인하는거지
const initP2PServer = (p2pPort) => {
const server = new WebSocketServer({port:p2pPort});
// websocket 내에서 일어나는 event는 정의되어 있다. 그 이벤트가 발생했을때 우리는 어떤 함수를 실행(호출)할 것인지 정해주면 된다.
server.on('connection', (ws) => {
initConnection(ws)
console.log('똑똑. 누가 방문함')
})
console.log('listening P2PServer Port : ', p2pPort);
}
const initConnection = (ws) => {
sockets.push(ws)
initMessageHandler(ws) // 메세지 핸들러는 여기서 호출!
}
// 다른사람의 정보를 가지고 접속할 수 있는 환경
// 새로운 ip와 peer를 받아서 웹소켓에 넣어주고, open이 된다면 웹소켓에 추가해주기
const connectionToPeer = (newPeer) => {
const ws = new WebSocket(newPeer);
ws.on('open', () => { initConnection(ws); console.log('Connect peer : ', newPeer);})
ws.on('error', () => { console.log('fail to connection peer : ', newPeer);})
}
구조를 잘 봐야한다. 먼저 sendMessage() 함수가 실행된다. post방식으로 받아온 data와 sockets배열에 있는 모든 ip주소를 socket이라는 매개변수로 담아 write() 함수를 실행한다. 즉 앞서 initConnection() 함수를 실행시키며 sockets에 들어간 모든 주소값에 이 함수가 실행된 것이다. 이때 받아온 JSON형태의 data를 string화 해주고 send라는 이벤트를 발생시킨다.
여기서 중요한게 앞서 말한 initConnection() 함수이다. 위에서 언급했듯 내가 상대방과 연결하거나, 상대방이 나한테 연결할때마다 실행되면서 그 안에서 ip주소값을 매개변수로 받은 initMessageHandler() 함수가 실행되었다.(실행되어져 있는 상태). 이 상태에서 send 이벤트가 트리거가 되어 initMessageHandler() 안에 messge 이벤트가 발생하는 구조이다. 이때 string화한 data를 다시 JSON형태로 바꿔주고, switch문 안에서 data의 type이 앞서 정의한 MessageType.SENT_MESSAGE와 같을경우 data의 message를 콘솔창에 찍어준다.
// 메세지 타입
const MessageType = {
RESPONCE_MESSAGE : 0, // 메세지 받을때
SENT_MESSAGE : 1 // 메세지 보낼때
// 최신 블록 요청
// 모든 블록 요청
// 블록 전달
}
// initConnection이 일어날때 등록? 매개변수로 들어갔던 ws주소
const initMessageHandler = (ws) => {
ws.on('message', (data) => { // 메세지가 있으면 data가 있다 / 저 message란 이벤트가 발동되면 밑에 함수를 발동해라
const message = JSON.parse(data) // 그 데이터를 JSON 형태로 바꿔주기
switch(message.type)
{
// case MessageType.RESPONCE_MESSAGE: // 메시지 받았을 때
// break;
case MessageType.SENT_MESSAGE: // 메시지 보낼 때
console.log(message.message);
//write(ws, message); // 보내는 함수는 여기서 호출!
break;
// 다른사람이 보낼때 SEND_message로 보내고있고
// 나도 받을때 SEND_MESSAGE로 받고 있다 그래서 사실상 위에 RESPONCE가 없다
// 다른사람이 본내거를 보는거라서 send_message로 되어있음. 헷갈릴수 있으니 주의
}
})
}
const write = (ws, message) => {
console.log('write()', message)
ws.send(JSON.stringify(message)) // send함수를 사용하여 보내기 / JSON형태를 문자열로 stringfy
} // 뒤 메세지를 앞 웹소켓에 보낸다
// 상대방 ws send시 이벤트발생
const sendMessage = (message) => {
sockets.forEach( (socket) => { // sockets에서 하나씩 돌아가는게 socket 헷갈리지 말자
write(socket, message); // 내가연결하고 있는 모든 소켓에게 write함수 실행 (broadcast)
});
}
// 다른 노드와 통신을 위한 서버 // 원장공유
// peer to peer / node 대 node / 개인과 개인
// 서로 필요한 정보들을 서로서로 공유하는 탈중앙화 시스템
import WebSocket from 'ws';
import { WebSocketServer } from 'ws'
// 메세지 타입
const MessageType = {
RESPONCE_MESSAGE : 0, // 메세지 받을때
SENT_MESSAGE : 1 // 메세지 보낼때
}
// 아래 매개변수 ws가 계속 늘어나기 때문에 저장해줄 자료구조를 만들어보자
// 지금 sockets가 가르키는건 빈 배열이 담긴 주소값. 따라서 안에 data에 push로 추가되고 바뀌는건 크게 상관이 없다
const sockets = [];
// 소켓 확인 하는거 추가
const getPeers = () => {
return sockets
}
// 연결되는걸 확인하는거지
const initP2PServer = (p2pPort) => {
const server = new WebSocketServer({port:p2pPort});
// websocket 내에서 일어나는 event는 정의되어 있다. 그 이벤트가 발생했을때 우리는 어떤 함수를 실행(호출)할 것인지 정해주면 된다.
server.on('connection', (ws) => {
initConnection(ws) // 만들어야할 함수
console.log('똑똑. 누가 방문함')
})
console.log('listening P2PServer Port : ', p2pPort);
}
const initConnection = (ws) => {
sockets.push(ws)
initMessageHandler(ws) // 메세지 핸들러는 여기서 호출!
}
// 다른사람의 정보를 가지고 접속할 수 있는 환경
// 새로운 ip와 peer를 받아서 웹소켓에 넣어주고, open이 된다면 웹소켓에 추가해주기
const connectionToPeer = (newPeer) => {
const ws = new WebSocket(newPeer);
ws.on('open', () => { initConnection(ws); console.log('Connect peer : ', newPeer);})
ws.on('error', () => { console.log('fail to connection peer : ', newPeer);})
}
// initConnection이 일어날때 등록? 매개변수로 들어갔던 ws주소
const initMessageHandler = (ws) => {
ws.on('message', (data) => { // 메세지가 있으면 data가 있다 / 저 message란 이벤트가 발동되면 밑에 함수를 발동해라
const message = JSON.parse(data) // 그 데이터를 JSON 형태로 바꿔주기
switch(message.type)
{
// case MessageType.RESPONCE_MESSAGE: // 메시지 받았을 때
// break;
case MessageType.SENT_MESSAGE: // 메시지 보낼 때
console.log(message.message);
//write(ws, message); // 보내는 함수는 여기서 호출!
break;
// 다른사람이 보낼때 SEND_message로 보내고있고
// 나도 받을때 SEND_MESSAGE로 받고 있다 그래서 사실상 위에 RESPONCE가 없다
// 다른사람이 본내거를 보는거라서 send_message로 되어있음. 헷갈릴수 있으니 주의
}
})
}
const write = (ws, message) => {
console.log('write()', message)
ws.send(JSON.stringify(message)) // send함수를 사용하여 보내기 / JSON형태를 문자열로 stringfy
} // 뒤 메세지를 앞 웹소켓에 보낸다
// 상대방 ws send시 이벤트발생
const sendMessage = (message) => {
sockets.forEach( (socket) => { // sockets에서 하나씩 돌아가는게 socket 헷갈리지 말자
write(socket, message); // 내가연결하고 있는 모든 소켓에게 write함수 실행 (broadcast)
});
}
export { initP2PServer, connectionToPeer, getPeers, sendMessage }