zoukankan      html  css  js  c++  java
  • go websocket

    websocket介绍

    The WebSocket Protocol  RFC6455,这个是WebSocket的RFC文档,所以内容非常全面(当然只涉及协议,不涉及具体实现),不过内容太多,如果是初次了解,可以挑自己感兴趣的看看。

    《WebSocket 是什么原理?为什么可以实现持久连接?》,这个是知乎上的一篇文章,对于WebSocket讲的通俗易懂,可以了解一下。 

           在介绍WebSocket之前,我们先来了解一下为什么会出现WebSocket。我们知道HTTP协议(底层使用的是TCP协议)是无状态的,即一次Request,一个Response就结束了。实际中的场景就是客户端(比如浏览器)向服务器发送一次请求,然后服务器返回一个响应,一次交互就结束了,底层的TCP连接也会断掉,下次请求时,重新再创建新的连接。而且这种通信是被动式的,就是说服务器端不能主动向客户端发响应,只能是客户端一个Request,服务的一个Response这种模式(当然最新的协议里面,可能可以将多个Request合并一次发给服务端,但模型仍旧是这种模式)。

           如果你曾经使用TCP协议写过通信程序,应该非常熟悉那种模式:客户端和服务端(有时都没有清晰的界限)通过三步握手建立连接后,就可以相互随便发送数据,除非网络异常或者主动关闭,否则该TCP连接将一直存在。而WebSocket的出现就是为了在应用层实现类似传输层的TCP协议,当然它底层和HTTP一样使用的是TCP协议。这样我们就明白一些了,WebSocket不像HTTP协议,一次交互后就结束,它建立后会一直存在(除非主动断开或者网络异常等),而且客户端和服务端可以任意向对方发送数据,不再像以前那么“傻”了。也就是说,HTTP协议是一次性的,“单工的”;而WebSocket是真正意义上的长连接,且是全双工的。当然,上述提及的需求HTTP通过poll和轮循等方式也可以实现,但弊端非常多:

    • 服务器端需要在底层为每个HTTP连接维护一个TCP连接,比如一个用于发送消息,一个用于接收消息等。
    • 资源浪费,每次的HTTP请求中都需要携带消息头。
    • 客户端还必须通过一些手段知道哪些响应对应发出去的哪些请求。

    go websocket

           Go官方的标准包里面提供了一个WebSocket的包 golang.org/x/net/websocket,但也说的很明确,这个包里面并没有实现WebSocket协议规定的一些特性,而推荐使用github.com/gorilla/websocket这个包。

    代码示例,代码主要实现服务端向客户端写入数据,你可以通过channel Broadcast插入数据。

    web页面的测试,可以在百度上选择一些在线的,如http://coolaf.com/tool/chattest

    package controllers
    
    import (
    	"github.com/astaxie/beego"
    	"github.com/gorilla/websocket"
    	"net/http"
    )
    
    const (
    	readBufferSize  = 1024
    	writeBufferSize = 1024
    )
    
    type Client struct {
    	conn     *websocket.Conn
    	messages chan []byte
    }
    
    var (
    	upgrader = websocket.Upgrader{
    		ReadBufferSize:  readBufferSize,
    		WriteBufferSize: writeBufferSize,
    		CheckOrigin: func(r *http.Request) bool {
    			return true
    		},
    	}
    	clients    map[*Client]bool // 存储所有的链接
    	addClients chan *Client     // 新链接进来的chan
    	delClients chan *Client     // 删除退出的chan
    	Broadcast  chan []byte      // 广播聊天的chan
    )
    
    func (c *Client) writePump() {
    	defer func() {
    		delClients <- c
    		c.conn.Close()
    	}()
    	for {
    		select {
    		case message := <- c.messages:
    			err := c.conn.WriteMessage(websocket.TextMessage, message)
    			if err != nil {
    				return
    			}
    		}
    	}
    }
    
    func manager() {
    	clients = make(map[*Client]bool)
    	addClients = make(chan *Client)
    	delClients = make(chan *Client)
    
    	for {
    		select {
    		case message := <- Broadcast:
    			for client := range clients {
    				select {
    				case client.messages <- message:
    				default:
    					close(client.messages)
    					delete(clients, client)
    				}
    			}
    		case client := <- addClients:
    			clients[client] = true
    		case client := <- delClients:
    			if _, ok := clients[client]; ok {
    				close(client.messages)
    				delete(clients, client)
    			}
    		}
    	}
    }
    
    type WebSocketController struct {
    	beego.Controller
    }
    
    func (this *WebSocketController) Ws() {
    	ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil)
    	if err != nil {
    		return
    	}
    	client := &Client{conn: ws, messages: make(chan []byte, 256)}
    	addClients <- client
    	go client.writePump()
    }
    
    func init() {
    	Broadcast = make(chan []byte)
    	go manager()
    }

    参考文章:

    http://time-track.cn/websocket-and-golang.html

    https://www.cnblogs.com/cornor/p/6178507.html

    https://github.com/gorilla/websocket/tree/master/examples/chat

    https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.2.md

    https://beego.me/docs/examples/chat.md

  • 相关阅读:
    spring的bean的属性注入
    spring中bean的常用属性
    spring Code(spring 核心)
    mybatis的缓存
    mybatis 调用存储过程
    mybatis的动态sql
    mybatis的关系映射
    mybatis添加信息自动生成主键
    mybatis传递参数的方法
    如何编写跨平台的Java代码
  • 原文地址:https://www.cnblogs.com/shhnwangjian/p/8301349.html
Copyright © 2011-2022 走看看