Golang構(gòu)建WebSocket服務(wù)器和客戶端的示例詳解
簡介
本教程將教你如何使用Go語言構(gòu)建WebSocket服務(wù)器和客戶端,以實(shí)現(xiàn)雙向 通信。我們將介紹如何創(chuàng)建一個WebSocket服務(wù)器,用于接收來自客戶端的消息,以及如何創(chuàng)建一個WebSocket客戶端,用于與服務(wù)器通信。我們還將介紹一個簡單的HTML頁面,演示如何在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。
操作
1.創(chuàng)建一個文件夾.
2.使用GO mod初始化go mod init your_module_name
3.創(chuàng)建文件
4.go get github.com/gorilla/websocket
WebSocket 服務(wù)器
首先,我們將創(chuàng)建一個WebSocket服務(wù)器,用于接收來自客戶端的消息。我們使用github.com/gorilla/websocket包來處理WebSocket連接。
WsServer.go
// 導(dǎo)入必要的包
import (
"fmt"
"net"
"net/http"
"time"
"github.com/gorilla/websocket"
)
// 創(chuàng)建WsServer結(jié)構(gòu)體
type WsServer struct {
listener net.Listener
addr string
upgrade *websocket.Upgrader
}
// 初始化WsServer
func NewWsServer() *WsServer {
ws := new(WsServer)
ws.addr = "0.0.0.0:10215"
ws.upgrade = &websocket.Upgrader{
ReadBufferSize: 4096,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
if r.Method != "GET" {
fmt.Println("method is not GET")
return false
}
if r.URL.Path != "/ws" {
fmt.Println("path error")
return false
}
return true
},
}
return ws
}
// 處理WebSocket連接
func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/ws" {
httpCode := http.StatusInternalServerError
reasePhrase := http.StatusText(httpCode)
fmt.Println("path error ", reasePhrase)
http.Error(w, reasePhrase, httpCode)
return
}
conn, err := self.upgrade.Upgrade(w, r, nil)
if err != nil {
fmt.Println("websocket error:", err)
return
}
fmt.Println("client connect:", conn.RemoteAddr())
go self.connHandle(conn)
}
// 處理WebSocket連接中的消息
func (self *WsServer) connHandle(conn *websocket.Conn) {
defer func() {
conn.Close()
}()
stopCh := make(chan int)
go self.send(conn, stopCh)
for {
conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
_, msg, err := conn.ReadMessage()
if err != nil {
close(stopCh)
if netErr, ok := err.(net.Error); ok {
if netErr.Timeout() {
fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr())
return
}
}
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err)
}
return
}
fmt.Println("Received message:", string(msg))
}
}
// 向客戶端發(fā)送消息
func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) {
self.send10(conn)
for {
select {
case <-stopCh:
fmt.Println("connection closed")
return
case <-time.After(time.Second * 1):
data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
err := conn.WriteMessage(1, []byte(data))
fmt.Println("Sending....")
if err != nil {
fmt.Println("send message failed ", err)
return
}
}
}
}
// 測試一次性發(fā)送10萬條數(shù)據(jù)給客戶端
func (self *WsServer) send10(conn *websocket.Conn) {
for i := 0; i < 100000; i++ {
data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
err := conn.WriteMessage(1, []byte(data))
if err != nil {
fmt.Println("send message failed ", err)
return
}
}
}
// 啟動WebSocket服務(wù)器
func (w *WsServer) Start() (err error) {
w.listener, err = net.Listen("tcp", w.addr)
if err != nil {
fmt.Println("net listen error:", err)
return
}
err = http.Serve(w.listener, w)
if err != nil {
fmt.Println("http serve error:", err)
return
}
return nil
}
func main() {
ws := NewWsServer()
ws.Start()
}
這是WebSocket服務(wù)器的代碼。它創(chuàng)建一個WsServer結(jié)構(gòu)體,用于處理WebSocket連接。服務(wù)器會不斷接收來自客戶端的消息,并發(fā)送一些測試消息。你可以根據(jù)需要進(jìn)行修改和擴(kuò)展。
WebSocket 客戶端
接下來,我們將創(chuàng)建一個WebSocket客戶端,用于與服務(wù)器進(jìn)行通信。我們將使用自定義的WebSocket客戶端實(shí)現(xiàn)。
WsClient.go
package main
import (
"fmt"
"net"
"net/http"
"time"
"github.com/gorilla/websocket"
)
// 創(chuàng)建自定義WebSocket客戶端
type VIL struct{}
type DefaultWebSocket struct {
_host string
_isOpen bool
_bufQueue []interface{}
_bufCap int
_call interface{}
_socket *websocket.Conn
}
// 初始化自定義WebSocket客戶端
func (self *VIL) DefaultWebSocket(host, call) {
_host = host
_isOpen = false
_bufQueue = []interface{}
_bufCap = 100
if call != nil {
_call = call
} else {
_call = {
onConnect: func(e) {
fmt.Println("connect success ", e)
},
onDisconnect: func(e) {
fmt.Println("disconnect ", e)
},
onMsg: func(data) {
// fmt.Println("receive message ", data)
},
}
}
_socket = new(websocket.Conn)
_socket = websocket.DefaultDialer.Dial(_host, nil)
_socket.binaryType = "arraybuffer"
}
// 設(shè)置發(fā)送消息緩存隊(duì)列的容量
func (self *DefaultWebSocket) setBufferCap(cap) {
if cap < 0 {
fmt.Println("parameter value can not be less than 0")
return
}
_bufCap = cap
}
// 發(fā)送消息
func (self *DefaultWebSocket) send(data) {
if _isOpen && _socket != nil {
_socket.send("")
} else {
if len(_bufQueue) < _bufCap {
_bufQueue = append(_bufQueue, data)
}
}
}
// 關(guān)閉WebSocket連接
func (self *DefaultWebSocket) close() {
_socket.Close(1000, "normal")
_isOpen = false
}
// 處理WebSocket連接中的消息
func (self *DefaultWebSocket) handle() {
go func() {
for {
_socket.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
_, message, err := _socket.ReadMessage()
if err != nil {
fmt.Println("Error while reading message:", err)
return
}
_call.onMsg(message)
}
}()
}
// 向客戶端發(fā)送消息
func (self *DefaultWebSocket) sendToServer() {
go func() {
for {
select {
case <-stopCh:
fmt.Println("Interrupted, closing connection...")
err := _socket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
fmt.Println("Error while sending close message:", err)
return
}
select {
case <-done:
case <-time.After(time.Second):
}
return
case <-time.After(time.Second * 1):
data := fmt.Sprintf("Hello WebSocket test from client %v", time.Now().UnixNano())
err := _socket.WriteMessage(1, []byte(data))
if err != nil {
fmt.Println("Error while sending message:", err)
return
}
}
}
}()
}
這是WebSocket客戶端的代碼。它創(chuàng)建了一個DefaultWebSocket結(jié)構(gòu)體,用于處理WebSocket連接??蛻舳藭粩喟l(fā)送消息給服務(wù)器,并處理來自服務(wù)器的消息。你可以根據(jù)需要進(jìn)行修改和擴(kuò)展。
HTML 頁面
最后,我們創(chuàng)建一個簡單的HTML頁面,用于在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。以下是HTML代碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Example</title>
</head>
<body>
</body>
<script type="text/javascript" src="./index.js"></script>
<script>
let counter = 0;
let isConnect = false;
let handler = {
onConnect: function (e) {
isConnect = true;
console.log("handler connect success ", e);
var se = setInterval(function () {
if (isConnect === false) {
clearInterval(se);
}
console.log("setInterval", Date.now());
socket.send("Web browser setInterval");
}, 3000);
},
onDisconnect: function (e) {
isConnect = false;
console.log("handler disconnect ", e);
},
onMsg: function (data) {
counter++;
if (counter >= 2000) {
counter = 0;
console.log("handler receive message ", data);
}
}
};
let socket = new VIL.DefaultWebSocket("ws://127.0.0.1:10215/ws", handler);
</script>
</html>
index.js
let VIL = (function () {
let VIL = {
};
function DefaultWebSocket(host, call) {
let _host = host;
let _isOpen = false;
let _bufQueue = [];
let _bufCap = 100;
let _call = null;
if("undefined" !== typeof call && call !== null){
_call = call
}else{
_call = {
onConnect:function (e) {
console.log("connect success ", e);
},
onDisconnect:function (e) {
console.log("disconnect ", e);
},
onMsg:function (data) {
//console.log("receive message ", data)
}
}
}
let _socket = new WebSocket(_host);
_socket.binaryType = "arraybuffer";
/**
* 設(shè)置發(fā)送消息緩存隊(duì)列的容量
* @param {number} cap
* @constructor
*/
this.setBufferCap = function(cap){
if("number" !== typeof cap ){
console.error("parameter type is not number ");
return ;
}
if(cap < 0){
console.error("parameter value can not less then 0");
return ;
}
_bufCap = cap;
};
/**
* 發(fā)送消息
* @param {string | ArrayBuffer } data
* @constructor
*/
this.send = function(data){
if(_isOpen && _socket){
_socket.send("");
}else{
if (_bufQueue < _bufCap){
_bufQueue.push(data);
}
}
};
this.close = function(){
_socket.close(1000, "normal");
};
_socket.onopen = function(even){
_isOpen = true;
_call.onConnect(even);
while (_bufQueue > 0){
_socket.send(_bufQueue.shift());
}
};
_socket.onmessage = function(e){
let data = e.data;
_call.onMsg(data);
};
/**
* 收到關(guān)閉連接
* @param even
*/
_socket.onclose = function(even){
_isOpen = false;
_call.onDisconnect({host:_host, event:even});
};
/**
* 收到錯誤
* @param err
*/
_socket.onerror = function(err){
_isOpen = false;
_call.onDisconnect({host:_host, event:err});
};
}
try{
VIL.EngineSocket = DefaultWebSocket ;
}catch (e) {
console.error("VILEngine error ", e);
}
return VIL;
})();這個HTML頁面包括一個WebSocket客戶端的JavaScript代碼,它會連接到我們的服務(wù)器,并處理連接、斷開連接和消息。在瀏覽器中打開這個頁面后,你將能夠與服務(wù)器進(jìn)行WebSocket通信。
總結(jié)
在本教程中,我們創(chuàng)建了一個WebSocket服務(wù)器和客戶端,并演示了如何在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。WebSocket提供了一種強(qiáng)大的方式來實(shí)現(xiàn)實(shí)時(shí)雙向通信,適用于各種應(yīng)用程序,如在線聊天、實(shí)時(shí)數(shù)據(jù)更新等。你可以根據(jù)需要進(jìn)一步擴(kuò)展這些示例,以構(gòu)建更復(fù)雜的WebSocket應(yīng)用程序。
以上就是Golang構(gòu)建WebSocket服務(wù)器和客戶端的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于go WebSocket的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言rune與字符串轉(zhuǎn)換的密切關(guān)系解析
這篇文章主要為大家介紹了Go語言rune與字符串轉(zhuǎn)換的密切關(guān)系示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
go?原子操作的方式及實(shí)現(xiàn)原理全面深入解析
這篇文章主要為大家介紹了go?原子操作的方式及實(shí)現(xiàn)原理深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
初探Golang數(shù)據(jù)結(jié)構(gòu)之Slice的使用
在學(xué)習(xí)Go語言時(shí),一直對數(shù)組和切片的使用場景好奇,不明白為什么推薦使用切片來代替數(shù)組,所以本文就來和大家梳理一下Slice切片的相關(guān)知識吧2023-09-09
Go語言設(shè)計(jì)實(shí)現(xiàn)在任務(wù)欄里提醒你喝水的兔子
這篇文章主要為大家介紹了Go語言設(shè)計(jì)實(shí)現(xiàn)在任務(wù)欄里提醒你喝水的兔子示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

