mirror of
https://gitee.com/johng/gf
synced 2026-06-27 01:43:33 +08:00
完成ghttp.Server对WebSocket的支持,并完成测试以及对应示例
This commit is contained in:
@ -58,7 +58,6 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
return request
|
||||
}
|
||||
|
||||
|
||||
// 初始化GET请求参数
|
||||
func (r *Request) initGet() {
|
||||
if !r.parsedGet.Val() {
|
||||
@ -82,6 +81,17 @@ func (r *Request) initPost() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取Web Socket连接对象
|
||||
func (r *Request) WebSocket() (*WebSocket, error) {
|
||||
if conn, err := wsUpgrader.Upgrade(r.Response.ResponseWriter.ResponseWriter, &r.Request, nil); err == nil {
|
||||
return &WebSocket {
|
||||
conn,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 获得指定名称的参数字符串(GET/POST),同 GetRequestString
|
||||
// 这是常用方法的简化别名
|
||||
func (r *Request) Get(k string) string {
|
||||
|
||||
@ -24,6 +24,7 @@ import (
|
||||
"gitee.com/johng/gf/g/os/gspath"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/os/genv"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -48,7 +49,6 @@ type Server struct {
|
||||
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
signalQueue chan os.Signal // 终端命令行监听队列
|
||||
// 服务注册相关
|
||||
hmmu sync.RWMutex // handler互斥锁
|
||||
hmcmu sync.RWMutex // handlerCache互斥锁
|
||||
@ -98,7 +98,7 @@ type HandlerItem struct {
|
||||
router *Router // 注册时绑定的路由对象
|
||||
}
|
||||
|
||||
// http注册函数
|
||||
// HTTP注册函数
|
||||
type HandlerFunc func(r *Request)
|
||||
|
||||
// 文件描述符map
|
||||
@ -107,13 +107,11 @@ type listenerFdMap map[string]string
|
||||
// Server表,用以存储和检索名称与Server对象之间的关联关系
|
||||
var serverMapping = gmap.NewStringInterfaceMap()
|
||||
|
||||
// Web Server多进程管理器
|
||||
var procManager = gproc.NewManager()
|
||||
// Web Socket默认配置
|
||||
var wsUpgrader = websocket.Upgrader{}
|
||||
|
||||
// Web Server开始执行事件通道,由于同一个进程支持多Server,因此该通道为非阻塞
|
||||
var readyChan = make(chan struct{}, 100000)
|
||||
// Web Server已完成服务事件通道,当有事件时表示服务完成,当前进程退出
|
||||
var doneChan = make(chan struct{}, 100000)
|
||||
var doneChan = make(chan struct{}, 1000)
|
||||
|
||||
// Web Server进程初始化
|
||||
func init() {
|
||||
@ -159,7 +157,6 @@ func GetServer(name...interface{}) (*Server) {
|
||||
sessionIdName : gtype.NewString(gDEFAULT_SESSION_ID_NAME),
|
||||
servedCount : gtype.NewInt(),
|
||||
closeQueue : gqueue.New(),
|
||||
signalQueue : make(chan os.Signal),
|
||||
logPath : gtype.NewString(),
|
||||
accessLogEnabled : gtype.NewBool(),
|
||||
errorLogEnabled : gtype.NewBool(true),
|
||||
|
||||
@ -156,7 +156,7 @@ func forkReloadProcess(newExeFilePath...string) {
|
||||
if len(newExeFilePath) > 0 {
|
||||
path = newExeFilePath[0]
|
||||
}
|
||||
p := procManager.NewProcess(path, os.Args, os.Environ())
|
||||
p := gproc.NewProcess(path, os.Args, os.Environ())
|
||||
// 创建新的服务进程,子进程自动从父进程复制文件描述来监听同样的端口
|
||||
sfm := getServerFdMap()
|
||||
// 将sfm中的fd按照子进程创建时的文件描述符顺序进行整理,以便子进程获取到正确的fd
|
||||
@ -195,7 +195,7 @@ func forkRestartProcess(newExeFilePath...string) {
|
||||
os.Unsetenv(gADMIN_ACTION_RELOAD_ENVKEY)
|
||||
env := os.Environ()
|
||||
env = append(env, gADMIN_ACTION_RESTART_ENVKEY + "=1")
|
||||
p := procManager.NewProcess(path, os.Args, env)
|
||||
p := gproc.NewProcess(path, os.Args, env)
|
||||
if _, err := p.Start(); err != nil {
|
||||
glog.Errorfln("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
|
||||
}
|
||||
|
||||
@ -117,17 +117,20 @@ func (s *Server)serveFile(r *Request, path string) {
|
||||
}
|
||||
info, _ := f.Stat()
|
||||
if info.IsDir() {
|
||||
// 处理访问目录
|
||||
// 处理index files
|
||||
if len(s.config.IndexFiles) > 0 {
|
||||
for _, file := range s.config.IndexFiles {
|
||||
fpath := path + gfile.Separator + file
|
||||
if gfile.Exists(fpath) {
|
||||
// 这里使用s.paths来检索,而不是直接使用path,避免多目录检索情况
|
||||
// 例如:/tmp目录及源码目录都存在的情况按照优先级会返回/tmp,但是index files只在源码目录中存在
|
||||
fpath := s.paths.Search(r.URL.Path + gfile.Separator + file)
|
||||
if fpath != "" {
|
||||
f.Close()
|
||||
s.serveFile(r, fpath)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理访问目录
|
||||
if s.config.IndexFolder {
|
||||
s.listDir(r, f)
|
||||
} else {
|
||||
|
||||
14
g/net/ghttp/ghttp_server_websocket.go
Normal file
14
g/net/ghttp/ghttp_server_websocket.go
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
|
||||
package ghttp
|
||||
|
||||
import "github.com/gorilla/websocket"
|
||||
|
||||
type WebSocket struct {
|
||||
*websocket.Conn
|
||||
}
|
||||
93
geg/net/ghttp/websocket/echo/index.html
Normal file
93
geg/net/ghttp/websocket/echo/index.html
Normal file
@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>gf websocket echo server</title>
|
||||
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||
<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="list-group" id="divShow"></div>
|
||||
<div>
|
||||
<div><input class="form-control" id="txtContent" autofocus rows="6" placeholder="请输入发送内容"></div>
|
||||
<div><button class="btn btn-default" id="btnSend" style="margin-top:15px">发 送</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script type="application/javascript">
|
||||
// 显示提示信息
|
||||
function showInfo(content) {
|
||||
$("<div class=\"list-group-item list-group-item-info\">" + content + "</div>").appendTo("#divShow")
|
||||
}
|
||||
// 显示警告信息
|
||||
function showWaring(content) {
|
||||
$("<div class=\"list-group-item list-group-item-warning\">" + content + "</div>").appendTo("#divShow")
|
||||
}
|
||||
// 显示成功信息
|
||||
function showSuccess(content) {
|
||||
$("<div class=\"list-group-item list-group-item-success\">" + content + "</div>").appendTo("#divShow")
|
||||
}
|
||||
// 显示错误信息
|
||||
function showError(content) {
|
||||
$("<div class=\"list-group-item list-group-item-danger\">" + content + "</div>").appendTo("#divShow")
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var url = "ws://127.0.0.1:8199/ws";
|
||||
var ws = new WebSocket(url);
|
||||
try {
|
||||
// ws连接成功
|
||||
ws.onopen = function () {
|
||||
showInfo("WebSocket Server [" + url +"] 连接成功!");
|
||||
};
|
||||
// ws连接关闭
|
||||
ws.onclose = function () {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
showError("WebSocket Server [" + url +"] 连接关闭!");
|
||||
};
|
||||
// ws连接错误
|
||||
ws.onerror = function () {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
showError("WebSocket Server [" + url +"] 连接关闭!");
|
||||
};
|
||||
// ws数据返回处理
|
||||
ws.onmessage = function (result) {
|
||||
showWaring(" > " + result.data);
|
||||
};
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
|
||||
// 按钮点击发送数据
|
||||
$("#btnSend").on("click", function () {
|
||||
if (ws == null) {
|
||||
showError("WebSocket Server [" + url +"] 连接失败,请F5刷新页面!");
|
||||
return;
|
||||
}
|
||||
var content = $.trim($("#txtContent").val()).replace("/[\n]/g", "");
|
||||
if (content.length <= 0) {
|
||||
alert("请输入发送内容!");
|
||||
return;
|
||||
}
|
||||
$("#txtContent").val("")
|
||||
showSuccess(content);
|
||||
ws.send(content);
|
||||
});
|
||||
|
||||
// 回车按钮触发发送点击事件
|
||||
$("#txtContent").on("keydown", function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
$("#btnSend").trigger("click");
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
</script>
|
||||
25
geg/net/ghttp/websocket/echo/main.go
Normal file
25
geg/net/ghttp/websocket/echo/main.go
Normal file
@ -0,0 +1,25 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/ws", func(r *ghttp.Request) {
|
||||
conn, _ := r.WebSocket()
|
||||
for {
|
||||
msgType, msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.WriteMessage(msgType, msg); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user