- [github.com/klauspost/compress](https://github.com/klauspost/compress) under [BSD 3-Clause](https://raw.githubusercontent.com/klauspost/compress/master/LICENSE)\r
- [github.com/andybalholm/brotli](https:github.com/andybalholm/brotli) under [MIT](https://raw.githubusercontent.com/andybalholm/brotli/master/LICENSE)\r
- [github.com/klauspost/pgzip](https://github.com/klauspost/pgzip) under [MIT](https://raw.githubusercontent.com/klauspost/pgzip/master/LICENSE)\r
+- [github.com/skratchdot/open-golang/open](https://github.com/skratchdot/open-golang) under [MIT](https://raw.githubusercontent.com/skratchdot/open-golang/master/LICENSE)\r
+- [github.com/gorilla/websocket](https://github.com/gorilla/websocket) under [BSD 2-Clause](https://raw.githubusercontent.com/gorilla/websocket/master/LICENSE)\r
---\r
\r
#### 介绍\r
github.com/klauspost/pgzip v1.2.5
github.com/miekg/dns v1.1.35
github.com/shirou/gopsutil v3.20.12+incompatible
+ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/thedevsaddam/gojsonq v2.3.0+incompatible
github.com/thedevsaddam/gojsonq/v2 v2.5.2
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.20.12+incompatible h1:6VEGkOXP/eP4o2Ilk8cSsX0PhOEfX6leqAnD+urrp9M=
github.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
+github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/thedevsaddam/gojsonq v1.9.1 h1:zQulEP43nwmq5EKrNWyIgJVbqDeMdC1qzXM/f5O15a0=
github.com/thedevsaddam/gojsonq v2.3.0+incompatible h1:i2lFTvGY4LvoZ2VUzedsFlRiyaWcJm3Uh6cQ9+HyQA8=
github.com/thedevsaddam/gojsonq v2.3.0+incompatible/go.mod h1:RBcQaITThgJAAYKH7FNp2onYodRz8URfsuEGpAch0NA=
--- /dev/null
+package part
+
+import (
+ "net/http"
+ "time"
+ "strconv"
+ "context"
+ p "github.com/qydysky/part"
+)
+
+type Web struct {
+ Server *http.Server
+ mux *http.ServeMux
+}
+
+func New(conf *http.Server) (o *Web) {
+
+ o = new(Web)
+
+ o.Server = conf
+
+ if o.Server.Addr == `` {o.Server.Addr = "127.0.0.1:"+strconv.Itoa(p.Sys().GetFreePort())}
+ if o.Server.WriteTimeout == 0 {o.Server.WriteTimeout = time.Second * time.Duration(10)}
+ if o.Server.Handler == nil {
+ o.mux = http.NewServeMux()
+ o.Server.Handler = o.mux
+ }
+
+ go o.Server.ListenAndServe()
+
+ return
+}
+
+func (t *Web) Handle(path_func map[string]func(http.ResponseWriter,*http.Request)) {
+ for k,v := range path_func {
+ t.mux.HandleFunc(k,v)
+ }
+}
+
+func Easy_boot() (*Web) {
+ s := New(&http.Server{})
+ s.Handle(map[string]func(http.ResponseWriter,*http.Request){
+ `/`:func(w http.ResponseWriter,r *http.Request){
+ var path string = r.URL.Path[1:]
+ if path == `` {path = `index.html`}
+ http.ServeFile(w, r, path)
+ },
+ `/exit`:func(w http.ResponseWriter,r *http.Request){
+ s.Server.Shutdown(context.Background())
+ },
+ })
+ return s
+}
--- /dev/null
+package part
+
+import (
+ "testing"
+ "time"
+)
+
+func Test_Server(t *testing.T) {
+ s := Easy_boot()
+ t.Log(`http://`+s.Server.Addr)
+ time.Sleep(time.Second*time.Duration(100))
+}
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html lang="zh-cmn-Hans">
+ <head>
+ <meta charset="utf-8" />
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+ </head>
+ <body>
+ <h3>ok</h3>
+ </body>
+</html>
--- /dev/null
+package part
+
+import (
+ "net"
+ "net/http"
+ "time"
+ "github.com/gorilla/websocket"
+ idpool "github.com/qydysky/part/idpool"
+ mq "github.com/qydysky/part/msgq"
+)
+
+type Server struct {
+ ws_mq *mq.Msgq
+ userpool *idpool.Idpool
+}
+
+type Uinterface struct {
+ Id uintptr
+ Data []byte
+}
+
+func New_server() (*Server) {
+ return &Server{
+ ws_mq: mq.New(200),//收发通道
+ userpool: idpool.New(),//浏览器标签页池
+ }
+}
+
+func (t *Server) WS(w http.ResponseWriter, r *http.Request) {
+ upgrader := websocket.Upgrader{}
+
+ ws, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ t.ws_mq.Push_tag(`error`,err)
+ return
+ }
+ defer ws.Close()
+
+ //从池中获取本会话id
+ User := t.userpool.Get()
+ defer t.userpool.Put(User)
+
+
+ //发送
+ t.ws_mq.Pull_tag(map[string]func(interface{})(bool){
+ `send`:func(data interface{})(bool){
+ if u,ok := data.(Uinterface);ok && u.Id == 0 || u.Id == User.Id{
+ if err := ws.WriteMessage(websocket.TextMessage,u.Data);err != nil {
+ t.ws_mq.Push_tag(`error`,err)
+ return true
+ }
+ }
+ return false
+ },
+ `close`:func(data interface{})(bool){
+ if u,ok := data.(Uinterface);ok && u.Id == 0 || u.Id == User.Id{
+ return true
+ }
+ return false
+ },
+ })
+
+ //接收
+ for {
+ ws.SetReadDeadline(time.Now().Add(time.Second*time.Duration(300)))
+ if _, message, err := ws.ReadMessage();err != nil {
+ if websocket.IsCloseError(err,websocket.CloseGoingAway) {
+ } else if err,ok := err.(net.Error);ok && err.Timeout() {
+ //Timeout , js will reload html
+ } else {
+ t.ws_mq.Push_tag(`error`,err)
+ }
+ t.ws_mq.Push_tag(`close`,Uinterface{
+ Id:User.Id,
+ })
+ break
+ } else {
+ t.ws_mq.Push_tag(`recv`,Uinterface{
+ Id:User.Id,
+ Data:message,
+ })
+ }
+ }
+}
+
+//how to use
+// ws_mq.Pull_tag(map[string]func(interface{})(bool){
+// `recv`:func(data interface{})(bool){
+// if tmp,ok := data.(Uinterface);ok {
+// log.Println(tmp.Id,string(tmp.Data))
+
+// if string(tmp.Data) == `close` {
+// ws_mq.Push_tag(`close`,Uinterface{//close
+// Id:0,//close all connect
+// })
+// //or
+// // ws_mq.Push_tag(`close`,Uinterface{//close
+// // Id:tmp.Id,//close this connect
+// // })
+// return false
+// }
+
+// ws_mq.Push_tag(`send`,Uinterface{//just reply
+// Id:tmp.Id,
+// Data:tmp.Data,
+// })
+// //or
+// ws_mq.Push_tag(`send`,Uinterface{//just reply
+// Id:0,//send to all
+// Data:tmp.Data,
+// })
+// }
+// return false
+// },
+// `error`:func(data interface{})(bool){
+// log.Println(data)
+// return false
+// },
+// })
+func (t *Server) Interface() (*mq.Msgq) {
+ return t.ws_mq
+}
+
+func (t *Server) Len() uint {
+ return t.userpool.Len()
+}
\ No newline at end of file
--- /dev/null
+package part
+
+import (
+ "testing"
+ "net/http"
+ "time"
+ "github.com/skratchdot/open-golang/open"
+ web "github.com/qydysky/part/web"
+)
+
+func Test_Server(t *testing.T) {
+ s := New_server()
+ {
+ ws_mq := s.Interface()
+ ws_mq.Pull_tag(map[string]func(interface{})(bool){
+ `recv`:func(data interface{})(bool){
+ if tmp,ok := data.(Uinterface);ok {
+ t.Log(tmp.Id,string(tmp.Data))
+ ws_mq.Push_tag(`send`,Uinterface{//just reply
+ Id:tmp.Id,
+ Data:tmp.Data,
+ })
+ }
+ return false
+ },
+ })
+ }
+
+ w := web.Easy_boot()
+ open.Run("http://"+w.Server.Addr)
+ w.Handle(map[string]func(http.ResponseWriter,*http.Request){
+ `/ws`:func(w http.ResponseWriter,r *http.Request){
+ s.WS(w,r)
+ },
+ })
+ time.Sleep(time.Second*time.Duration(100))
+}
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html lang="zh-cmn-Hans">
+ <head>
+ <meta charset="utf-8" />
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1"
+ />
+ </head>
+ <body>
+ <h3>websocket</h3>
+ <p id="ws"></p>
+ <script>
+ if (window["WebSocket"]) {
+ conn = new WebSocket("ws://" + document.location.host + "/ws");
+ conn.onclose = function () {
+ document.getElementById("ws").innerHTML += `close!`
+ };
+ conn.onmessage = function (evt) {
+ document.getElementById("ws").innerHTML += evt.data+`<br>`
+ };
+
+ setInterval(()=>{
+ conn.send(`send`)
+ },3000)
+ }
+ </script>
+ </body>
+</html>