Как использовать сегодняGoсоздание языкаWebSocketуслуги, о которых кратко рассказано в первых двух частях статьиWebSocketсоглашение иGoКак создается стандартная библиотекаWebSocketСлужить. В третьей части практической части мы использовалиgorilla/websocketБиблиотеки помогают нам быстро строитьWebSocketСервисы, которые помогают инкапсулировать использованиеGoРеализация стандартной библиотекиWebSocketБазовая логика, связанная с услугами, позволяет нам освободиться от громоздкого базового кода и быстро строить в соответствии с потребностями бизнеса.WebSocketСлужить.
Исходный код для каждой статьи серии Web Programming сыграл соответствующую версию пакета для вашей справки. Отвечать
gohttp10Получить исходный код этой статьи
Введение в веб-сокеты
WebSocketпротокол связи через единыйTCPСоединение обеспечивает полнодуплексный канал связи. иHTTPпо сравнению с,WebSocketВам не нужно отправлять запрос, чтобы получить ответ. Это позволяет двунаправленный поток данных, так что вы просто ждете сообщения от сервера. когдаWebsocketОн отправит вам сообщение, когда он будет доступен. Для сервисов, требующих непрерывного обмена данными (таких как мессенджеры, онлайн-игры и торговые системы в реальном времени),WebSocketявляется хорошим решением.WebSocketСоединение запрашивается браузером, отвечает сервером, и соединение устанавливается. Этот процесс широко известен как рукопожатие. WebSocketСпециальные заголовки в : требуют только одного рукопожатия между браузером и сервером для установления соединения, которое будет оставаться активным в течение всего времени существования.WebSocketрешать многие в режиме реального времениWebразработанные головоломки, и с традиционнымиHTTPПо сравнению с, имеет много преимуществ:
- Облегченные заголовки сокращают накладные расходы на передачу данных.
- не замужем
WebКлиенту нужен только одинTCPсоединять. -
WebSocketСервер может отправлять данные наWebклиент.
Протокол WebSocket относительно прост в реализации. оно используетHTTPПротокол выполняет начальное рукопожатие. После успешного рукопожатия соединение устанавливается.WebSocketглавное использовать оригиналTCPЧтение/запись данных.
Запрос клиента выглядит так:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
Вот ответ сервера:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Как создать приложение WebSocket в Go
На основе встроенного языка Gonet/httpПитание библиотекиWebSocketсервер, вам нужно:
- инициировать рукопожатие
- Получение фрейма данных от клиента
- Отправить фрейм данных клиенту
- тесное рукопожатие
инициировать рукопожатие
Во-первых, давайте создадимWebSocketКонечная точкаHTTPОбработчик:
// HTTP server with WebSocket endpoint
func Server() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
ws, err := NewHandler(w, r)
if err != nil {
// handle error
}
if err = ws.Handshake(); err != nil {
// handle error
}
…
затем инициализируйтеWebSocketструктура.
Первоначальный запрос на рукопожатие всегда исходит от клиента. сервер подтвержденWebSocketПосле запроса он должен ответить рукопожатием.
Помните, что вы не можете использоватьhttp.ResponseWriterНапишите ответ, потому что, как только он начинает отправлять ответ, он закрывает свой базовыйTCPподключение (этоHTTPЭто определяется механизмом работы протокола, и соединение закрывается после отправки ответа).
Поэтому вам нужно использоватьHTTPугнать(hijack). Угоняя, можно завладеть базовымTCPобработчик соединения иbufio.Writer. Это позволяет отключитьTCPЧтение и запись данных при подключении.
// NewHandler initializes a new handler
func NewHandler(w http.ResponseWriter, req *http.Request) (*WS, error) {
hj, ok := w.(http.Hijacker)
if !ok {
// handle error
} .....
}
Для завершения рукопожатия сервер должен ответить соответствующими заголовками.
// Handshake creates a handshake header
func (ws *WS) Handshake() error {
hash := func(key string) string {
h := sha1.New()
h.Write([]byte(key))
h.Write([]byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}(ws.header.Get("Sec-WebSocket-Key"))
.....
}
Инициировано клиентомWebSocketиспользуется для запросов на подключениеSec-WebSocket-keyОн генерируется случайным образом и кодируется Base64. После принятия запроса серверу необходимо привязать к этому ключу фиксированную строку. Предположим, что секретный ключx3JJHMbDL1EzLkh9GBhXDw==. В этом примере вы можете использоватьSHA-1Вычислите двоичное значение и используйтеBase64закодировать его. получитьHSmrc0sMlYUkAGmm5OPpG2HaGWk=. затем используйте его какSec-WebSocket-AcceptЗначение заголовка ответа.
передать кадр данных
После успешного завершения рукопожатия ваше приложение может считывать данные с клиента или записывать данные на него.Спецификация веб-сокетаОпределяет определенный формат кадра, используемый между клиентом и сервером. Вот битовая структура кадра:
Используйте следующий код для декодирования полезной нагрузки клиента:
// Recv receives data and returns a Frame
func (ws *WS) Recv() (frame Frame, _ error) {
frame = Frame{}
head, err := ws.read(2)
if err != nil {
// handle error
}
Эти строки кода, в свою очередь, позволяют кодировать данные:
// Send sends a Frame
func (ws *WS) Send(fr Frame) error {
// make a slice of bytes of length 2
data := make([]byte, 2)
// Save fragmentation & opcode information in the first byte
data[0] = 0x80 | fr.Opcode
if fr.IsFragment {
data[0] &= 0x7F
}
.....
тесное рукопожатие
Рукопожатие закрывается, когда одна из сторон отправляет кадр закрытия со статусом «закрыто» в качестве полезной нагрузки. При желании сторона, отправляющая кадр закрытия, может отправить причину закрытия в полезной нагрузке. Если закрытие было инициировано клиентом, сервер должен ответить соответствующим кадром закрытия.
// Close sends a close frame and closes the TCP connection
func (ws *Ws) Close() error {
f := Frame{}
f.Opcode = 8
f.Length = 2
f.Payload = make([]byte, 2)
binary.BigEndian.PutUint16(f.Payload, ws.status)
if err := ws.Send(f); err != nil {
return err
}
return ws.conn.Close()
}
Быстро создавайте с помощью сторонних библиотекWebSocketСлужить
Из предыдущих глав видно, чтоGoавтономныйnet/httpреализация библиотекиWebSocketСлужба все еще слишком сложна. К счастью, пар много.WebSocketХорошо поддерживаемые сторонние библиотеки могут значительно сократить нашу низкоуровневую работу по написанию кода. Здесь мы используемgorilla web toolkitЕще одна библиотека для семьиgorilla/websocketреализовать нашиWebSocketобслуживание, построение простогоEchoСлужить(echoЭто означает эхо, которое отправляет клиент, а сервер отправляет сообщение обратно клиенту).
мы вhttp_demoПроектhandlerСоздайте новый в каталогеwsподкаталоги для храненияWebSocketОбработчик запроса, соответствующий маршруту, связанному со службой.
Добавьте два маршрута:
-
/ws/echoechoМаршрут для службы WebSocket приложения. -
/ws/echo_displayechoМаршрут к клиентской странице приложения.
Создайте сервер WebSocket
// handler/ws/echo.go
package ws
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func EchoMessage(w http.ResponseWriter, r *http.Request) {
conn, _ := upgrader.Upgrade(w, r, nil) // 实际应用时记得做错误处理
for {
// 读取客户端的消息
msgType, msg, err := conn.ReadMessage()
if err != nil {
return
}
// 把消息打印到标准输出
fmt.Printf("%s sent: %s\n", conn.RemoteAddr(), string(msg))
// 把消息写回客户端,完成回音
if err = conn.WriteMessage(msgType, msg); err != nil {
return
}
}
}
-
connТип переменной*websocket.Conn,websocket.Connтип, используемый для представленияWebSocketсоединять. серверное приложение отHTTPвызов обработчика запросаUpgrader.Upgradeспособ получить*websocket.Conn -
вызов подключен
WriteMessageиReadMessageспособы отправки и получения сообщений. вышеmsgПосле его получения он отправляется обратно клиенту ниже.msgТип[]byte.
Создайте клиент WebSocket
Обработчик запроса, соответствующий маршрутизации страницы внешнего интерфейса, выглядит следующим образом и возвращает напрямуюviews/websockets.htmlПросто отобразите страницу в браузере.
// handler/ws/echo_display.go
package ws
import "net/http"
func DisplayEcho(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "views/websockets.html")
}
websocket.htmlгде нам нужно использоватьJavaScriptсоединятьWebScoketСервис отправляет и принимает сообщения, выложу только из соображений экономии места.JSКод готов, полный код можно скачать с официального аккаунта через пароль в этом разделе.
<form>
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
</form>
...
<script>
var input = document.getElementById("input");
var output = document.getElementById("output");
var socket = new WebSocket("ws://localhost:8000/ws/echo");
socket.onopen = function () {
output.innerHTML += "Status: Connected\n";
};
socket.onmessage = function (e) {
output.innerHTML += "Server: " + e.data + "\n";
};
function send() {
socket.send(input.value);
input.value = "";
}
</script>
...
зарегистрировать маршрут
После того, как серверная и клиентская программы готовы, регистрируем маршруты и соответствующие обработчики запросов для них по заранее согласованному пути:
// router/router.go
func RegisterRoutes(r *mux.Router) {
...
wsRouter := r.PathPrefix("/ws").Subrouter()
wsRouter.HandleFunc("/echo", ws.EchoMessage)
wsRouter.HandleFunc("/echo_display", ws.DisplayEcho)
}
Тестовая проверка
Доступ после перезапуска службыhttp://localhost:8000/ws/echo_display, любое сообщение, введенное в поле ввода, может быть снова отображено в браузере.
Сервер распечатывает полученное сообщение на терминале, а затем вызываетwriteMessageСообщение отправляется обратно клиенту, и запись можно просмотреть в терминале.
Суммировать
WebSocketОн очень широко используется в приложениях, которые сейчас часто обновляются.WebSocketПрограммирование также является важным навыком, которым нам необходимо овладеть. Практические упражнения из статьи немного проще, а проверок на ошибки и безопасности нет. В основном, чтобы прояснить общий процесс. оgorilla/websocketДля получения более подробной информации вам необходимо проверить официальную документацию при его использовании.
Ссылка на ссылку:
Arran Ascension.com/blog/how-to…
woohoo.gorilla toolkit.org/pkg/Web sock…
Предыдущий отзыв
Глубокое погружение в написание HTTP-серверов в Go
Подробное руководство по применению библиотеки шаблонов Go
Создайте статический файловый сервер в Go
Внедрение управления сеансами на стороне клиента с помощью SecureCookie