본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[119일차] 블록체인 네트워크 웹소켓 http ws 웹서버 구축

반응형

 

P2P (Peer to Peer) 구현 방법 

-> WebSocket 

-> socket.io = 웹소켓으로 웹으로 구성할 때 필수적인 구성을 미리 만들어 놓은 패키지 

   이전 node.js chatting을 만들 때 사용함

   기본 기능 외 여러가지 기능이 많다. 

   처음 사용하는 사람이 사용하기 편하다

-> ws (web socket 약자) 

   접속에 대한 것만 ex) broadcast, to 

 

블록체인은 두 개의 port가 필요

1) 서버 - 클라이언트 

2) 노드끼리 통신 

 

오늘 웹서버 구축의 기초작업 / setting 예정 ! 

 

 


 

 

1) server - client http 서버 먼저 만들기 

with express 

 

express 설치  **** 터미널  경로 src 에서 

npm i express

 

src > server.js 파일 생성 

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;



app.listen(port,()=>{
    console.log(`server start port ${port}`)
})

 

 

잘 나오는지 확인 

node server.js

 

 

 

 

 

환경변수 설정

윈도우    -   ** "" or '' 따옴표 없음 

set [변수명]=값 
set [변수명] // 확인 

mac or linux 

export [변수명]=값
env | grep [변수명] 

 

환경변수 port number 바꿔보기 

 

 

 

 

<웹서버 구축 목적>

블록 가져오기 

peer / 간단한 기록들 / 버전

중단 

 

 

server.js - 어제 만든 blocks 가져오기 

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const bc = require('./block.js')  //const {getBlocks} require~ 와 같음. 이번엔 객체로 가져와서 쓰기



app.listen(port,()=>{
    console.log(`server start port ${port}`)
})

 

json으로 대화할 예정 ! -> bodyParser 설치 / 가져오기 / 코드 작성

 

npm i body-parser

server.js

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const bodyParser = require('body-parser')
const bc = require('./block.js')  //const {getBlocks} require~ 와 같음. 이번엔 객체로 가져와서 쓰기


app.use(bodyParser.json());


app.get('/blocks', (req,res)=>{
    res.send(bc.getBlocks())
})



app.listen(port,()=>{
    console.log(`server start port ${port}`)
})

 

 

window terminal에서 아래 명령어 입력해서 응답 쳌 

리눅스 명령어임 !리눅스 환경에서 해야해

curl -X GET http://localhost:3000/blocks
curl http://localhost:3000/blocks -X GET   (요것도 가능!) 

-X 을 써서 method 입력 가능 

-X GET 

-X POST 

 

 

python이 설치되어 있다면 아래 명령어로 더 편리하게 볼 수 있다. 

curl -X GET http://localhost:3000/blocks | python3 -m json.tool

어제 만든 제네시스 블록과 그 다음 두 번째 블록의 정보들이 나옴! 

 

Python 이 없다면 Postman 사용 가능 ! 

 

 

 

 

 

Version 확인

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const bodyParser = require('body-parser');
const bc = require('./block.js');  //const {getBlocks} require~ 와 같음. 이번엔 객체로 가져와서 쓰기


app.use(bodyParser.json());


app.get('/blocks', (req,res)=>{
    res.send(bc.getBlocks());
})

app.get('/version',(req,res)=>{
    res.send(bc.getVersion());
})


app.listen(port,()=>{
    console.log(`server start port ${port}`);
})

 

 

 

 

 

 

블록을 추가하는 것을 웹 서버에서 해보기 

data이다 보니 post로 해보기 

src > server.js

app.post('/mineBlock',(req,res)=>{
    let {data} = req.body;
    const result = bc.addBlock(data)  // 응답을 수정해보기 
    
})

 

src> block.js 응답 보내는 곳 newBlock을 보내도록 수정 

// 얘는 단순히 Push만 할 것 ! 인자값에 배열을 넣어주기 (data)
function addBlock(data) {
    // new header 만들어서 => new block(header, body) 
    const newBlock = nextBlock(data) // return 다음 블록에 대한 객체 
    // newBlock과 마지막 
    if(isValidNewBlock(newBlock, getLastBlock())){
        Blocks.push(newBlock);
        return newBlock;  //함수가 여기서 끝나버리게 -------------true -> newBlock 수정
    }
    return false;
}

 

 

=> result 는 false or newBlock 둘 중 하나의 응답을 받게됨 ! 

app.post('/mineBlock',(req,res)=>{
    let {data} = req.body;
    const result = bc.addBlock(data)  // 응답을 수정해보기 
    if(result ==false){
        res.send(`mineBlock failed')`)
    }else{
        res.send(result)
    }
})

 

 

리눅스 명령어 설명 

//  curl - http 통신을 할거다 ~ 라는 의미 
// 모든 리눅스 명령어는 - 는 옵션값

// -X : request method (ex. POST, GET,..)
// -H : "Content-Type:application/json"      // H : header
// -H 쓰고 header 더 쓸 수 있음              
// -d "{\"data\":[\"Helloooooo\"]}"      // D : data

 

POST 리눅스에서 아래 명령어 실행 

curl -X POST -H "Content-Type:application/json" -d "{\"data\":[\"hello\"]}" http://localhost:3000/mineblock
or python json tool 사용해서 
curl -X POST -H "Content-Type:application/json" -d "{\"data\":[\"hello\"]}" http://localhost:3000/mineblock | python3 -m json.tool

 

=> 실행할 때마다 index가 늘어남 (new block 이 생성되고 있음 !) 

=> 하나의 서버가 블록체인과 통신할 수 있는 server가 생김 ! 

 

 

 

 

프로세스 종료하기 

vs terminal 에서 Ctrl + c 로 server 끄는 거 말고 원격으로 끄도록 만들기 

server.js 코드 추가 -> server 쪽에 코드를 수정 or 추가할 시에는 꼭 server를 껐다가 켜야함 !!!!!!* 

app.get('/stop',(req,res)=>{
    res.send("server's jsut stopped (●ˇ∀ˇ●)")
    process.exit(0)
})

 

 

 

Client server 끝! 

 

 

 

 

 

 

 

이제 socket server만들기 

 

ws를 사용해서 구현해보기 

1. npm 설치 

npm i ws

2. src > network.js 파일 생성 + 코드작성

const WebSocket = require('ws');
const wsPORT = process.env.WS_PORT || 6005;

터미널 명령어 

set WS_PORT=6005
set WS_PORT   // 로 확인 

 

하나의 컴퓨터에 두 개의 port를 열어 놓기 ! 

아까 만든 client server = 3000 

지금 만들 ws server = 6005 

 

3. socket 만들기 

const WebSocket = require('ws');
const wsPORT = process.env.WS_PORT || 6005;

// 전역변수 sockets - 소켓에 연결된 사람(peer)들을 모아놓은 list 
let sockets =[];

function getSockets(){
    return sockets
}

// socket은 event 적인 js code가 많아! event 란 ? 보통 비동기 (async await)
// Object.on('msg',()=>{ ()=> .... blabla.........콜백지옼


// 최초의 접속 socket.io 에서 connect와 같은.. 
function wsInit(){
    const server = new WebSocket.Server({port:wsPORT}); 
    // 첫번째 인자값은 msg 값 , 그리고 두 번째 인자값 = 함수를 실행하겠다.
    // wsPORT(port 6005)인 server 
    // 웹소켓의 인자값 ws를 받아 어디론가 전달해줄 것 
    server.on('connection',(ws)=>{
        console.log('ws=',ws)
        init(ws)
    })
}

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

 

 

4. socket.io의 broadcast 기능 구현

socket.io는 body내용을 객체로 보내고 받는게 가능 

ws는 정석대로, 원래 데이터는 string으로 주고 받는 것 - > parse해서 객체로 변환해서 쓰는 것이었다.. 

// 최초의 접속 socket.io 에서 connect와 같은.. 
function wsInit(){
    const server = new WebSocket.Server({port:wsPORT}); 
    // 첫번째 인자값은 msg 값 , 그리고 두 번째 인자값 = 함수를 실행하겠다.
    // wsPORT(port 6005)인 server 
    // 웹소켓의 인자값 ws를 받아 어디론가 전달해줄 것 
    server.on('connection',(ws)=>{
        console.log('ws=',ws)
        init(ws)
    })
}

function write(ws,message){
    // message 객체 를 글자로 바꿔서 보내준다. 
    ws.send(JSON.stringify(message))
}

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

 

 

 

5. broadcast 

function write(ws,message){
    // message 객체 를 글자로 바꿔서 보내준다. 
    ws.send(JSON.stringify(message))
}

function broadcast(message){
    sockets.forEach( socket=>{
        // 나 자신에게 msg 보내기 
        write(socket, message)
    })
}

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

 

 

 

 

6. Peer 

function connectionToPeers(newPeers){ //newPeers 는 배열 
    // peer -> string 주소 값 ws://localhost:7001
    // http protocal 대신 ws 포로토콜! 
    newPeers.forEach(peer=>{ 
        // 접속 
        const ws = new WebSocket(peer) //websocket자체를 실행(도메인넣기) 아까 한 Server는 포트만 넣어 생성한 것 
        // WebSocket.Server는 open까지 해주는데 WebSocket는 직접 옵흔해주기
        ws.on('open',()=>{init(ws)})
        // ERROR
        ws.on('error',()=>{console.log(`Connection failed (newPeers.forEach ERROR...)`)})
        
    })
}

 

 

 

 

7. server.js 시작할 때, network.js도 시작되도록 하기 

network.js module exports

module.exports = {
    wsInit, getSockets, broadcast, connectionToPeers,
}

server.js 가져와서 실행

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const bodyParser = require('body-parser');
const bc = require('./block.js');  //const {getBlocks} require~ 와 같음. 이번엔 객체로 가져와서 쓰기
const ws = require('./network.js');

.
.
.

ws.wsInit();
app.listen(port,()=>{
    console.log(`server start port ${port}`);
})

 

server 실행해보고 오류가 있는지 점검 

node server.js

=> 3000번 , 6005번 각각 port가 둘 다 열림 

 

 

 

 

8. Block을 모으기 

 

express : Client 

Websocket : server 측의 느낌이 있음.... 

 

peers 를 볼 수 있는 것  => 현재 가지고 있는 socketlist  (getSockets)  -> get

addPeers, peer를 추가하는 것이 필요 -> 내가 보낼 주소 값에 소켓을 생성하는 작업  (connectToPeers) -> 데이터를 넘겨줌 -> post 

 

// peers 현재 가지고 있는 socket List 
app.get('/peers',(req,res)=>{ 
    // ws.getSockets() => return 배열
    res.send(ws.getSockets().map(socket=>{
        return `${socket._socket.remoteAddress}:${socket._socket.remotePort}`;
    }))
})

리눅스에 명령어 입력 

curl http://localhost:3000/peers

 

 

addpeers 

// addpeers -> 내가 보낼 주소값에 소켓을 생성하는 작업 
// data 보내야하니깐 POST
// curl -X POST -H "Content-Type:application/json" -d "{\"peers\":[\"ws://localhost:7001\", \"ws://localhost:7002\"]}" http://localhost:3000/addPeers
app.post('/addPeers', (req,res)=>{
    const peers = req.body.peers || [] // 예외처리 
    ws.connectionToPeers(peers);
    res.send('success');
})

server off - >on ** 반드쉬 

curl -X POST -H "Content-Type:application/json" -d "{\"peers\":[\"ws://localhost:7001\", \"ws://localhost:7002\"]}" http://localhost:3000/addPeers

 

 

failed 되는 것은 당연한 것이라고 함 !

이유 : ws://localhost:7001 로 실행된 건 없다.. 

local에 7001이 구동되고 있어야함! 

내pc에서 7001을 구동시켜보자 

 

 

websocket은 client, server 둘 다 될 수 있다. 

 

 

 

src 폴더 복사 -> src2 명명

server.js port : 3001

netword.js : 7001로 수정 

 

src node server.js

src2 node server.js  둘 다 실행하기 ! 

 

어렵군어려워..

 

<코드 흐름 이해,,,>

하나의 컴퓨터 안에

1) port 3000 실행 (http 통신규약)

2) port 6005 실행 (ws 웹소켓 통신규약) 

=> server.js 로 묶여있음 ! 

 

3000, 6005번이 함께 컴퓨터에 돌고 있는 상태 

ws -> 모듈로 빼서 server.js 파일이 ws에 접근할 수 있다. 

 

curl -X GET http://3000/peers ~ 요청 on Linux

요청을 받은 http://3000/peers  express가 실행 

----> ws://6005   getSockets()의 결과물을 반환 to 3000

-----> 3000은 결과를 ----> Linux 로 돌려줌 (test로 받은 것 !) 

 

 

curl -X POST http://3000/addPeers 요청 on Linux 

요청 받은 http://3000/addPeers -> connectionToPeers() (http:6001) 실행   -> ws://7001,7002 이 없는데 -> 실패라고 응답 to 3000 -----> 3000 응답 전달 ---> Linux says failed.

 

-> src 를 복사해서 3001, 7001 로 수정 => 총 server 4대가 돌아가고 있음 ! 

http3001은 사실 필요가 없다 ! 근데 ws 7001이 필요! ws7001만 만들면 되는데 빠른 진행위해 그냥 복사해서 넣은 것! 

 

 

 

3000/addPeers 를 linux에 구동시킨 수 만큼 peers 배열에 추가가 된다. 

 

오늘 배운건 채팅과 같은 원리 - 그저 터미널로 했을뿐 ! 

사실 웹소켓에 연결된 것- Clinet & Server의 연결 

 

 

이번 주말 과제 : 이전에 해본 socket.io 대신 ws 로 채팅 구현해보기 

반응형