Go 기반 WebSocket 통신

개시하다

  • 저는 최근에 Real World HTTP 버전 2 프로토콜의 업그레이드 항목을 읽었습니다. 현재 프로토콜의 업그레이드는 웹소켓 전용이기 때문에 복습을 위해 Go가 웹소켓 통신을 하는 서버를 설치했습니다.
  • WebSocket 정보

  • 웹소켓은 클라이언트와 서버 간에 비용이 적은 양방향 통신을 하는 프로토콜로 예를 들면 채팅 앱을 들 수 있다.규격RFC 6455, The WebSocket Protocol에 대한 구체적인 설명이 있다.비공식적인 문서지만 존재한다일본어로 번역된 문서.
  • 웹소켓을 통한 양방향 통신을 위해 앞에서 말한 바와 같이 HTTP 프로토콜 업그레이드 기능을 먼저 사용한다.구체적으로 클라이언트가 서버에 다음과 같이 요청합니다.
  • GET /ws HTTP/1.1
    Host: server.localhost
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://localhost
    Sec-WebSocket-Version: 13
    
  • Upgrade,Connection 헤더 부여를 통해 서버에 사용된 프로토콜이 HTTP에서 WebSocket으로 업그레이드되었음을 알립니다.Sec-WebSocket-Key 헤드는 특정 클라이언트와의 연결 여부를 판정하는 데 사용되며 사칭을 방지할 수 있다.Sec-WebSocket-Version 헤더에 대해 현재 버전의 13을 설정합니다.
  • 클라이언트가 요청을 실행하면 서버에서 다음 응답을 반환합니다.
  • HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    
  • Sec-WebSocket-Accept 헤더의 값과 Sec-WebSocket-Key 헤더의 값이 대응한다.이후 WebSocket을 사용하여 클라이언트 서버 간 양방향 통신이 가능합니다.
  • 이루어지다

  • 이번에는 Go, HTML+JavaScript로 서버 측의 설치를 실시하였다.파일 구성은 다음과 같습니다.
  • $ tree
    .
    ├── main.go
    ├── public
    │   ├── index.html
    │   └── main.js
    ..
    

    서버 측 설치

  • 먼저 서버 측면의 설치는 다음과 같습니다.이번 Go 버전1.15.5은 웹 프레임워크Echo에서 사용됩니다.버전은 v4.2.1입니다.
  • main.go
    package main
    
    import (
    	"fmt"
    	"github.com/labstack/echo/v4"
    	"github.com/labstack/echo/v4/middleware"
    	"golang.org/x/net/websocket"
    )
    
    func handleWebSocket(c echo.Context) error {
    	websocket.Handler(func(ws *websocket.Conn) {
    		defer ws.Close()
    
    		// 初回のメッセージを送信
    		err := websocket.Message.Send(ws, "Server: Hello, Client!")
    		if err != nil {
    			c.Logger().Error(err)
    		}
    
    		for {
    			// Client からのメッセージを読み込む
    			msg := ""
    			err = websocket.Message.Receive(ws, &msg)
    			if err != nil {
    				c.Logger().Error(err)
    			}
    
    			// Client からのメッセージを元に返すメッセージを作成し送信する
    			err := websocket.Message.Send(ws, fmt.Sprintf("Server: \"%s\" received!", msg))
    			if err != nil {
    				c.Logger().Error(err)
    			}
    		}
    	}).ServeHTTP(c.Response(), c.Request())
    	return nil
    }
    
    func main() {
    	e := echo.New()
    	e.Use(middleware.Logger())
    	e.Static("/", "public")
    	e.GET("/ws", handleWebSocket)
    	e.Logger.Fatal(e.Start(":8080"))
    }
    

    클라이언트 설치

  • 다음은 클라이언트의 설치 지침입니다.먼저 HTML 코드입니다.
  • index.html
    <!doctype html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8">
        <title>WebSocket</title>
        <script src="main.js"></script>
    </head>
    
    <body>
        <p id="output"></p>
        <input type="text" id="input"></p>
        <p><input type="submit" class="btn" value="送信"></p>
    </body>
    
    </html>
    
  • 다음은 JavaScript 코드입니다.
  • main.js
    document.addEventListener('DOMContentLoaded', () => {
        let loc = window.location;
        let uri = 'ws:';
        if (loc.protocol === 'https:') {
            uri = 'wss:';
        }
        uri += '//' + loc.host;
        uri += loc.pathname + 'ws';
    
        const ws = new WebSocket(uri)
        ws.onopen = function() {
            console.log('Connected')
        }
    
        ws.onmessage = function(evt) {
            let out = document.getElementById('output');
            out.innerHTML += evt.data + '<br>';
        }
    
        const btn = document.querySelector('.btn')
        btn.addEventListener('click', () => {
            ws.send(document.getElementById('input').value)
        })
    });
    

    확인

  • 실제 부팅 서버의 작동 확인이 이루어졌습니다.브라우저http://localhost:8080에 액세스하여 텍스트 상자에 문자를 입력하고 [보내기] 버튼을 누르면 서버에서 입력한 문자열이 포함된 메시지가 반환됩니다.동작 확인을 위한 이미지는 다음과 같습니다.
  • 좋은 웹페이지 즐겨찾기