zoukankan      html  css  js  c++  java
  • websocket 学习笔记

    websocket

    协议了解

    https://www.cnblogs.com/chyingp/p/websocket-deep-in.html

    源代码

    
    var addr = flag.String("addr", "localhost:8080", "http service address")
    
    var upgrader = websocket.Upgrader{} // use default options
    
    func echo(w http.ResponseWriter, r *http.Request) {
    	c, err := upgrader.Upgrade(w, r, nil)
    	if err != nil {
    		log.Print("upgrade:", err)
    		return
    	}
    	defer c.Close()
    	for {
    		mt, message, err := c.ReadMessage()
    		if err != nil {
    			log.Println("read:", err)
    			break
    		}
    		log.Printf("recv: %s", message)
    		err = c.WriteMessage(mt, message)
    		if err != nil {
    			log.Println("write:", err)
    			break
    		}
    	}
    }
    
    
    // Upgrade upgrades the HTTP server connection to the WebSocket protocol.
    //
    // The responseHeader is included in the response to the client's upgrade
    // request. Use the responseHeader to specify cookies (Set-Cookie). To specify
    // subprotocols supported by the server, set Upgrader.Subprotocols directly.
    //
    // If the upgrade fails, then Upgrade replies to the client with an HTTP error
    // response.
    func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
    
        // 省略部分代码
    
    	h, ok := w.(http.Hijacker)
    	var brw *bufio.ReadWriter
        netConn, brw, err := h.Hijack()
        
        // 省略部分代码
    	c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
    	c.subprotocol = subprotocol
    
    	if compress {
    		c.newCompressionWriter = compressNoContextTakeover
    		c.newDecompressionReader = decompressNoContextTakeover
    	}
    
    	// Use larger of hijacked buffer and connection write buffer for header.
    	p := buf
    	if len(c.writeBuf) > len(p) {
    		p = c.writeBuf
    	}
    	p = p[:0]
    
    	p = append(p, "HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: "...)
    	p = append(p, computeAcceptKey(challengeKey)...)
    	p = append(p, "
    "...)
    	if c.subprotocol != "" {
    		p = append(p, "Sec-WebSocket-Protocol: "...)
    		p = append(p, c.subprotocol...)
    		p = append(p, "
    "...)
    	}
    	if compress {
    		p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover
    "...)
    	}
    	for k, vs := range responseHeader {
    		if k == "Sec-Websocket-Protocol" {
    			continue
    		}
    		for _, v := range vs {
    			p = append(p, k...)
    			p = append(p, ": "...)
    			for i := 0; i < len(v); i++ {
    				b := v[i]
    				if b <= 31 {
    					// prevent response splitting.
    					b = ' '
    				}
    				p = append(p, b)
    			}
    			p = append(p, "
    "...)
    		}
    	}
    	p = append(p, "
    "...)
    
    	// Clear deadlines set by HTTP server.
    	netConn.SetDeadline(time.Time{})
    
    	if u.HandshakeTimeout > 0 {
    		netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
    	}
    	if _, err = netConn.Write(p); err != nil {
    		netConn.Close()
    		return nil, err
    	}
    	if u.HandshakeTimeout > 0 {
    		netConn.SetWriteDeadline(time.Time{})
    	}
    
    	return c, nil
    }
    
    // ReadMessage is a helper method for getting a reader using NextReader and
    // reading from that reader to a buffer.
    func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
        var r io.Reader
        // 返回 messagereader
    	messageType, r, err = c.NextReader()
    	if err != nil {
    		return messageType, nil, err
        }
        // 读取内容
    	p, err = ioutil.ReadAll(r)
    	return messageType, p, err
    }
    
    
    // NextReader returns the next data message received from the peer. The
    // returned messageType is either TextMessage or BinaryMessage.
    //
    // There can be at most one open reader on a connection. NextReader discards
    // the previous message if the application has not already consumed it.
    //
    // Applications must break out of the application's read loop when this method
    // returns a non-nil error value. Errors returned from this method are
    // permanent. Once this method returns a non-nil error, all subsequent calls to
    // this method return the same error.
    func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
    
        // 读取 frame
    	for c.readErr == nil {
    		frameType, err := c.advanceFrame()
    		if err != nil {
    			c.readErr = hideTempErr(err)
    			break
    		}
    
    		if frameType == TextMessage || frameType == BinaryMessage {
    			c.messageReader = &messageReader{c}
    			c.reader = c.messageReader
    			if c.readDecompress {
    				c.reader = c.newDecompressionReader(c.reader)
    			}
    			return frameType, c.reader, nil
    		}
    	}
    
    
    	return noFrame, nil, c.readErr
    }
    
    
    func (r *messageReader) Read(b []byte) (int, error) {
    	c := r.c
    
        // 省略部分代码
    	for c.readErr == nil {
    
    		if c.readRemaining > 0 {
                
                // 读取内容
    			n, err := c.br.Read(b)
    			
    		}
    
    
    	}
    
    	err := c.readErr
    	if err == io.EOF && c.messageReader == r {
    		err = errUnexpectedEOF
    	}
    	return 0, err
    }
    
    
    // WriteMessage is a helper method for getting a writer using NextWriter,
    // writing the message and closing the writer.
    func (c *Conn) WriteMessage(messageType int, data []byte) error {
    
    	if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
    		// Fast path with no allocations and single frame.
    
    		var mw messageWriter
    		if err := c.beginMessage(&mw, messageType); err != nil {
    			return err
    		}
    		n := copy(c.writeBuf[mw.pos:], data)
    		mw.pos += n
    		data = data[n:]
    		return mw.flushFrame(true, data)
    	}
    
    	w, err := c.NextWriter(messageType)
    	if err != nil {
    		return err
    	}
    	if _, err = w.Write(data); err != nil {
    		return err
    	}
    	return w.Close()
    }
    func (w *messageWriter) Write(p []byte) (int, error) {
    	if w.err != nil {
    		return 0, w.err
    	}
    
    	if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
            // Don't buffer large messages.
            // 服务器端
    		err := w.flushFrame(false, p)
    		if err != nil {
    			return 0, err
    		}
    		return len(p), nil
    	}
    
    	nn := len(p)
    	for len(p) > 0 {
    		n, err := w.ncopy(len(p))
    		if err != nil {
    			return 0, err
    		}
    		copy(w.c.writeBuf[w.pos:], p[:n])
    		w.pos += n
    		p = p[n:]
    	}
    	return nn, nil
    }
    
    
    // flushFrame writes buffered data and extra as a frame to the network. The
    // final argument indicates that this is the last frame in the message.
    func (w *messageWriter) flushFrame(final bool, extra []byte) error {
    
        // 省略部分代码
    
    	err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
    
    
    	// Setup for next frame.
    	w.pos = maxFrameHeaderSize
    	w.frameType = continuationFrame
    	return nil
    }
    
    // 写数据
    func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {
    	<-c.mu
    	defer func() { c.mu <- struct{}{} }()
    
    	c.writeErrMu.Lock()
    	err := c.writeErr
    	c.writeErrMu.Unlock()
    	if err != nil {
    		return err
    	}
    
    	c.conn.SetWriteDeadline(deadline)
    	if len(buf1) == 0 {
    		_, err = c.conn.Write(buf0)
    	} else {
    		err = c.writeBufs(buf0, buf1)
    	}
    	if err != nil {
    		return c.writeFatal(err)
    	}
    	if frameType == CloseMessage {
    		c.writeFatal(ErrCloseSent)
    	}
    	return nil
    }
    
    // 最后关闭 写最后一个 frame
    func (w *messageWriter) Close() error {
    	if w.err != nil {
    		return w.err
    	}
    	return w.flushFrame(true, nil)
    }
    
  • 相关阅读:
    积水路面Wet Road Materials 2.3
    门控时钟问题
    饮料机问题
    Codeforces Round #340 (Div. 2) E. XOR and Favorite Number (莫队)
    Educational Codeforces Round 82 (Rated for Div. 2)部分题解
    Educational Codeforces Round 86 (Rated for Div. 2)部分题解
    Grakn Forces 2020部分题解
    2020 年百度之星·程序设计大赛
    POJ Nearest Common Ancestors (RMQ+树上dfs序求LCA)
    算法竞赛进阶指南 聚会 (LCA)
  • 原文地址:https://www.cnblogs.com/SLchuck/p/14081768.html
Copyright © 2011-2022 走看看