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)
    }
    
  • 相关阅读:
    Linux压缩和解压缩命令(linux必学基础)
    Windows Server 2012 忘记登录密码怎么办?
    linux双网卡双网关设置
    date命令的帮助信息,使用date命令输出数字做为命名标题则不会有重复标题
    linux中history加入显示时间命令代码
    the MTS failed last time时的解决办法
    linux tar.gz zip 解压缩 压缩命令
    如果centos7添加新网卡,系统不识别的解决办法
    Linux常用命令英文全称与中文解释 (pwd、su、df、du等)
    使用动态规划算法解释硬币找零问题
  • 原文地址:https://www.cnblogs.com/SLchuck/p/14081768.html
Copyright © 2011-2022 走看看