您好,登錄后才能下訂單哦!
瀏覽器客戶端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket測試程序 </title>
<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.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function (ev) {
if (ws) {
return false;
}
ws = new WebSocket("ws://127.0.0.1:8888/ws");
ws.onopen = function (ev) {
print("連接成功");
};
ws.onclose = function (ev) {
print("連接關(guān)閉");
};
ws.onerror = function (ev) {
print("發(fā)生錯(cuò)誤 " + ev.data)
};
ws.onmessage = function (ev1) {
print("服務(wù)端消息: " + ev1.data)
};
return false
};
document.getElementById("send").onclick = function (ev) {
if (!ws) {
return false
}
if (input.value !== "") {
ws.send(input.value)
} else {
print("發(fā)送內(nèi)容不能為空")
}
};
document.getElementById("close").onclick = function (ev) {
if (ws) {
ws.close()
}
}
})
</script>
</head>
<body>
<div>
<br>
websocket測試程序,消息又客戶端發(fā)送到server然后原封不動(dòng)的返回,server使用go實(shí)現(xiàn)
<br>
<br>
<br>
<input type="button" value="連接" id="open">
<input placeholder="輸入要發(fā)送的消息..." id="input">
<input type="button" value="發(fā)送" id="send">
<input type="button" value="關(guān)閉" id="close">
</div>
<div id="output">
</div>
</body>
</html>
版本一:
package main
import (
"net/http"
"github.com/gorilla/websocket"
)
var (
// http升級websocket協(xié)議的配置
upgrader = websocket.Upgrader{
// 允許所有CORS跨域請求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
func wsHandler(writer http.ResponseWriter, request *http.Request) {
var (
conn *websocket.Conn
err error
//msgType int
data []byte
)
//完成握手應(yīng)答
if conn, err = upgrader.Upgrade(writer, request, nil); err != nil {
return
}
//數(shù)據(jù)收發(fā)
for {
//數(shù)據(jù)類型有text、binary,此處選text
if _, data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
}
ERR:
conn.Close()
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("127.0.0.1:8888", nil)
}
版本一未做優(yōu)化
版本二:
connection.go:
package impl
import (
"github.com/gorilla/websocket"
"sync"
"errors"
)
type Connection struct {
wsConn *websocket.Conn // 底層websocket
inChan chan []byte // 讀隊(duì)列
outChan chan []byte // 寫隊(duì)列
closeChan chan byte // 關(guān)閉通知
isClosed bool
mutex sync.Mutex // 避免重復(fù)關(guān)閉管道
}
//封裝websocket長連接
func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) {
conn = &Connection{
wsConn: wsConn,
inChan: make(chan []byte, 1000),
outChan: make(chan []byte, 1000),
closeChan: make(chan byte, 1),
}
//啟動(dòng)讀協(xié)程
go conn.readLoop()
//啟動(dòng)寫協(xié)程
go conn.writeLoop()
return
}
func (conn *Connection) ReadMessage() (data []byte, err error) {
select {
case data = <- conn.inChan:
case <- conn.closeChan:
err = errors.New("connection is closed")
}
return
}
func (conn *Connection) WriteMessage(data []byte) (err error) {
select {
case conn.outChan <- data:
case <- conn.closeChan:
err = errors.New("connection is closed")
}
return
}
func (conn *Connection) Close() {
// wsConn.Close是線程安全的,可重入的(可以多次關(guān)閉)
conn.wsConn.Close()
//一個(gè)chan只能關(guān)閉一次(所以要保證這行代碼只執(zhí)行一次)
conn.mutex.Lock()
if !conn.isClosed{
close(conn.closeChan)
conn.isClosed = true
}
conn.mutex.Unlock()
}
//內(nèi)部實(shí)現(xiàn)
func (conn *Connection) readLoop() {
var (
data []byte
err error
)
//不停的讀
for {
if _, data, err = conn.wsConn.ReadMessage(); err != nil {
goto ERR
}
//阻塞在這里,等待inChan有空閑位置
select {
case conn.inChan <- data:
case <- conn.closeChan: //當(dāng)closeChan被關(guān)閉就進(jìn)入這個(gè)分支
goto ERR
}
}
ERR:
conn.Close()
}
func (conn *Connection) writeLoop() {
var (
data []byte
err error
)
//不停寫
for {
select {
case data = <-conn.outChan:
case <- conn.closeChan:
goto ERR
}
if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil{
goto ERR
}
}
ERR:
conn.Close()
}
server.go:
package main
import (
"net/http"
"github.com/gorilla/websocket"
"./impl"
"time"
)
var (
// http升級websocket協(xié)議的配置
upgrader = websocket.Upgrader{
// 允許所有CORS跨域請求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
func wsHandler(writer http.ResponseWriter, request *http.Request) {
var (
wsConn *websocket.Conn
err error
//msgType int
data []byte
conn *impl.Connection
)
//完成握手應(yīng)答
if wsConn, err = upgrader.Upgrade(writer, request, nil); err != nil {
return
}
if conn, err = impl.InitConnection(wsConn); err != nil {
goto ERR
}
// 不停發(fā)送心跳信息
go func() {
var (
err error
)
for {
if err = conn.WriteMessage([]byte("heartbeat")); err != nil {
return
}
time.Sleep(time.Second)
}
}()
for {
if data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(data); err != nil {
goto ERR
}
}
ERR:
// 關(guān)閉連接的操作
conn.Close()
/*//數(shù)據(jù)收發(fā)
for {
//數(shù)據(jù)類型有text、binary,此處選text
if _, data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
}
ERR:
conn.Close()*/
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("127.0.0.1:8888", nil)
}
做了封裝,線程安全
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。