当前位置:网站首页>[Go WebSocket] Your first Go WebSocket server: echo server

[Go WebSocket] Your first Go WebSocket server: echo server

2022-08-10 19:03:00 51CTO

大家好,我是公众号「线下聚会游戏」作者,开发了​ ​《联机桌游合集》​​​,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏.其中的核心技术就是WebSocket,快来关注专栏​ ​《Go WebSocket》​​,跟我一起学习吧!

背景

上篇文章:​ ​《为什么我选用Go重构Python版本的WebSocket服务?》​​,介绍了我的目标.

从这篇文章开始,我们进入实战,正式介绍Go WebSocket框架.

还没学过Go,You first look at what?

建议你花1天时间,看一下Go的原理简介、基础语法.Any tutorial can be,Well-known tutorial of the.

At least to understand:各种数据类型,控制流(for、if等)写法,弄懂channel和goroutine,如何加锁.

Must you writegoroutine和channel试一下,Understand the basic grammar.

此外,To know more about the use of the commonly used package,包括fmt、net/http.

技术选型

Face yourself don't familiar with the language and not familiar with the framework of,How to do technology selection?

I tell you a little skill,直接在Github上搜索,看StarMost of the warehouse,就可以啦~

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_Go

看吧,We went​​gorilla/websocket​​,starSignificant differences in behind the back of a few.It is no good of tangled,Determined to use it.

新建项目

在使用GoLand时,新建Go Project会有2个选项:

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_客户端_02

We choose the first can be.

如果你没有GoLand,Can also manually create folder,在里面新建文件​​go.mod​​(I am using the latest stable version1.18)

      
      
module echo

go 1.18
  • 1.
  • 2.
  • 3.

安装依赖

      
      
go get github.com/gorilla/websocket
  • 1.

拷贝echo代码

把​​gorilla/websocket​​的官方demo拷贝过来即可,我们慢慢分析:

You just need to copy a file,命名为server.go即可.

先尝试运行

      
      
go run server.go
  • 1.

然后浏览器打开 ​ ​localhost:8080​​就可以了~

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_客户端_03

  • 点击「Open」建立WebSocket连接
  • Edit the text,按Send发送一个消息给服务器
  • Server immediately respond to an identical message,这就是echo
  • 点击「Close」关闭连接,之后无法Send

You all the operations will be record on the page:

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_后端_04

当然,Also can open the developer tools,查看WebSocket连接,As you can seeHttp请求那样.This article has taught you how to useChromeThe developer of the panel caught:​ ​《遇到表格,手动翻页太麻烦?我教你写脚本,一页展示所有数据》​​.

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_Go_05

[Go WebSocket] 你的第一个Go WebSocket服务: echo server_html_06

代码解读

引入依赖

      
      
package main

import (
"flag"
"html/template"
"log"
"net/http"

"github.com/gorilla/websocket"
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

定义服务地址

      
      
var addr = flag . String( "addr", "localhost:8080", "http service address")
  • 1.

This is to define the server startup services address,​​flag​​Package for processing command line arguments.Which means the service address can be through the command line parameter dynamic modification.

For example you could start:​​go run server.go -addr="localhost:8888"​

The browser should open​​localhost:8888​​来访问.

Of course if you don't need to order parameter intoaddr,Completely can delete this line,改为:

      
      
const addr = "localhost:8080"
  • 1.

同时,还要把main函数中,最后一行改成:(删掉了addr前面的星号)

      
      
log . Fatal( http . ListenAndServe( addr, nil))
  • 1.

同时,把​​flag​​Relevant rows to delete.(开头的import和main函数中的Parse)

主函数

We first introduce the main function(Although the main function definition in the back).But the main function has the role of a route,Distribute the request.我们先介绍一下,方便后续理解.

      
      
func main() {
flag . Parse()
log . SetFlags( 0)
http . HandleFunc( "/echo", echo)
http . HandleFunc( "/", home)
log . Fatal( http . ListenAndServe( * addr, nil))
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

我们通过​​net/http​​​提供的能力,使用​​ListenAndServe​​启动了Http/WebSocket服务.

其中,我们注册了2个处理函数,一个是针对path为​​/echo​​​的,这是用echo函数处理.另一个是针对path为​​/​​的,这是用home函数处理.

When you use the browser to directly access​​localhost:8080​​​时,是用了​​home​​函数处理,一个http请求,获得一个html文件,在浏览器展示.

当你在JS中写​​new WebSocket('wss://localhost:8080/echo')​​​时,是用了​​echo​​函数处理,一个WebSocket连接.

We then introduce the2个函数.

定义echo服务(WebSocket协议)

      
      
var upgrader = websocket . Upgrader{} // use default options

func echo( w http . ResponseWriter, r * http . Request) {
c, err : = upgrader . Upgrade( w, r, nil)
if err != nil {
log . Print( "upgrade:", err)
return
}
defer c . Close()
for {
mt, message, err : = c . ReadMessage()
if err != nil {
log . Println( "read:", err)
break
}
log . Printf( "recv: %s", message)
err = c . WriteMessage( mt, message)
if err != nil {
log . Println( "write:", err)
break
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

当客户端使用​​new WebSocket('ws://localhost:8080/echo')​​​建立时,就会开启一个goroutine,执行类似​​go echo(w, r)​​的操作.只要这个WebSocket没有关闭,那么这个goroutine就会一直存在.

如果客户端关闭了WebSocket,Or server-side thisgoroutine执行结束了(因为有​​defer c.Close()​​),都会导致WebSocket断掉.It is reasonable and correct,Don't write so there will be a problem.

这段​​echo​​​函数很简单,不断循环,读取消息​​c.ReadMessage()​​​,如果没消息,Will be suspended,Until there is a message.After the news,通过​​log​​​打印收到的消息,并且通过​​c.WriteMessage(mt, message)​​The output message to the client.

这里​​mt​​是消息类型Message Type,有2种:二进制消息、文本消息.

When the server after the output,Waiting for the client input again.

可以看到,At present is an ordered linear service:收一个、发一个、收一个、发一个.If the client sent at the same time100个,Then the server will be in accordance with this100Read a message in the order of,And in the original orderecho回去.处理完一个、To receive the next.Is the guarantee to send and receive the order of sex(The server to send the order must be compatible with the order of a closed),The downside is that can't concurrent read,性能有影响,If each processing received messages to deal with for a long time,The message behind the block、Backlog in memory.

下一篇我们会介绍chat server,避免了这种问题. 敬请期待,可以先关注专栏、关注我噢~.

Html文本服务(Http协议)

      
      
func home( w http . ResponseWriter, r * http . Request) {
homeTemplate . Execute( w, "ws://" + r . Host + "/echo")
}

var homeTemplate = template . Must( template . New( "") . Parse( `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.textContent = message;
output.appendChild(d);
output.scroll(0, output.scrollHeight);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div>
</td></tr></table>
</body>
</html>
`))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.

这个服务比较简单,就是Html模板渲染.

Pay attention to a template variable:​​"ws://"+r.Host+"/echo"​​,This template variables are actually don't need.

HTML中可以直接这么写:把​​ws = new WebSocket("{{.}}");​​​改为​​ws = new WebSocket('ws://' + window.location.host + '/echo');​

写在最后

我是HullQin,独立开发了​ ​《联机桌游合集》​​​,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告.还独立开发了​ ​《合成大西瓜重制版》​​​.还开发了​ ​《Dice Crush》​​​参加了某个游戏制作比赛.喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:​ ​《教你做小游戏》​​​、​ ​《极致用户体验》​​.

本文正在参加​ ​技术专题18期-聊聊Go语言框架​​.

原网站

版权声明
本文为[51CTO]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/222/202208101833445024.html