dongmeng1868 2019-04-05 13:24
浏览 327
已采纳

发送连接升级后,如何将客户端http连接升级到golang中的websockets

I need a golang client that can upgrade from an http get response to a websocket connection.

I have a JS client that works and I've seen direct ws client connections but I have to upgrade from http. I have tried looking for other 3GL solutions (Java, C#, Python) but I need to be able to implement the upgrade in Go. I have seen Dart detaching the socket and creating a websocket from it.

WebSocket.fromUpgradedSocket

I noticed Client does not support Hijack but the discussion didn't get me anywhere. I am using github.com/gorilla/websocket but can change that if it helps.

Server:


func main() {
    srv := Srv{}
    count = 0
    http.HandleFunc("/", srv.handleRoot)
    http.HandleFunc("/ws", srv.handleWs)
    log.Fatal(http.ListenAndServe(":5002", nil))
}

func (tool *Srv) handleRoot(w http.ResponseWriter, r *http.Request) {

    webSocketKey := hdr.Get("Sec-WebSocket-Key")
    log.Printf("Socket key = '%v'", webSocketKey)

    secWsAccept := computeAcceptKey(webSocketKey)
    log.Printf("Accept = '%v'", secWsAccept)
    w.Header().Add("sec-websocket-accept", secWsAccept)
    w.Header().Add("upgrade", "websockt")
    w.Header().Add("connection", "upgrade")
    w.WriteHeader(101)
}

func (tool *Srv) handleWs(w http.ResponseWriter, r *http.Request) {
    var upgrader = websocket.Upgrader{}
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatalf("Websocket fatal error. %v", err)
    }
    tool.conn = conn
    go tool.serviceWsRequests()
}

func (tool *Srv) serviceWsRequests() {
    for {
        log.Printf("starting ws")
        req := request{}
        err := tool.conn.ReadJSON(&req)
        if err != nil {
            log.Printf("Failed to decode ws message. %v", err)
            break
        }
        fmt.Printf("Got request. %v
", req)
        if req.Method == "ping" {
            fmt.Printf("Param=%v
", req.Parameters)
        }
    }
}

var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

func computeAcceptKey(challengeKey string) string {
    h := sha1.New()
    h.Write([]byte(challengeKey))
    h.Write(keyGUID)
    return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

Client:


func main() {
    tr := &http.Transport{
        MaxIdleConns:       10,
        IdleConnTimeout:    30 * time.Second,
        DisableCompression: true,
    }
    client := &http.Client{
        Transport: tr,
        // Do NOT follow redirects
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse
        },
    }
    wsKey, err := generateKey()
    if err != nil {
        log.Printf("Cannot generate challenge key %v", err)
    }

    // Get request for ws upgrade.
    req, err := http.NewRequest("GET", "http://localhost:5002", nil)
    req.Header.Add("Connection", "Upgrade")
    req.Header.Add("Upgrade", "websocket")
    req.Header.Add("Sec-WebSocket-Version", "13")
    req.Header.Add("Sec-WebSocket-Key", wsKey)
    log.Printf("ws key '%v'", wsKey)
    resp, err := client.Do(req)

    if err != nil {
        log.Printf("Get error %v", err)
    }
    defer func() {
        if resp != nil {
            err = resp.Body.Close()
        }
    }()

    log.Printf("Status='%v', proto='%v'", resp.Status, resp.Proto)
    body, err := ioutil.ReadAll(resp.Body)
    hdr := resp.Header
    for k, v := range hdr{
        log.Printf("%v : %v", k, v)
    }
    log.Printf("Body = %v", string(body))

    resp, err = http.Get("ws://localhost:5002/ws")
    if err != nil {
        log.Printf("Error '%v'", err)
    }

}

func generateKey() (string, error) {
    p := make([]byte, 16)
    if _, err := io.ReadFull(rand.Reader, p); err != nil {
        return "", err
    }
    return base64.StdEncoding.EncodeToString(p), nil
}

var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

func computeAcceptKey(challengeKey string) string {
    h := sha1.New()
    h.Write([]byte(challengeKey))
    h.Write(keyGUID)
    return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

I get an error

Error 'Get ws://localhost:5002/ws: unsupported protocol scheme "ws"'

Which doesn't surprise me because I haven't upgraded the connection. So how do I go an upgrade in Go?

  • 写回答

1条回答

  • 普通网友 2019-04-05 14:17
    关注

    Use the Gorilla client to dial websocket connections:

    func main() {
        c, _ , err := websocket.DefaultDialer.Dial("ws://localhost:5002/ws", nil)
        if err != nil {
            // handle error
        }
        defer c.Close()
    
        // do something with c, a *websocket.Conn
    
    }
    

    The Dial method issues a GET to the server requesting an upgrade to the WebSocket protocol. On successful completion of the upgrade, Dial returns a *websocket.Conn.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)