如何连接到它反应的 websocket 而不是普通的 javascript?

Posted

技术标签:

【中文标题】如何连接到它反应的 websocket 而不是普通的 javascript?【英文标题】:How do I connect to a websocket it react instead of plain javascript? 【发布时间】:2020-06-25 16:04:10 【问题描述】:

我想创建一个与 websocket 建立连接的组件。

我有一个当前创建 websocket 连接的 go 后端。

    r.LoadhtmlFiles("index.html")
    r.GET("/room/:roomId", func(c *gin.Context) 
        c.HTML(200, "index.html", nil)
    )
    r.GET("/ws/:roomId", func(c *gin.Context) 
        roomID := c.Param("roomId")
        handler.OpenWebSocket(c.Writer, c.Request, roomID)
    )

这是我的html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Chat Example</title>
    <script type="text/javascript">
        window.onload = function () 
            let conn;
            let msg = document.getElementById("msg");
            let log = document.getElementById("log");

            function appendLog(item) 
                let doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;
                log.appendChild(item);
                if (doScroll) 
                    log.scrollTop = log.scrollHeight - log.clientHeight;
                
            

            document.getElementById("form").onsubmit = function () 
                if (!conn) 
                    console.log("hello1")
                    return false;
                
                if (!msg.value) 
                    console.log("hello2")

                    return false;
                
                conn.send(msg.value);
                msg.value = "";
                return false;
            ;

            if (window["WebSocket"]) 
                const params = window.location.href.split("/");
                const roomId = params[params.length - 1];
                const url ="ws://" + document.location.host + "/ws/" + 99
                conn = new WebSocket(url);
                console.log(url)
                conn.onclose = function (evt) 
                  console.log("i was closed")
                    let item = document.createElement("div");
                    item.innerHTML = "<b>Connection closed.</b>";
                    appendLog(item);
                ;
                conn.onmessage = function (evt) 
                    let messages = evt.data.split('\n');
                    console.log(messages)
                    for (let i = 0; i < messages.length; i++) 
                        let item = document.createElement("div");
                        item.innerText = messages[i];
                        appendLog(item);
                    
                ;
             else 
                let item = document.createElement("div");
                item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
                appendLog(item);
            
        ;
    </script>
    <style type="text/css">
                

        body 
            overflow: hidden;
            padding: 0;
            margin: 0;
            width: 100%;
            height: 100%;
            background: gray;
        

        #log 
            background: white;
            margin: 0;
            padding: 0.5em 0.5em 0.5em 0.5em;
            position: absolute;
            top: 0.5em;
            left: 0.5em;
            right: 0.5em;
            bottom: 3em;
            overflow: auto;
        

        #form 
            padding: 0 0.5em 0 0.5em;
            margin: 0;
            position: absolute;
            bottom: 1em;
            left: 0px;
            width: 100%;
            overflow: hidden;
        

    </style>
</head>
<body>
<div id="log"></div>
<form id="form">
    <input type="text" id="msg" size="64" autofocus/>
    <input type="submit" value="Send"/>
</form>
</body>
</html>

这很好,如果我访问 http://localhost:8080/room/12 然后我可以访问聊天,一切都很好。但是,当我尝试使用 react 和 nextjs 建立连接时,我得到了

WebSocket connection to 'ws://localhost:8080/ws' failed: Error during WebSocket handshake: Unexpected response code: 404

这是我的反应页面。

import React, useState, useEffect, useRef  from 'react'

import Head from 'next/head'

import  useRouter  from 'next/router'
import  initializeApollo, useApollo  from '@/utils/apollo'
import  ProfileDataQuery  from '@/gql'

import  UserData  from '@/types'
import  NextPageContext  from 'next'

type ProfilePageProps = 
  initialApolloState: any


const ProfilePage: React.FunctionComponent<ProfilePageProps> = ( initialApolloState ) => 
  const apolloClient = useApollo(initialApolloState)
  const router = useRouter()
  const [isPaused, setPause] = useState(false)

  useEffect(() => 
    ws.current = new WebSocket("ws://localhost:8080/ws");
    ws.current.onopen = () => console.log("ws opened");
    ws.current.onclose = () => console.log("ws closed");

    return () => 
      ws.current.close()
    
  , []);
  useEffect(() => 
    if (!ws.current) return;

    ws.current.onmessage = e => 
        if (isPaused) return;
        const message = JSON.parse(e.data);
        console.log("e", message);
    ;
, [isPaused]);
  const data = apolloClient.readQuery(
    query: ProfileDataQuery,
    variables: 
      uuid: router.query.uuid
    
  )
  console.log(data)

  const userData: UserData = data.Users[0]
  const followers: UserData[] = data.followers
  const following: UserData[] = data.following
  const ws = useRef(null)
  
  return (
    <div className="main flex flex-col">
      <Head>
        <title> userData.username  - Omiran</title>
      </Head>
      <div className="flex-grow"/>
      <div className="flex flex-col border border-gray-500 rounded-lg w-11/12 md:w-4/5 p-5 mx-auto">
        <div className="flex flex-row items-center w-full">
          <img
            className="rounded-full mr-6"
            src=userData.profilePicture
            alt=userData.username
            height=100
            width=100
          />
          <div className="flex flex-col">
            <h1 className="text-xl sm:text-2xl md:text-3xl text-left">userData.username</h1>
            <div className="flex flex-row">
              <span className="text-sm mr-3">
                <b>followers.length</b> Followers
              </span>
              <span className="text-sm">
                <b>following.length</b> Following
              </span>
            </div>
          </div>
          <div className="flex-grow" />
          <button className="btn btn-orange">true ? 'Edit Profile' : 'Follow'</button>
        </div>
      </div>
      <div className="flex-grow-3"/>
    </div>
  )


export async function getServerSideProps(context: NextPageContext) 
  const  uuid  = context.query

  const apolloClient = initializeApollo()

  await apolloClient
    .query(
      query: ProfileDataQuery,
      variables: 
        uuid: String(uuid)
      
    )

  return 
    props: 
      initialApolloState: apolloClient.cache.extract(),
    ,
  


export default ProfilePage

【问题讨论】:

【参考方案1】:

我必须 CheckOrigin 以允许来自前端的连接:

var upgrader = websocket.Upgrader
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool 
        return r.Header.Get("Origin") == "http://localhost:3000" || r.Header.Get("Origin") == "http://localhost:8080"
    ,


这是上下文的整个文件:

package handler

import (
    "log"
    "net/http"
    "time"

    "github.com/gorilla/websocket"
)

const (
    // Time allowed to write a message to the peer.
    writeWait = 10 * time.Second

    // Time allowed to read the next pong message from the peer.
    pongWait = 60 * time.Second

    // Send pings to peer with this period. Must be less than pongWait.
    pingPeriod = (pongWait * 9) / 10

    // Maximum message size allowed from peer.
    maxMessageSize = 512
)

//H is my connection
var H = hub
    broadcast:  make(chan message),
    register:   make(chan subscription),
    unregister: make(chan subscription),
    rooms:      make(map[string]map[*connection]bool),


type hub struct 
    rooms      map[string]map[*connection]bool
    broadcast  chan message
    register   chan subscription
    unregister chan subscription


type message struct 
    data []byte
    room string


type subscription struct 
    conn *connection
    room string


type connection struct 
    ws   *websocket.Conn
    send chan []byte


//OpenWebSocket opens a connection to our nice websocket
func OpenWebSocket(w http.ResponseWriter, r *http.Request, roomID string) 
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil 
        log.Println(err.Error())
        return
    
    co := &connectionsend: make(chan []byte, 256), ws: ws
    s := subscriptionco, roomID
    H.register <- s
    go s.writePump()
    go s.readPump()


var upgrader = websocket.Upgrader
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool 
        return r.Header.Get("Origin") == "http://localhost:3000" || r.Header.Get("Origin") == "http://localhost:8080"
    ,


func (c *connection) write(mt int, payload []byte) error 
    c.ws.SetWriteDeadline(time.Now().Add(writeWait))
    return c.ws.WriteMessage(mt, payload)


func (s subscription) readPump() 
    c := s.conn
    defer func() 
        H.unregister <- s
        c.ws.Close()
    ()
    c.ws.SetReadLimit(maxMessageSize)
    c.ws.SetReadDeadline(time.Now().Add(pongWait))
    c.ws.SetPongHandler(func(string) error  c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil )
    for 
        _, msg, err := c.ws.ReadMessage()
        if err != nil 
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) 
                log.Printf("error: %v", err)
            
            break
        
        m := messagemsg, s.room
        H.broadcast <- m
    


func (s *subscription) writePump() 
    c := s.conn
    ticker := time.NewTicker(pingPeriod)
    defer func() 
        ticker.Stop()
        c.ws.Close()
    ()
    for 
        select 
        case message, ok := <-c.send:
            if !ok 
                c.write(websocket.CloseMessage, []byte)
                return
            
            if err := c.write(websocket.TextMessage, message); err != nil 
                return
            
        case <-ticker.C:
            if err := c.write(websocket.PingMessage, []byte); err != nil 
                return
            
        
    


func (H *hub) Run() 
    for 
        select 
        case s := <-H.register:
            connections := H.rooms[s.room]
            if connections == nil 
                connections = make(map[*connection]bool)
                H.rooms[s.room] = connections
            
            H.rooms[s.room][s.conn] = true
        case s := <-H.unregister:
            connections := H.rooms[s.room]
            if connections != nil 
                if _, ok := connections[s.conn]; ok 
                    delete(connections, s.conn)
                    close(s.conn.send)
                    if len(connections) == 0 
                        delete(H.rooms, s.room)
                    
                
            
        case m := <-H.broadcast:
            connections := H.rooms[m.room]
            for c := range connections 
                select 
                case c.send <- m.data:
                default:
                    close(c.send)
                    delete(connections, c)
                    if len(connections) == 0 
                        delete(H.rooms, m.room)
                    
                
            
        
    

【讨论】:

以上是关于如何连接到它反应的 websocket 而不是普通的 javascript?的主要内容,如果未能解决你的问题,请参考以下文章

WCF 连接到 Websocket 服务器

如何在不停止反应器的情况下停止 websocket 客户端

连接到 WebSocket 时如何覆盖 Chrome 中的 Origin 标头?

如何从不同的服务器连接到 websocket?

OkHttp Websockets - 连接到 websocket 时添加一个主体

SailsJs websocket与自定义路线而不是蓝图?