【问题标题】:Websocket Handshake Failed In JavaJava中的Websocket握手失败
【发布时间】:2014-01-04 00:32:28
【问题描述】:

所以,我正在尝试解析一个使用 websocket 向客户端发送数据的网站。根据我的阅读:

我知道为了创建一个连接,我需要向服务器发送一个特殊的 http 请求,然后会检索一些我用来访问通道的数据。

我做了什么:

  • 创建了 http 请求

    `URL obj = new URL(url);
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    
    // optional default is GET
    con.setRequestMethod("GET");
    
    //add request header
    con.setRequestProperty("Cache-Control", "no-cache");
    con.setRequestProperty("Host", "www.example.com");
    con.setRequestProperty("Upgrade", "websocket");
    con.setRequestProperty("Connection", "upgrade");
    con.setRequestProperty("Origin", "https://example.com");
    con.setRequestProperty("Sec-WebSocket-Extensions", "x-webkit-deflate-frame");
    con.setRequestProperty("Sec-WebSocket-Key", "QkWkChtTGpaZL5PkOMaRtg==");
    con.setRequestProperty("Sec-WebSocket-Version", "13");
    con.setRequestProperty("User-Agent", "Mozilla/..;
    

    `

我阅读了带有代码 200 的响应(不是应该的 101)。

问题:

  1. 如何生成Sec-Websocket-Key? (示例取自萤火虫,实际上所有标题)

1.1。还有什么可以做的?

1.2。必须使用来自 http 的相同链接来访问 websocket?仅使用 wss 而不是 https?

  • 在此之后,我尝试将客户端连接到服务器并在 onMessage 方法中接收消息。

    WebSocketContainer container = ContainerProvider.getWebSocketContainer(); Session session = container.connectToServer(ClientEndpoint.class, buildCEC(), new URI("wss://async.example.com/socket.io/1/?t=" + System.currentTimeMillis()));

  • 我尝试在 buildCec() 中插入标头以连接到服务器。 ClientEndpointConfig config = ClientEndpointConfig.Builder.create().build(); Configurator conf = config.getConfigurator();

    `Map<String, List<String>> headers = new HashMap<String, List<String>>();
    
    headers.put("Connection", Arrays.asList("keep-alive", "Upgrade"));
    headers.put("Sec-Websocket-Extensions", Arrays.asList("x-webkit-deflate-frame"));
    headers.put("Sec-Websocket-key", Arrays.asList("cx36sHrtuW9HZAaB6kKa/Q=="));
    headers.put("Sec-Websocket-Version", Arrays.asList("13"));
    headers.put("Upgrade", Arrays.asList("websocket"));
    headers.put("User-Agent", Arrays.asList("Mozill..."));
    
    conf.beforeRequest(headers);
    
    return config;`
    

问题

  1. 如果我解决并收到来自 http 升级请求的 101 状态代码,我的判断是否正确?(我的意思是,我接下来如何处理这些细节?)
  2. 请不要评判我,我刚开始使用java中的websockets,我不太了解javascript,我想用jsr-356在纯java中做这个客户端。我已经用 websockets 做了一些小例子,但是我在任何地方都找不到一个好的客户端,带有标头和更复杂的握手。
  3. 是否可以连接到未知服务器,但不知道所有详细信息?

我正在使用编程端点并导入 javax.websocket.ContainerProvider;用于处理来自 jsr356 的 websocket 连接。

【问题讨论】:

  • 我认为使用一些客户端库会更容易。一些谷歌搜索:eclipse.org/jetty/documentation/current/…
  • 我想了解他们创建 websockets 概念的基础知识,首先,然后使用库。反正我去看看。
  • 将来,您可能希望将这些多主题的问题拆分为单独的问题。

标签: java websocket handshake


【解决方案1】:

这里有很多主题。

  1. 不要将 URLHttpConnection 用于 WebSocket,它基于 HTTP 语义管理输出/输入流,不处理升级连接。使用标准 Socket。
  2. 阅读RFC-6455: The WebSocket Protocol
  3. Sec-WebSocket-Key在RFC6455中被多次提及,它有一个总体目的,用于客户端证明它从服务器接收到一个有效的WebSocket opening handshake。客户端有规则,服务器端有规则,它应该如何处理它,以及它应该如何响应它。 (简而言之,它是客户端选择的随机值,发送到服务器,服务器在 RFC 指定的 GUID 上为这一步添加,计算 SHA-1 哈希,并在其自己的 Sec-WebSocket-Key 中使用此哈希进行响应,客户端验证以确认服务器确实支持 WebSocket)
  4. 仔细阅读5.2. Base Framing Protocol 部分,尤其是围绕 3 种有效载荷长度形式的部分(您必须实现对所有 3 种形式的支持,因为服务器需要规则来验证这一点)' - 请参阅关于这个的其他答案 - Java Websocket Text Frame Error at length >=128?
  5. 至于javax.websocket.server.ServerEndpointConfig.Configurator,你不能调用那些方法。这些在建立连接的过程中由 JSR-356 websocket 服务器实现调用。请参阅我关于此过程的其他答案 - Accessing HttpSession from HttpServletRequest in a Web Socket @ServerEndpoint
  6. 注意帧验证规则 - How to parse and validate a WebSocket frame in Java?

我知道有一些 JSR-356 独立客户端。

    <dependency>
      <groupId>org.eclipse.jetty.websocket</groupId>
      <artifactId>javax-websocket-client-impl</artifactId>
      <version>9.1.0.v20131115</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-websocket</artifactId>
      <version>8.0.0-RC5</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-coyote</artifactId>
      <version>8.0.0-RC5</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.grizzly</groupId>
      <artifactId>grizzly-websockets</artifactId>
      <version>2.3.8</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.tyrus.bundles</groupId>
      <artifactId>tyrus-standalone-client</artifactId>
      <version>1.3.3</version>
    </dependency>

如果您确实编写了自己的 WebSocket 客户端,请务必针对 Autobahn WebSocket Protocol Testsuite 进行测试(每个针对此测试套件编写实现测试的人,它都会成为事实上的协议验证套件)

【讨论】:

  • 感谢您提供如此详细的答复。我会查看你告诉我的内容并通知你。
  • 你好,我阅读了你给我的所有内容,我已经实现了一个小例子,但现在我想解析真实的站点。我使用过 Jetty Client,起初一切似乎都很好,握手提供了 200 个包含所有信息的状态代码。但是当我尝试建立连接时,它仍然处于挂起状态。我要解析的网站使用这个:github.com/LearnBoost/socket.io-spec。我遵循了本文档中的所有步骤,但没有运气。在尝试建立真正的连接时,它仍然处于待处理状态。您是否知道服务器接受了某种形式的接受?
  • 它适用于“ws://echo.websocket.org”,更具体地说,当我说接受时,我指的是某种标题。
  • 您的问题似乎是一长串“……哦,还有一件事……”。 Socket.IO 不是 websocket,它是 websocket 之上的一个复杂层。你的问题是关于 websocket 的。就这个新问题提出另一个问题。不要盯着旧的问题/答案。 Stackoverflow 不是正在进行的对话的论坛,它是一个问答网站。
【解决方案2】:

我认为这是您需要的:

 String secAccept = sha1base64(secKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");

 private String sha1base64(String str) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return Base64.encode(md.digest(str.getBytes()));
    }

【讨论】:

    猜你喜欢
    • 2015-03-16
    • 1970-01-01
    • 2011-04-07
    • 2023-03-05
    • 2021-11-13
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多