[Blockchain] JS로 Blockchain 구현하기 (5)

Jaewonee·2022년 5월 1일

목표

  • 다른 websocket server와 연결하기
  • 이를 통해 메세지 주고 받기

httpServer.js 수정

아래 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));
  });



websocket 연결하기

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 }
profile
🙋‍♂️블록체인 개발자 되기 / 📑 공부기록 공간

0개의 댓글