[elixir! #0066] 打工人的摸鱼之作 —— websocket client 最小实现

文则
• 阅读 1241

很早之前就对 WebSocket 协议非常感兴趣,今天有空时看了一下 RFC6455, 发现其实是一个很简单的协议。于是尝试着实现了一个客户端。这里摘取一些关键部分的代码。

WebSocket 和普通的 tcp 连接很类似,可以双向发送消息(区别于 http的request-response模式)。

首先第一步是建立 tcp 连接,然后发送 http 协议升级消息:

  defp upgrade_msg(uri, nonce) do
    """
    GET / HTTP/1.1\r
    Host: #{uri.authority}\r
    Upgrade: websocket\r
    Connection: Upgrade\r
    Sec-WebSocket-Key: #{nonce}\r
    Sec-WebSocket-Version: 13\r\n
    """
  end

之后服务器会返回一些内容,我们校验过后websocket连接就算建立成功了:


  defp handle_tcp(:handshake, data, %{challenge: challenge}) do
    {:ok, {:http_response, _, 101, "Switching Protocols"}, rest} =
      :erlang.decode_packet(:http_bin, data, []) |> IO.inspect()

    case validate_headers(rest, fn
           {:Connection, up} ->
             String.downcase(up) == "upgrade"

           {:Upgrade, ws} ->
             String.downcase(ws) == "websocket"

           {"Sec-WebSocket-Accept", ch} ->
             ch == challenge

           _ ->
             true
         end) do
      :ok ->
        IO.inspect("goto data_framing")
        {:data_framing, ""}

      {:error, wrong_header} ->
        IO.inspect("falied because: #{inspect(wrong_header)}")
        {:failed, :close}
    end
  end

连接建立之后就可以以特定的格式收发消息了,消息的最小单位是 frame,它的结构是这样的:

  def encode(%{opcode: op, mask: mask} = meta) do
    op = enop(op)
    payload = if mask, do: meta.masked_payload, else: meta.payload
    mask_key = if mask, do: meta.mask_key, else: <<>>
    mask = if mask, do: 1, else: 0

    <<1::size(1), 0::size(3), op::size(4), mask::size(1),
      encode_payload_length(byte_size(payload))::bitstring, mask_key::bytes, payload::bytes>>
  end

客户端发送给服务器的内容需要 mask,而服务端发给客户端的则不需要。

opcode 常用的有 text,binary,ping,pong,close. 分别代表不同的消息类型。

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
4年前
java 实现websocket
最近了解了下websocket和socket这个东西,说不得不来说下为何要使用WebSocket,和为何不用http。为何需要WebSocket?HTTP协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。这种通信模型有一个弊端:HTTP协议无法实现服务器主
Wesley13 Wesley13
4年前
Java WebSocket
本示例的目的,就是通过webSocket向客户端(浏览器端)发送消息.一、什么是WebSocket  WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(fullduplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF(https://www.oschina.n
Stella981 Stella981
4年前
Spring Websocket 中文文档 (spring5)
这部分参考文档包括对Servlet堆栈的支持,包括原始WebSocket交互的WebSocket消息传递,通过SockJS的WebSocket仿真,以及通过STOMP作为WebSocket上的子协议的pubsub消息传递。4.1。介绍WebSocket协议RFC6455(https://www.oschina.net/action/G
Stella981 Stella981
4年前
Noark入门之WebSocket
支持WebSocket吗?你还在为H5的前端链接头疼吗?你还在了解WebSocket的握手协议吗?WebSocket有没有粘包概念啊?之前忘了说了,很不好意思,Noark在原来Tcp端口上实现了WebSocket协议的判定与处理,实现了Socket与WebSocket共存的效果还记得Socket链接服务器那一串暗号吗?那之前是为Flash
Stella981 Stella981
4年前
HTTP协议与WebSocket协议对比
1.HTTP从根本上讲,HTTP还是半双工的协议,也就是说,在同一时刻流量只能单向流动:客户端向服务器发送请求(单向),然后服务器响应请求(单向)。2.WebSocketWebSocket是一种自然的全双工、双向、单套接字连接。使用WebSocket,一旦建立连接,服务器与客户端可以随时发送消息。与HTTP轮询不同,WebSocket只发有一个请
Wesley13 Wesley13
4年前
34.TCP取样器
阅读文本大概需要3分钟。1、TCP取样器的作用   TCP取样器作用就是通过TCP/IP协议来连接服务器,然后发送数据和接收数据。2、TCP取样器详解!(https://oscimg.oschina.net/oscnet/32a9b19ba1db00f321d22a0f33bcfb68a0d.png)TCPClien
Stella981 Stella981
4年前
Hibernate纯sql查询结果和该sql在数据库直接查询结果不一致
问题:今天在做一个查询的时候发现一个问题,我先在数据库实现了我需要的sql,然后我在代码中代码:selectdistinctd.id,d.name,COALESCE(c.count_num,0),COALESCE(c.count_fix,0),COALESCE(c
天翼云CDN全站加速产品对websocket协议的支持
天翼云全站加速产品支持对webscoket协议和http/https协议可同时加速,即同一个域名可以既有http/https协议,又有websocket加速,您无需拆分域名,使用全站加速产品就可以实现对域名下http/https协议的应用和websocket协议的应用同时加速。全站加速节点会自动识别客户端与全站加速边缘节点通信使用的协议,自动切换协议。通常情况下,websocket协议的应用多为动态业务,对实时性要求很高,全站加速的动态探测选路能力可以为websocket应用选择最快的回源路径,提升websocket业务的访问效果。