简体   繁体   English

Safari 5 / iOS,WebSocket握手有时仅适用

[英]Safari 5 / iOS, WebSocket handshake only works sometimes

I have made a class in java that allows me to wrap an existing socket with the WebSocket protocols. 我在Java中创建了一个类,该类使我可以使用WebSocket协议包装现有套接字。 I have everything working for the RFC6445 protocol working and everything works in chrome and FF. 我的所有工作都适用于RFC6445协议,并且一切都适用于chrome和FF。 However Safari and iOS is using the hixie76 / HyBi00 protocol (according to Wikipedia). 但是Safari和iOS使用的是hixie76 / HyBi00协议(根据Wikipedia)。

I have everything working and Safari and iOS correctly handshake and start sending/receiving messages... well, at least most of the time. 我一切正常,Safari和iOS正确握手并开始发送/接收消息……至少在大多数时间里。

About 20-30% of the time, the handshake fails and Safari closes the connection. 大约20-30%的时间,握手失败并且Safari关闭了连接。 (Java reads a -1 byte upon trying to read first frame). (Java在尝试读取第一帧时会读取-1字节)。 Safari does not report any errors in the console, but just calls the onclose event handler. Safari不会在控制台中报告任何错误,而只是调用onclose事件处理程序。

Why would the handshakes only work part of the time? 为什么握手仅在部分时间有效?

Here is my handshake code: 这是我的握手代码:

Note: No exceptions are thrown and the "Handshake Complete" is written to the console. 注意:不会引发任何异常,并且“握手完成”将写入控制台。 But then upon trying to read the first frame the connection is closed. 但是随后在尝试读取第一帧时,连接被关闭。 (Java returns -1 on inst.read()) (Java在inst.read()上返回-1)

// Headers are read in a previous method which wraps the socket using RFC6445
// protocol. If it detects 2 keys it will call this and pass in the headers.
public static MessagingWebSocket wrapOldProtocol(HashMap<String, String> headers, PushbackInputStream pin, Socket sock) throws IOException, NoSuchAlgorithmException {
    // SPEC
    // http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#page-32

    // Read the "key3" value. This is 8 random bytes after the headers.
    byte[] key3 = new byte[8];
    for ( int i=0; i<key3.length; i++ ) {
        key3[i] = (byte)pin.read();
    }

    // Grab the two keys we need to use for the handshake
    String key1 = headers.get("Sec-WebSocket-Key1");
    String key2 = headers.get("Sec-WebSocket-Key2");

    // Count the spaces in both keys
    // Abort the connection is either key has 0 spaces
    int spaces1 = StringUtils.countMatches(key1, " ");
    int spaces2 = StringUtils.countMatches(key2, " ");
    if ( spaces1 == 0 || spaces2 == 0 ) {
        throw new IOException("Bad Handshake Request, Possible Cross-protocol attack");
    }

    // Strip all non-digit characters from each key
    // Use the remaining value as a base-10 integer.
    // Abort if either number is not a multiple of it's #spaces counterpart
    // Need to use long because the values are unsigned
    long num1 = Long.parseLong( key1.replaceAll("\\D", "") );
    long num2 = Long.parseLong( key2.replaceAll("\\D", "") );
    if ( !(num1 % spaces1 == 0) || !(num2 % spaces2 == 0) ) {
        throw new IOException("Bad Handshake Request. Possible non-conforming client");
    }

    // Part1/2 is key num divided by the # of spaces
    int part1 = (int)(num1 / spaces1);
    int part2 = (int)(num2 / spaces2);

    // Now calculate the challenge response
    // MD5( num1 + num2 + key3 )  ... concat, not add
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(ByteBuffer.allocate(4).putInt(part1));
    md.update(ByteBuffer.allocate(4).putInt(part2));
    md.update(key3);
    byte[] response = md.digest();

    // Now build the server handshake response
    // Ignore Sec-WebSocket-Protocol (we don't use this)
    String origin = headers.get("Origin");
    String location = "ws://" + headers.get("Host") + "/";

    StringBuilder sb = new StringBuilder();
    sb.append("HTTP/1.1 101 WebSocket Protocol Handshake").append("\r\n");
    sb.append("Upgrade: websocket").append("\r\n");
    sb.append("Connection: Upgrade").append("\r\n");
    sb.append("Sec-WebSocket-Origin: ").append(origin).append("\r\n");
    sb.append("Sec-WebSocket-Location: ").append(location).append("\r\n");
    sb.append("\r\n");

    // Anything left in the buffer?
    if ( pin.available() > 0 ) {
        throw new IOException("Unexpected bytes after handshake!");
    }

    // Send the handshake & challenge response
    OutputStream out = sock.getOutputStream();
    out.write(sb.toString().getBytes());
    out.write(response);
    out.flush();

    System.out.println("[MessagingWebSocket] Handshake Complete.");

    // Return the wrapper socket class.
    MessagingWebSocket ws = new MessagingWebSocket(sock);
    ws.oldProtocol = true;
    return ws;
}

Thanks! 谢谢!

Note: I am not looking for third-party alternatives for WebSockets such at jWebSocket, Jetty and Socket.IO. 注意:我不是在寻找WebSocket的第三方替代品,例如jWebSocket,Jetty和Socket.IO。 I already know about many of these. 我已经知道很多。

Your MD5 digest method has a bug: protocol described as below: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.2 您的MD5摘要方法有一个错误:协议描述如下: http : //tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.2

byte[] bytes = new byte[16];    
BytesUtil.fillBytesWithArray(bytes, 0, 3, BytesUtil.intTobyteArray(part1));
BytesUtil.fillBytesWithArray(bytes, 4, 7, BytesUtil.intTobyteArray(part2));
BytesUtil.fillBytesWithArray(bytes, 8, 15, key3);

I think your problem is caused by Little Endian and Big Endian. 我认为您的问题是由Little Endian和Big Endian引起的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM