jnk1m
Foliage IT
jnk1m
전체 방문자
오늘
어제
  • 분류 전체보기 (209)
    • Today I Learned (34)
    • Java (47)
    • Database (15)
    • [NHN Academy] (27)
    • Spring (47)
    • HTML + CSS + JavaScript (11)
    • JSP (3)
    • Node.js (10)
    • React Native (2)
    • 기타 (8)
    • 스크랩 (5)

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
글쓰기 / 관리자
jnk1m

Foliage IT

Node.js

[Node.js] 2022/05/16 : Node_Socket

2022. 5. 18. 01:04

**Node_Socket
1.Socket
=>통신을 할 수 있도록 해주는 NIC(Network Interface Card)를 추상화(프로그래밍에서 사용할 수 있도록 객체로 변환) 한 것

1)소켓 프로그래밍의 분류
=>고수준 소켓 프로그래밍: 직접 연결 과 해제를 하지 않는 방식으로 웹 프로그래밍이 대표적인 고수준 소켓 프로그래밍의 대표적인 방식인데 구현이 쉽지만 효율이 떨어집니다.

=>저수준 소켓 프로그래밍(소켓 프로그래밍): 직접 소켓을 생성해서 통신을 하는 방식으로 구현이 어렵지만 효율이 좋습니다.

2)프로토콜 종류
=>TCP: 연결형 통신
요청 -> 제공하는 쪽에서 메타 데이터 전송 -> 요청 -> 데이터 전송 -> 응답 

=>UDP: 비연결형 통신
요청 -> 데이터 전송

2.Web Socket
1)HTTP, HTTPS
=>Client 와 Server 간 접속을 유지하지 않고 한 번에 한 방향으로만 통신이 가능한 Half-Duplex(반 이중 - 어느 한 순간에는 수신이나 송신만 가능)
=>Client 와 Server 간의 정보 유지를 위해서 Cookie 와 Session의 개념을 학습니다.
=>서로 간에 짧은 주기를 가지고 데이터를 자주 주고 받아야 하는 경우에 성능 저하를 피할 수 없음
=>접속을 유지할 수 없기 때문에 Client 의 요청 없이 Server 가 클라이언트에게 데이터를 전송할 수 없음 
이 부분은 ajax pooling 기법으로 해결

2)HTML5에 새로 추가된 API
=>HTML5의 로컬 저장소: WebStorage, Web SQL, Indexed DB 같은 개념을 이용해서 웹 브라우저에 데이터를 저장하는 개념
=>HTML5의 Web Socket: 클라이언트 와 서버 간의 Full-Duplex(전 이중 - 동시에 주고 받는 것이 가능)
=>HTML5의 Web Push: 서버가 클라이언트의 요청이 없어도 데이터를 전송하는 기능, Notification 이나 
SSE(Server Sent Event) 이라고 합니다.

3)웹 소켓
=>ws 프로토콜 사용
=>모든 브라우저가 웹 소켓을 지원하지는 않습니다.
IE 하위 버전에서는 지원하지 않습니다.

3.Node 에서의 Web Socket - websocket 모듈 이용
=>Socket.IO 모듈을 이용하는 경우가 많이 이용하지만 ws 모듈을 이용하기도 하고 websocket 모듈도 기능을 제공함
1)web socket 구현
=>프로젝트 생성

2)필요한 패키지 설치
=>websocket, express, morgan

=>개발용으로 nodemon


3)프로젝트에 index.html 파일을 생성하고 작성
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>웹 소켓</title>
    </head>
    <body>
        소켓 연결 상태:<span id="status"></span>
        <br />
        메시지:<ul id="message"></ul>
    </body>
</html>

4)프로젝트에 index.js 파일을 추가하고 작성
var WebSocketServer = require('websocket').server;
var http = require('http');
var fs = require('fs');

var server = http.createServer((req, res) => {
    if(req.url == "/"){
        res.writeHead(200, {'Content-Type':'text/html'});
        res.end('Web Socket')
    }else if(req.url == "/index"){
        fs.readFile("index.html", (error, data) => {
            res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});
            res.end(data);
        })
    }


})

//서버 구동
server.listen(8000, function(){
    console.log('Server is listening on port 8000');
})

5)프로젝트를 실행하고 브라우저에 localhost:8000, localhost:8000/index 요청을 확인

6)index.js 파일에 웹 소켓 서버 구현 코드를 추가
//웹 소켓 서버 구현
wsServer = new WebSocketServer({
    httpServer:server,
    autoAcceptConnections:false
});

//클라이언트에서 연결 요청이 오면
wsServer.on('request', (request) => {
    //클라이언트 와 example-echo 라는 이름으로 연결
    var connection = request.accept('example-echo', request.origin);

    //연결된 클라이언트에서 메시지가 오면
    connection.on('message', (message) => {
        //텍스트 데이터라면
        if(message.type === 'utf8'){
            //메시지 출력
            console.log('받은 메시지:' + message.utf8Data);
            //받은 메시지를 클라이언트에게 전송
            connection.sendUTF(message.utf8Data);
        }
        //일반 파일 데이터라면
        else if(message.type == 'binary'){
            connection.sendUTF(message.binaryData);
        }

        connection.on('close', (reasonCode, description) => {
            console.log('Peer ' + connection.remoteAddress + 
            ' disconnected.')
        })
    });
})

7)index.html 파일에 웹 소켓 사용을 위한 스크립트 코드를 추가
    <script>
        //브라우저의 웹 소켓 여부 확인
        if('WebSocket' in window){
            //DOM(Document Object Model) 찾아오기
            var status = document.getElementById('status');
            var message = document.getElementById('message');

            //웹 소켓 연결
            //IP는 서버의 IP적어야 하고 이름은 서버에서 만든 이름을 적어야 합니다.
            var ws = new WebSocket('ws://127.0.0.1:8000', 'example-echo');

            ws.addEventListener('open', (e) => {
                status.innerHTML = '연결 성공';
                for(var i = 0; i<10; i++){
                    //웹 소켓 서버에게 전송
                    ws.send('Hello ' + i);
                }
            });

            ws.addEventListener('message', (evt) =>{
                message.innerHTML += 
                    '<li>받은 메시지:' + evt.data + '</li>';
            })
        }
    </script>

4.Node 에서의 Web Socket - ws 모듈 이용: express 모듈 과 함께 사용 가능
1)패키지를 추가 설치
cookie-parser, detenv, express, express-session, morgan, nunjucks, ws 

2)프로젝트에 라우팅 모듈화를 위한 routes 디렉토리를 생성

3)routes 디렉토리에 index.js 파일을 생성하고 요청 처리 코드를 작성
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
    res.render('websocket');
});

module.exports = router;

4)웹 소켓 로직을 위한 socket.js 파일을 생성하고 작성
const WebSocket = require('ws');

module.exports = (server) => {
    //웹 소켓 서버 생성
    const wss = new WebSocket.Server({server});

    //클라이언트가 접속을 하면
    wss.on('connection', (ws, req)=>{
        //클라이언트의 IP 확인
        const ip = req.headers['x-forwarded-for'] 
            || req.connection.remoteAddress;
        console.log('새로운 클라이언트 접속:' + ip);

        ws.on('message', (message) => {
            console.log("클라이언트에게 받은 메시지:", message);
        });

        ws.on('close', () => {
            console.log("클라이언트 접속 종료:", ip);
            //타이머 종료
            clearInterval(ws.interval);
        });

        //타이머를 이용해서 클라이언트에게 주기적으로 메시지를 전송
        ws.interval = setInterval(() => {
            if(ws.readyState === ws.OPEN){
                ws.send('서버에서 클라이언트에게 메시지를 전송합니다.');
            }
        }, 3000)

    })
};

5)프로젝트의 idnex.js 파일을 수정
//ws 모듈을 이용한 웹 소켓 구현 - 서버에서 일정한 주기를 가지고 메시지 전송
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const nunjucks = require('nunjucks');
const dotenv = require('dotenv');

dotenv.config();

const webSocket = require('./socket');
//파일 이름을 생략하면 index.js 입니다.
const indexRouter = require('./routes')

const app = express();
app.set('port', 8001);
//뷰 템플릿(서버의 데이터를 출력할 수 있는 html 파일) 설정
app.set('view engine', 'html');
nunjucks.configure('views', {
    express:app,
    watch:true
});

app.use(morgan('dev'));

app.use(express.static(path.join(__dirname, 'public')));

app.use(express.json());
app.use(express.urlencoded({extended:false}))

app.use(cookieParser('websocket'));

app.use(session({
    resave:false,
    saveUninitialized:false,
    secre:'websocket',
    cookie:{
        httpOnly:true,
        secure:false
    }
}))

// /로 시작하는 요청은 indexRouter 가 처리
app.use('/', indexRouter);

const server = app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 대기 중');
})

webSocket(server);


6)프로젝트에 views 디렉토리를 생성하고 websocket.html 파일을 만든 후 작성
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>웹 소켓</title>
    </head>
    <body>
        <div>검사를 이용해서 console 과 network를 확인하세요</div>
    </body>

    <script>
        const webSocket = new WebSocket("ws://localhost:8001");
        webSocket.addEventListener("open", () => {
            console.log("웹 소켓 서버와 연결");
        });
        webSocket.addEventListener('message', (evt) => {
            console.log(evt.data);
            webSocket.send("클라이언트가 서버에게 보내는 메시지");
        })
    </script>
</html>

5.node 에서 웹 소켓 구현 - socket.io 모듈
1)설치
npm install socket.io

2)프로젝트의 socket.js 파일을 수정 - 없으면 생성

const SocketIO = require('socket.io');

module.exports = (server) => {
    //웹 소켓 서버 생성
    const io = SocketIO(server, {path:'/socket.io'});

    //클라이언트가 접속을 하면
    io.on('connection', (socket)=>{
        //클라이언트의 IP 확인
        const req = socket.request;
        const ip = req.headers['x-forwarded-for'] 
            || req.connection.remoteAddress;
        
        console.log('새로운 클라이언트 접속:' + ip);

        socket.on('disconnect', () => {
            clearInterval(socket.interval);
        });

        socket.on('reply', (data) => {
            console.log(data);
        })

        socket.interval = setInterval(()=>{
            //emit 은 강제로 이벤트를 발생시키는 것입니다.
            //news 라는 이벤트를 안녕이라는 파라미터로 발생
            socket.emit('news', '안녕');
        }, 3000)

    })
};

3)websocket.html 파일 수정
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>웹 소켓</title>
    </head>
    <body>
        <div>검사를 이용해서 console 과 network를 확인하세요</div>
    </body>

    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io.connect('http://localhost:8001', {
            path:'/socket.io',
            transports:['websocket']
        })

        socket.on('news', function(data){
            console.log(data);
            socket.emit('reply', 'Hello socket.io');
        })
    </script>
</html>



6.채팅 구현
1)socket.js 파일을 수정
//socket.io 모듈 가져오기
const SocketIO = require('socket.io');

//서버를 생성해서 다른 곳에서 사용할 수 있도록 설정
module.exports = (server) => {
    //웹 소켓 서버 생성
    const io = SocketIO(server, {path:'/socket.io'});

    //클라이언트가 접속을 하면
    io.on('connection', (socket)=>{
        //클라이언트의 IP 확인
        const req = socket.request;
        const ip = req.headers['x-forwarded-for'] 
            || req.connection.remoteAddress;
        
        console.log('새로운 클라이언트 접속:' + ip);

        //접속을 해제 했을 때 처리 - 타이머 종료
        socket.on('disconnect', () => {
            clearInterval(socket.interval);
        });

        //reply 이벤트가 발생했을 때 처리 - 사용자가 보내는 reply 이벤트
        //원래 존재하는 이벤트가 아님
        socket.on('reply', (data) => {
            console.log(data);
        })

        //타이머 생성 - 3초 마다 강제로 news 라는 이벤트를 발생
        socket.interval = setInterval(()=>{
            //emit 은 강제로 이벤트를 발생시키는 것입니다.
            //news 라는 이벤트를 안녕이라는 파라미터로 발생
            socket.emit('news', '안녕');
        }, 3000)

        //클라이언트가 메시지를 전송하면
        socket.on('message', (data) => {
            //모든 클라이언트에게 메시지 전송
            io.sockets.emit('message', data);
        });

    })
};

2)websocket.html 파일 수정
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>웹 소켓</title>

        <!-- 모바일 웹 페이지 생성 시 옵션 설정-->
        <meta name="viewport" 
            content="width=device-width, initial-scale=1" />

        <!-- jquery mobile 설정-->
        <link rel = "stylesheet" 
        href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css"/>

        <!-- jquery mobile은 기본적으로 single page application 을 제작
        내부 코드는 ajax로 동작-->
        <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
        <script 
        src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js">
        </script>
        <!--소켓 설정-->
        <script src="/socket.io/socket.io.js"></script>
        <script>
            $(document).ready(function(){
                //웹 소켓 생성
                var socket = io.connect('http://localhost:8001');

                //소켓 서버로부터 message 이벤트가 오면
                socket.on('message', function(data){
                    //받은 메시지를 이용해서 출력할 내용을 생성
                    var output = '';
                    output += '<li>';
                    output += '<h3>' + data.name + '</h3>';
                    output += '<p>' + data.message + '</p>';
                    output += '<p>' + data.date + '</p>';
                    output += '</li>';

                    //메시지 출력
                    $(output).prependTo('#content');
                    $('#content').listview('refresh');
                });

                //버튼 눌렀을 때 메시지 전송
                $('button').click(function(){
                    socket.emit('message', {
                        name:$('#name').val(),
                        message:$('#message').val(),
                        date:new Date().toUTCString()
                    });
                    $('#message').val('');
                })
            })
        </script>


    </head>
    <body>
        <div data-role = 'page'>
            <div data-role = 'header'>
                <h1>socket.io chatting</h1>
            </div>
            <div data-role='content'>
                <h3>별명</h3>
                <input id='name' />
                <a data-role="button" href="#chatpage">채팅 시작</a>
            </div>
        </div>

        <div data-role = 'page' id="chatpage">
            <div data-role="header">
                <h1>socket.io chatting</h1>
            </div>
            <div data-role="content">
                <input id="message" />
                <button>전송</button>
                <ul id="content" data-role="listview" data-inset="true">
                    
                </ul>
            </div>
        </div>
    </body>

    
</html>

7.전자 칠판 구현
1)프로젝트에 public 디렉토리를 생성 - 정적인 데이터 저장이 목적

2)websocket.html 파일 수정
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>웹 소켓</title>

        <!-- jquery mobile은 기본적으로 single page application 을 제작
        내부 코드는 ajax로 동작-->
        <script src="http://code.jquery.com/jquery-1.8.2.min.js">

        </script>
        <script src="js/board.js"></script>
        <link rel = 'stylesheet' href="stylesheet/style.css">

    </head>
    <body>
        <canvas id="cv" width="860px" height="645px"></canvas>
    </body>

</html>



3)public 디렉토리에 images 디렉토리르 생성하고 blackboard.jpg 파일을 추가

4)public 디렉토리에 stylesheet 디렉토리를 생성하고 style.css 파일을 추가한 후 작성
body{
    margin:0px;
}

#cv{
    width:860px;
    height:645px;
    background-image: url('../images/blackboard.jpg');
}

5)websocket.html 파일을 수정
=>캔버스 아래에 메뉴 와 버튼을 추가
<div class="menu"></div>
<button id="clear">전체 삭제</button>

7)style.css 파일의 내용을 수정
body{
    margin:0px;
}

#cv{
    width:860px;
    height:645px;
    background-image: url('../images/blackboard.jpg');
    float:left;
}

.menu{
    float:left;
}

button{
    width:100px;
    height:50px;
}

6)public 디렉토리에 js 디렉토리를 생성하고 board.js 파일을 추가한 후 작성
var ctx;
$(function(){
    ctx = $('#cv').get(0).getContext('2d');
  
    $('#cv').bind('mousedown', draw.start);
    $('#cv').bind('mousemove', draw.move);
    $('#cv').bind('mouseup', draw.end);
    $('#clear').bind('click', draw.clear);
    
    //기본 설정
    shape.setShape()
})

var shape = {
    color:'white',
    width:3,
    setShape : function(color, width){
        if(color != null){
            this.color = color;
        }
        if(width != null){
            this.width = width;
        }
        ctx.strokeStyle = this.color;
        ctx.lineWidth = this.width;
    }
}

var draw = {
    drawing:null,
    start:function(e){
        ctx.beginPath();
        ctx.moveTo(e.pageX, e.pageY);
        this.drawing = true;
    },
    move:function(e){
        if(this.drawing){
            ctx.lineTo(e.pageX, e.pageY);
            ctx.stroke();
        }
    },
    end:function(e){
        this.drawing = false;
    },

    clear:function(){
        ctx.clearRect(0, 0, cv.width, cv.height);
    }
}

7)현재까지 작성한 후 실행해서 마우스로 선이 그려지고 버튼을 누르면 삭제되는지 확인

8)websocket.html 파일의 메뉴 영역에 색상 변경, 펜 두께, 펜 모양 메뉴를 추가
 <div class="menu">
            <button id="clear">전체 삭제</button>
            <fieldset>
                <legend>색상 변경</legend>
                <select id="pen_color"></select>
            </fieldset>
            <fieldset>
                <legend>두께</legend>
                <select id="pen_width"></select>
            </fieldset>
            <fieldset id="pen_shape">
                <legend>모양</legend>
            </fieldset>
        </div>

9)style.css 파일에 추가한 DOM 의 style 설정 코드 추가
#cv_pen{
    width:100px;
    height:50px;
    float:left;
    background-image: url('images/blackboard.jpg');
}
fieldset{
    width:100px;
    height:60px;
    float:left;
}
#pen_shape{
    position: absolute;
    top:10px;
    left:700px;
    color:white;
}





    'Node.js' 카테고리의 다른 글
    • [Node.js] 2022/05/17 : Node_Socket2, UDP, TCP
    • [MongoDB] Day 09
    • [Node.js] Day 08: 데이터 가져와서 출력하기2
    • [Node.js] Day 07: 데이터 가져와서 출력하기

    티스토리툴바