dukDukz

[블록체인] network.js 상세설명 본문

웹 개발/블록체인

[블록체인] network.js 상세설명

헤일리_HJ 2021. 9. 7. 15:09

[network.js]
ws.on 얘도 비동기이다.

 

여기서도 이벤트 종류가 몇개 있을 것이다.
흔히 자주 쓰는 메서드가 무엇인지...

 

websocket 의 on 이라는 매서드를 실행했을때..
얘도 비동기로 실행되는 것이다.

[websocket 의 이벤트 종류]

open
connection
error 
close
message 

*message 가 가장 중요하다
- 얘도 비동기로 처리가 된다.
이 인자값(data)은 누가 주는걸까?

ws.on("message", data => {}
어케 가져왔지??
data라는 변수는 on 이라는 애가 만들어준것이다.


이제 그러면 network.js 를 쓰면서 이해해 보도록 하자!

가장 핵심적인 녀석이 ws

Websocket 의 사용방법도 알아야 한다.

npm socket 을 쳐서 내용을 봐보자

const WebSocket = require('ws')

cosnt ws = new Websocket('ws:localhost:3000')


new를 붙였으니까 ws 는 어떤 내용을 담고 있는 객체가 된다.


http 프로토콜

잠깐!
http://     localhost:3000
ws://   localhost:3000
        IP : Port

이 두개는 다르다

[ ] : // IP : Port
ㄴ 얘는 뭘까?

바로 프로토콜이다.
- 데이터 통신 방법이 어떤거냐를 묻는거임

http 는 우리가 요청을 보내면 문서가 하나 생긴다 request msg 
그 문서의 형태
--------------
start line      
header

body
-------------


http 는 항상 이렇게 텍스트를 만들어서 보내줘야해


ws 프로토콜

그러면 ws 는 뭘까?

websocket 이라는게 생긴지는 10년정도 밖에 안되었다,
브라우저가 발달이 되면서 나온것
그래서 이 프로토콜은 생긴지 얼마 안된것

http 문서의 형태와는 전혀 다르다.

cosnt ws = new Websocket('ws:localhost:3000')

 

얘가 하는 역할은 상대방 주소..를 적는 것이다.

즉 websocket은 서버와 클라간에 소통이다.

우리가 서버 한대만 있으면 연결할 사람이 없다
서버 두대가 있어야 websocket 을 실행할 수 있다,
하나가 클라 역할을 해줘야 한다는 뜻

그래서 서버 기준으로 상대방의 주소(클라의 주소)를 적어야 한다


ws.on('open')
서로 연결이 되어서 클라 쪽에서 실행되는 코드
오픈이 되면 실행이 된다.

ws.on('message')
어떤 데이터를 받았을 때 실행이 되는 이벤트

서버 측 내용 - 예시

더보기
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });    // 주소는 아니까 port 만 적음. 내가 서버의 생성을 할거다

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });

  ws.send('something');
});

-> 콜백 지옥이 될듯.. 이거를 벗어나기 위해서 function 함수로 뺀 것

function wsInit(){  

}

 

얘의 역할은 내 자신을 websocket 서버로 만들겠다는 내용을 담고 있는 코드를 작성했다

// 예제
const wss = new WebSocketServer({ port: 8080 });
이거랑

// network.js
const server = new WebSocket.Server({port:6005})
이거는 같은 거임

[network2.js - 클라 부분]

const ws = new WebSocket('ws://www.host.com/path', {
  perMessageDeflate: false
});

 

클라 부분에 얘까지 되었을 때 wss.on('connection') 이 되는 것이다.

여기는 서버 측 관련된것을 만든것이다
안에 있는 코드들은 내가 서버를 만들고 어떻게 하겠다는 내용들..

 


[network.js] 간단한 데이터 주고받기

1. 1차 시도

function wsInit() {
    const server = new WebSocket.Server({ port: 6005 })

    // [비동기] 서버가 connection 맺어졌다 인자값이 하나 있는데 그걸 ws 라고 하겠다
    server.on("connection", (ws) => {
        ws.on('message', (message) => {
            console.log(`received: ${message}`);
        });

        ws.send('something')
    })
}

wsInit()


이렇게 작성했다.
$ node network

했는데 아무런 변화가 없다


2. 2차 시도

function wsInit() {
    const server = new WebSocket.Server({ port: 6005 })

    console.log(1);
    server.on("connection", (ws) => {
        console.log(2);
        ws.on('message', (message) => {
            console.log(3);
            console.log(`received: ${message}`);
        });

        ws.send('something')
        console.log(4);
    })
}

wsInit()


이렇게 했을 때 
$ node network 
하면 
1 까지 실행됨

connection 이 안되어서 2 부터는 실행이 안되고 있는거임

 


 

3. 3차 시도 - 클라이언트 생성 [network2.js]


그래서 network2.js 를 만들었다,
여기에는 클라쪽 코드를 작성 해 볼 것이다.

const WebSocket = require('ws')

const ws = new WebSocket('ws://localhost"6005')        
//network 6005 번으로 접속해야함

ws.on('open',()=>{
    ws.send('something')
})

ws.on('message',(message)=>{
    console.log(`received : ${message}`);
})


이렇게 작성해준다.

두개의 서버가 있어야 작동이 되는것이다

$ node network
$ node network2

하면

network
1
2
4
3
received: something

이런 결과가 나온다.

 

근데 network2 는
received : something
-> 얘는 왜 찍히는 걸까?

[network2.js]를 보면

ws.on('message',(message)=>{
    console.log(`received : ${message}`);
})

 

얘 때문에 실행되는것..
근데 저 message 라는 변수는 뭐냐? 위에있는 something 이냐?
아니다!!

 

저 message 는 서버 [network.js]측에서 보내주는 

ws.send('something')

 

이것이다.
(connection 이 맺어질때 바로 보낸거임)



저 부분을 알기 쉽게 ingoo 라고 바꿔보자
그러다면 send 라는 매서드는 내가 보낼 내용들을 전달할때 사용한다.
받는 애들은 나와 연결된 소켓들..

그걸 받는 이벤트 명이 'message' 인 것이다.
[network2.js]

ws.on('message',(message)=>{
    console.log(`received : ${message}`);
})

 

send('무언가') -> 'message'
이 무언가는 클라 쪽 콜백함수의 인자값으로 들어간다.

[서버]
ws.send('ingoo')


[클라]
ws.on('message',(message)=>{
    console.log(`received : ${message}`);
})

 


클라이언트 서버에서 connect() 하는 부분이 'open' 인가??

서버측을 종료하고 클라만 실행하면?
뭔가 안됨
그래서
[network2.js] 에

ws.on('error',()=>{
    console.log('error 발생');
})

 

이 부분을 추가하고 다시

$node network2 해보면

error 발생이 나온다.


[network2.js]

ws.on('open',()=>{
    ws.send('something')
})

 

이 코드의 형태는 이벤트를 받는 형태이다
무언가가 연결이 되어야 open 이라는 키워드를 받을것이다.
open 이라는 것은 연결이 완료가 되어있고
연결이 완료가 되었고, 연결 완료 되는 시점에 무언가 찍고 싶을때. 최초에 실행하는 코드 한번.

얘가 없어도 message 저거는 받아 올 수 있음

이벤트를 받아서 처리하는... 코드들은 커넥션을 맺기가 어렵다
이런 형태를 띄고 있다고 해서 connection - open ? 이거는 아니다.

 

[network] 전체코드

더보기
//ws
const WebSocket = require('ws')

// 내 자신을 WS 서버로 만들겠다
function wsInit() {
    const server = new WebSocket.Server({ port: 6005 })

    console.log(1);
    server.on("connection", (ws) => {   // 어떤 클라가 요청이 되면 (connection 이 맺어지는 신호를 받으면)실행시키겠다
        console.log(2);
        ws.on('message', (message) => { // 그럼 이 message 라는 애는 뭘까? 
            console.log(3);
            console.log(`received: ${message}`);
        });

        ws.send('ingoo')
        console.log(4);
    })
}

// wsInit()

module.exports = {
    wsInit,
}

[network.js]
그래서 wsInit() 은 소켓 서버를 구동시키는 코드이다

이 코드는 server.js 인터페이스 쪽에서 사용을 했었다.

그래서 우리가 코드를 작성할 때

module.exports = {
    wsInit,
}

이렇게 보냈었다.

근데 만약 서버가 3대 4대 들어오면 어떻게 될까?

서버 모두에게 ws.send('ingoo') 가 다 전달 될것이다.

그래서 이 ws 를 배열에 담기 시작한다.
ㄴ 특정 사람에게만 send 주기 위해서.
귓속말 처럼..

let sockets = []
이렇게 빈 배열을 만들어서

client가 connection을 맺을 때 마다 저 배열에 하나씩 쌓았다.

//ws
const WebSocket = require('ws')

let sockets = []    // 접속한 사람들 중 특정 사람에게만 내용을 전달하고 싶어서 이렇게 따로 뺌

function wsInit() {
    const server = new WebSocket.Server({ port: 6005 })
    server.on("connection", (ws) => { 
       init(ws)
    })
}

function init(ws){
    sockets.push(ws)
}

module.exports = {
    wsInit,
}
server.on("connection", (ws) => { 
       init(ws)
       // ws.on("message",()=>{}) 클라가 주는 메시지를 받고 싶어
       // ws.send("text ~~") 클라에게 내용전달
       // 원래는 이 안에 작성을 해야 한다 근데 우리는 밖에다 따로 뺐었다. - 길어질까봐

    })
function wsInit() {
    const server = new WebSocket.Server({ port: 6005 })
    server.on("connection", (ws) => { 
       init(ws)
    })
}

function init(ws){
    sockets.push(ws)
    initMessageHandeler(ws)
}

function initMessageHandeler(ws){
    ws.on('message', (data)=>{
        console.log(data);
    })
}

wsInit()

이렇게 함수로 빼주고

 


network2.js
에서 send로 보내면

ws.on('open',()=>{
    ws.send('서버야 받아봐라')
})


$ node network
$ node network2

하면 
<Buffer ec 84 9c eb b2 84 ec 95 bc 20 eb b0 9b ec 95 84 eb b4 90 eb 9d bc>
이런 값이 나온다

[network.js]

function initMessageHandeler(ws){
    ws.on('message', (data)=>{
        console.log(data.toString());
    })
}

 

이렇게만 바꿔주면
우리가 읽을 수 있는 형태로 나오게 된다.

 


websocket 과 socket.io 의 차이점

socket.io 에도 on 이라는게 있는데
내용을 보낼때 socket.emit("이벤트명", {내용들}) 을 통해서 보냈었다.
그래서 받았을 때 그 이벤트 명인 애를 받아서 처리가
socket.on("이벤트명",(인자값)=>{})
이렇게 받았었다. {내용들}은 인자값으로 들어가게 된다.

[socket.io]
내용을 전달할때 이벤트 명도 같이 적어서 보낸다.
특정 이벤트 일때만 실행되도록

[websocket]
은 객체를 못보냄 그냥 텍스트만 보낼 수 있음
한번 객체로 보내보자

ws.send(`{"name":"ingoo"}`) // 무조건 이 형태여야만 JSON.parse 가 작동함

 

이렇게 받아오면

ws.on('message', (data)=>{      
    console.log(JSON.parse(data))
})

 

근데 이렇게하면 너무 불편하다,
그래서

ws.on('open',()=>{
    let obj = {name : 'ingoo'}
    let rst = JSON.stringify(obj)
    ws.send(rst)
})

 

이렇게 보내면 잘 들어온다.

ws.send 
는 string 으로만 보낼 수 있다.
받을 때도 string 으로 받는다. 
그래서 받은 다음에 객체형태로 변경하는것

send (객체형태 -> string)
'message' (string -> JSON 으로 객체형태 만듦)

어차피 모든 send 는 obj -> string 으로 바꿔야 하는데 
이 기능을 따로 함수로 빼놓자 해서

ws.on('open',()=>{
    let obj = {name : 'ingoo'}
    write(ws,obj)
})

function write(ws, message){
    ws.send(JSON.stringify(message))
}

 

이런식으로 빼게 된것이다.

웹소켓으로 뭔가 전달할땐 무조건 string 으로만 전달한다

지금 사용하고 있는 웹소켓은 send 말고는 보낼 수 있는 방법이 없다.
그래서 객체 안에다가 구분값을 넣어줘야 한다.
그래서 객체에 type이라는 구분값을 만들어줬다

ws.on('open',()=>{
    let obj = {
        type : 'msg',    
        data : '안녕하세용'
    }
    write(ws,obj)
})


그런 다음 받을 때
switch 문으로 처리를 해줌

initMessageHandeler() 여기에서

function initMessageHandeler(ws){
    ws.on('message', (data)=>{      
        const message = JSON.parse(data)

        switch(message.type){
            case "msg":
                console.log(message.data);
                console.log("msg를 출력한다");
            break;
        }
    })
}


이렇게 작성하고 다시 클라를 날려보면
잘 나옴

 


[클라]
ws.on('open',()=>{
    let obj = {
        type : 'msg',    
        data : '안녕하세용'
    }
    write(ws,obj)
    
    obj2 = {
        type : 'send',
        data : '데이터를 보내겠다'
    }
    write(ws,obj2)
})

[서버]
function initMessageHandeler(ws){
    ws.on('message', (data)=>{      
        const message = JSON.parse(data)

        switch(message.type){
            case "msg":
                console.log(message.data);
                console.log("msg를 출력한다");
            break;
            case "send":
                console.log(message.data);
                console.log("data를 출력한다");
            break;
        }
    })
}

이렇게 내용을 구분 지을 수 있게 message.type 을 정해서 보내준다.
근데 socket.io 는 이렇게 안해도 보낼 수 있었다.
(앞에서 구분할 수 있게 하고 전송데이터 형식도 json 으로 보낼 수 있음 -io)

여기서 case 부분에 string 값으로 들어간것을 빼서 변수로 만들어주자

const MSG = "msg"
const SEND = "send"

 

이렇게 바꾸고 case 부분도 바꿔준다.

 

더 나아가서

const MessageAction = {
    MSG = "msg",
    SEND = "send"
}


변수들을 객체 안에 넣고 사용할때는
case MessageAction.MSG:
이런식으로 사용할 수 있다.


이 이벤트 타입을 3가지로 보냈었다.

[클라 보내기]
ws.on('open',()=>{
    let obj = {
        type : 0,    
        data : '안녕하세용'
    }
    write(ws,obj)

[서버 받기]
const MessageAction = {
    QUERY_LAST : 0,
    QUERY_ALL : 1,
    RESPONSE_BLOCK : 2
}

case MessageAction.QUERY_LAST:
...
case MessageAction.RESPONSE_BLOCK:
    handleBlockResponse()   // 코드 칠 양이 많아서 따로 뺐다.
break;



[클라- 보내기]
// 객체 만드는 부분 함수로 따로 빼기
// [network2.js]

ws.on('open',()=>{
    //type 0
    write(ws,queryBlockMsg())
    
    //type 1
    write(ws,queryAllMsg())
})

const MessageAction = {
    QUERY_LAST : 0,
    QUERY_ALL : 1,
    RESPONSE_BLOCK : 2
}
function queryBlockMsg(){
    return{
        type : MessageAction.QUERY_LAST,
        data : null,
    }
}
function queryAllMsg(){
    return{
        type : MessageAction.QUERY_ALL,
        data : null,
    }
}


이렇게 
만드는 부분 따로 빼주고 보내줄 때 따로 빼준 함수를 이용해서 보내주면 된다.

 


error 처리

[network.js]

서버 종료 : close 이벤트
에러 발생

function init(ws){
    sockets.push(ws)
    initMessageHandeler(ws)
    initErrorHandeler(ws)
}

function initErrorHandeler(ws){
    ws.on("close",()=>{console.log('close ??');})       // 둘 다 켜져있다가 클라가 꺼지면 발동되는 이벤트 명이다. - 내가 종료 X  |  클라가 종료 되었을때 O
    ws.on("error",()=>{console.log('error');})
}

 

이렇게만 하면 연결 끊긴 클라를 끊어낼 수 없다.

 


 

function initErrorHandeler(ws){
    // ws.on("close",()=>{
    //     console.log('close ??');
    //     sockets.splice(sockets.indexOf(ws),1)   // sockets 배열에서 자른다. - 찾을 인덱스값과 ,자를 갯수 - 접속을 해지한사람은 내가 관리할 수 있는 사람이 아니기 때문에 다시는 내용을 보낼 수 없게 배열에서 자른것 - 에러가 난 부분에도 똑같이 해주면 됨
    // })       // 둘 다 켜져있다가 클라가 꺼지면 발동되는 이벤트 명이다. - 내가 종료 X  |  클라가 종료 되었을때 O
    // ws.on("error",()=>{
    //     console.log('error');
    //     sockets.splice(sockets.indexOf(ws),1) 
    // })
    // 이렇게 했더니 코드가 너무 똑같아서

    ws.on("close",()=>{CloseConnection(ws)})
    ws.on("error",()=>{CloseConnection(ws)})

}

function CloseConnection(ws){
    console.log(`Connection cloese ${ws.url}`);
    sockets.splice(sockets.indexOf(ws),1) 
}