2017-01-15 5 views
2

Я попытался реализовать протокол WebSocket и подключить JavaScript WebSocket к серверу Java WebSocket.Рукопожатие между JS WebSocket и Java

Часть JavaScript довольно проста и работает должным образом. Я сам написал сервер Java и прочитал rfc 6455 page 7 для правильного ответа на подтверждение. Таким образом, сервер генерирует правильный ответ и отправляет его. Я написал макет для Java-клиента, чтобы убедиться, что он отправляет.

Но проблема в том, что JavaScript/Браузер, похоже, не получил ответ от рукопожатия и убивает запрос через несколько секунд (но не закрывает сокет tcp).

Вот рукопожатия:

Client

GET/HTTP/1.1 
Host: 127.0.0.1:4455 
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: de,en-US;q=0.7,en;q=0.3 
Accept-Encoding: gzip, deflate 
Sec-WebSocket-Version: 13 
Origin: http://localhost 
Sec-WebSocket-Extensions: permessage-deflate 
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== 
Connection: keep-alive, Upgrade 
Pragma: no-cache 
Cache-Control: no-cache 
Upgrade: websocket 

Сервер

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket 
Connection: Upgrade 
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= 
Sec-WebSocket-Protocol: chat 

HTML JavaScript

<!DOCTYPE HTML> 
<html> 
    <head> 
     <meta charset="utf-8"> 
     <title>Socket testing - Client</title> 
    </head> 
    <body> 
     <div id="container"></div> 
     <script type="text/javascript"> 

      var socket = new WebSocket('ws://127.0.0.1:4455'); 

      socket.addEventListener("error",function(e){ 
       console.log("an error ocurred: ",e) 
      }) 
      socket.addEventListener("close",function(e){ 
       console.log("the connection was closed: ",e) 
      }) 
      socket.addEventListener("open",function(e){ 
       console.log("the connection was opened: ",e) 
      }) 
      socket.addEventListener("message",function(e){ 
       console.log("recieved a message: ",e) 
      }) 

     </script> 
    </body> 
</html> 

Java (отрывок)

public class SocketHandlerWebSocketLevel extends SocketHandler { 

    private HashMap<String, String> connectionHeaders; 
    private InputStreamReader stringReader; 
    private OutputStreamWriter stringWriter; 

    public SocketHandlerWebSocketLevel(Socket socket) { 
     super(socket); 
     connectionHeaders = new HashMap<String, String>(); 

     try { 
      stringReader = new InputStreamReader(s.getInputStream()); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      close(); 
      print("could not get the input stream"); 
      return; 
     } 

     try { 
      stringWriter = new OutputStreamWriter(s.getOutputStream()); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      close(); 
      print("could not get the output stream"); 
      return; 
     } 
    } 

    @Override 
    public void run() { 

     print("Started handler"); 
     char b; 
     String buffer = ""; 
     try { 
      mainLoop: while (true) { 
       while (stringReader.ready() || buffer.length() == 0) { 
        if ((b = (char) stringReader.read()) != -1) { 
         buffer += b; 
        } else { 
         break mainLoop; 
        } 

       } 
       gotMessage(buffer); 
       buffer = ""; 
      } 
     } catch (IOException e) { 
      close(); 
      print("connection was killed remotly, could not read the next byte"); 
      return; 
     } 

     close(); 
     print("connection was closed remotely, stopped Handler, closed socked"); 
    } 

    private void gotMessage(String message) { 
     if (connectionHeaders.size() == 0) { 
      connectionHeaders = parseHttpHeader(message); 
      handshakeResponse(); 
     } else { 
      print(message); 
     } 
    } 

    private void handshakeResponse() { 
     /* 
      taken from: https://tools.ietf.org/html/rfc6455#page-7 
      For this header field, the server has to take the value (as present 
      in the header field, e.g., the base64-encoded [RFC4648] version minus 
      any leading and trailing whitespace) and concatenate this with the 
      Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA- 
      95CA-C5AB0DC85B11" in string form, which is unlikely to be used by 
      network endpoints that do not understand the WebSocket Protocol. A 
      SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of 
      [RFC4648]), of this concatenation is then returned in the server's 
      handshake. 

      Concretely, if as in the example above, the |Sec-WebSocket-Key| 
      header field had the value "dGhlIHNhbXBsZSBub25jZQ==", the server 
      would concatenate the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 
      to form the string "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA- 
      C5AB0DC85B11". The server would then take the SHA-1 hash of this, 
      giving the value 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 
      0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. This value is 
      then base64-encoded (see Section 4 of [RFC4648]), to give the value 
      "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=". This value would then be echoed in 
      the |Sec-WebSocket-Accept| header field. 
     */ 

     String secWebSocketKey, secWebSocketAccept, GUID, template, merged, toSend; 
     secWebSocketKey = connectionHeaders.get("Sec-WebSocket-Key"); 
     GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 
     template = "HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: %s\nSec-WebSocket-Protocol: chat\n"; 

     // combine secWebSocketKey and the GUID 
     merged = secWebSocketKey + GUID; 
     print("merged: " + merged); 

     // convert to byte[] 
     byte[] asBytes = merged.getBytes(); 
     print("asBytes: " + Arrays.toString(asBytes)); 

     // SHA-1 hash 
     byte[] sha1 = SHA1Hash(asBytes); 
     print("sha1: " + Arrays.toString(sha1)); 

     // base64 encode 
     byte[] base64 = base64Encode(sha1); 
     print("base64: " + Arrays.toString(base64)); 

     // reconvert to string to put it into the template 
     secWebSocketAccept = new String(base64); 

     toSend = String.format(template, secWebSocketAccept); 
     print(toSend); 

     try { 
      stringWriter.write(toSend, 0, toSend.length()); 
      stringWriter.flush(); 
     } catch (IOException e) { 
      print("hanshake sending failed!"); 
     } 

    } 

    private HashMap<String, String> parseHttpHeader(String h) { 
     HashMap<String, String> fields = new HashMap<String, String>(); 

     String[] rows = h.split("\n"); 
     if (rows.length > 1) { 
      fields.put("Prototcol", rows[0]); 
      Pattern pattern = Pattern.compile("^([^:]+): (.+)$"); 
      for (int i = 1; i < rows.length; i++) { 
       Matcher matcher = pattern.matcher(rows[i]); 
       while (matcher.find()) { 
        if (matcher.groupCount() == 2) { 
         fields.put(matcher.group(1), matcher.group(2)); 
        } 
       } 
      } 
     } 
     return fields; 
    } 

    private byte[] SHA1Hash(byte[] bytes) { 
     MessageDigest md; 

     try { 
      md = MessageDigest.getInstance("SHA-1"); 
     } catch (NoSuchAlgorithmException e) { 
      return null; 
     } 

     md.update(bytes); 
     return md.digest(); 
    } 

    private byte[] base64Encode(byte[] bytes) { 
     byte[] encodedBytes = Base64.getEncoder().encode(bytes); 
     return encodedBytes; 
    } 

Где моя ошибка? Что может отсутствовать, может быть, символ «конец сообщения»?

Обратите внимание, что я не хочу использовать фреймворк.

ответ

2

Решение было простым. Я просто использовал Wireshark для отладки всего этого: я просто забыл возврат каретки.

Правильная строка в классе Java будет:

template = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\nSec-WebSocket-Protocol: chat\r\n\r\n"; 

До этой модификации браузера не в состоянии интерпретировать его как HTTP данных в пакете TCP.

+0

Я только что заметил, что вы отменили одно из моих исправлений, в котором я обрезал какой-то болтливый материал. Я бы предложил [это обсуждение сообщества] (http://meta.stackoverflow.com/q/295810/472495). Если вы новый пользователь, я бы рекомендовал прочитать сайт _Meta_ в целом, чтобы понять некоторые способы редактирования сообщества, которые часто включают в себя обрезку любого материала, не связанного с вопросом или ответом. Я также готов обсуждать свое собственное обоснование редактирования, если вы пожелаете. – halfer

+0

@halfer извините, я был просто доволен хорошей поддержкой Wireshark, поэтому эта отладка была возможна. Я удалю его. – Feirell

+0

Ах, справедливо, спасибо! – halfer

 Смежные вопросы

  • Нет связанных вопросов^_^