浏览器客户端:
<!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("连接关闭");
};
ws.onerror = function (ev) {
print("发生错误 " + ev.data)
};
ws.onmessage = function (ev1) {
print("服务端消息: " + ev1.data)
};
return false
};
document.getElementById("send").onclick = function (ev) {
if (!ws) {
return false
}
if (input.value !== "") {
ws.send(input.value)
} else {
print("发送内容不能为空")
}
};
document.getElementById("close").onclick = function (ev) {
if (ws) {
ws.close()
}
}
})
</script>
</head>
<body>
<div>
<br>
websocket测试程序,消息又客户端发送到server然后原封不动的返回,server使用go实现
<br>
<br>
<br>
<input type="button" value="连接" id="open">
<input placeholder="输入要发送的消息..." id="input">
<input type="button" value="发送" id="send">
<input type="button" value="关闭" id="close">
</div>
<div id="output">
</div>
</body>
</html>
版本一:
package main
import (
"net/http"
"github.com/gorilla/websocket"
)
var (
// http升级websocket协议的配置
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
)
//完成握手应答
if conn, err = upgrader.Upgrade(writer, request, nil); err != nil {
return
}
//数据收发
for {
//数据类型有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)
}
版本一未做优化
版本二:
connection.go:
package impl
import (
"github.com/gorilla/websocket"
"sync"
"errors"
)
type Connection struct {
wsConn *websocket.Conn // 底层websocket
inChan chan []byte // 读队列
outChan chan []byte // 写队列
closeChan chan byte // 关闭通知
isClosed bool
mutex sync.Mutex // 避免重复关闭管道
}
//封装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),
}
//启动读协程
go conn.readLoop()
//启动写协程
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是线程安全的,可重入的(可以多次关闭)
conn.wsConn.Close()
//一个chan只能关闭一次(所以要保证这行代码只执行一次)
conn.mutex.Lock()
if !conn.isClosed{
close(conn.closeChan)
conn.isClosed = true
}
conn.mutex.Unlock()
}
//内部实现
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: //当closeChan被关闭就进入这个分支
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协议的配置
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
)
//完成握手应答
if wsConn, err = upgrader.Upgrade(writer, request, nil); err != nil {
return
}
if conn, err = impl.InitConnection(wsConn); err != nil {
goto ERR
}
// 不停发送心跳信息
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:
// 关闭连接的操作
conn.Close()
/*//数据收发
for {
//数据类型有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)
}
做了封装,线程安全
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。