Jetty 8.1.1 Websocket 客户端握手

Posted

技术标签:

【中文标题】Jetty 8.1.1 Websocket 客户端握手【英文标题】:Jetty 8.1.1 Websocket client handshake 【发布时间】:2013-11-04 14:47:32 【问题描述】:

我正在使用 Jetty 8.1.1 Websocket 客户端 API。 我需要用("Sec-WebSocket-Protocol", "xsCrossfire")("Authorization", "Basic TLVWQMZqRr2hasYnZoI=") 更新标题

WebSocketClientFactory factory = new WebSocketClientFactory();
factory.start();
client = factory.newWebSocketClient();
client.getCookies().put("Sec-WebSocket-Protocol", "xsCrossfire");
client.getCookies().put("Authorization", "Basic TLVWQMZqRr2hasYnZoI=");
Future<Connection> conn = client.open(uri, (WebSocket) this);
System.out.printf("Connecting to : %s%n", uri);

请求外观:

Host: iltlvl262:8000\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Version: 13\r\n
Sec-WebSocket-Key: FHKTsICO2vqGCxXVwLkH4Q==\r\n
Cookie: Sec-WebSocket-Protocol=xsCrossfire\r\n
Cookie: Authorization="Basic TLVWQMZqRr2hasYnZoI="\r\n

预期请求:

Host: iltlvl262:8000\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Version: 13\r\n
Sec-WebSocket-Key: FHKTsICO2vqGCxXVwLkH4Q==\r\n
Sec-WebSocket-Protocol: xsCrossfire\r\n
Authorization: "Basic TLVWQMZqRr2hasYnZoI="\r\n

如何在 8.1.1 版本中正确实现握手?

【问题讨论】:

您能告诉我您是如何设法从 WebSocketClient 获得原始请求和响应的吗?我整天都在努力得到它们。 【参考方案1】:

一些好消息和一些坏消息。

首先,好消息:

要设置Sec-WebSocket-Protocol 标头,请使用以下内容。

client.setProtocol("xsCrossfire");

在你使用client.open()之前

接下来是坏消息:

使用 Jetty 8.x,您无法设置任意非 websocket 标头。这是由于早期的 WebSocket 实验草案是如何编写的。您根本不允许根据早期草案规范设置任意标头,因此在 Jetty 8.x 时代的实现只是不允许这样做。

然而,随着 RFC6455(官方 WebSocket 规范)的最终确定,事情发生了变化,所有这些变化都被纳入了 Jetty 9.x 代码库。这是 100% RFC6455 兼容的。 (注意:Jetty 8 在服务器端 100% 符合 RFC6455。Jetty 8 在服务器和客户端使用的 RFC6455 协议上也 100% 兼容。但是,Jetty 8 在客户端仅部分符合,从功能和 API 的角度来看。)

Jetty 7 和 Jetty 8 的决定是为了保留旧的实验性草案,以供那些早期采用者和仍然使用它们的旧浏览器 (Safari 5.x) 使用。这一决定使我们无法允许在旧的实验草案中明确禁止的行为。

从 Jetty 9.x 开始,所有旧的 websocket 实验草案都被删除,只留下 RFC6455 支持,这允许 Jetty 开放更多以前不允许的功能。这包括 WebSocketClient 上的任意标头。

Jetty 9.1 WebSocket 客户端示例

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.Future;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;

public class ExampleClient

    public static class ExampleSocket extends WebSocketAdapter
    
        @Override
        public void onWebSocketText(String message)
        
            try
            
                // echo the message
                getRemote().sendString(message);
            
            catch (IOException e)
            
                e.printStackTrace();
            
        
    
    
    public static void main(String[] args)
    
        try
        
            new ExampleClient().demo();
        
        catch (Throwable t)
        
            t.printStackTrace(System.err);
        
    

    public void demo() throws Exception
    
        WebSocketClient client = new WebSocketClient();
        try
        
            client.start();
            ClientUpgradeRequest request = new ClientUpgradeRequest();
            request.setSubProtocols("xsCrossfire");
            request.setHeader("Authorization","Basic TLVWQMZqRr2hasYnZoI=");
            
            URI wsUri = URI.create("ws://iltlvl262:8000/echo");
            
            ExampleSocket socket = new ExampleSocket();
            Future<Session> future = client.connect(socket,wsUri,request);
            
            future.get(); // wait for connect
            
            socket.getRemote().sendString("hello"); // send message
        
        finally
        
            client.stop();
        
    

另请注意,从 Jetty 9.1 开始,甚至完全支持 javax.websocket (JSR-356) API。

在 Jetty 9.1 上使用 javax.websocket 的相同示例

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

public class ExampleClient

    @ClientEndpoint(subprotocols =  "xsCrossfire" , 
                    configurator = ExampleClient.Configurator.class)
    public static class ExampleSocket
    
        @OnMessage
        public String onMessage(String msg)
        
            return msg; // echo
        
    

    public static class Configurator 
           extends javax.websocket.ClientEndpointConfig.Configurator
    
        @Override
        public void beforeRequest(Map<String, List<String>> headers)
        
            List<String> authvalues = new ArrayList<>();
            authvalues.add("Basic TLVWQMZqRr2hasYnZoI=");
            headers.put("Authorization", authvalues);
            super.beforeRequest(headers);
        
    

    public static void main(String[] args)
    
        try
        
            new ExampleClient().demo();
        
        catch (Throwable t)
        
            t.printStackTrace(System.err);
        
    

    public void demo() throws Exception
    
        WebSocketContainer client = ContainerProvider.getWebSocketContainer();

        ExampleSocket socket = new ExampleSocket();
        URI wsUri = URI.create("ws://iltlvl262:8000/echo");

        Session session = client.connectToServer(socket,wsUri);
        session.getAsyncRemote().sendText("Hello");
    

【讨论】:

您好 Erdfelt,感谢您的详细解释。我切换到 Jetty 9,但出现以下异常: java.lang.UnsupportedClassVersionError: org/eclipse/jetty/websocket/api/WebSocketListener : Unsupported major.minor version 51.0 这是什么意思,我该如何解决这个问题?谢谢,汤姆 有关“major.minor version 51.0”的消息意味着您正在尝试在早期 Java 上使用 Java 1.7 类。

以上是关于Jetty 8.1.1 Websocket 客户端握手的主要内容,如果未能解决你的问题,请参考以下文章

Jetty websocket 客户端类 WebSocketClient 线程安全吗?

Jetty9 WebSocket 客户端 - SessionFactory.createSession 导致 java.lang.NullPointerException

让 Jetty Websocket 使用与 JavaScript Websocket 相同的语言

使 WebSocket 发送非阻塞

Jetty 9.2.x Websocket Server连接在使用Firefox而不是Chrome时连接后自动关闭

用于Rabbitmq Stomp的java websocket客户端