java_websocket详解(二)

上一篇讲解一下draft_10,draft_17,这一篇讲解一下draft_76。
GET /chat HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: localhost:8080             (客户端请求主机)
Origin: http://127.0.0.1         (来源网页地址)
Sec-WebSocket-Key1: 23 asdfJKj,asdjk
Sec_WebSocket-Key2: wewerw234 jij998

draft_75是没有Sec-WebSocket-Key1和Sec-WebSocket-Key2这两个值得。”/chat”一般为uri的path或者query,首先客户端是发一个握手请求,握手的请求是没有数据的,java_websocket是随机生成的一个数据。

public ClientHandshakeBuilder postProcessHandshakeRequestAsClient( ClientHandshakeBuilder request ) {
        request.put( "Upgrade", "WebSocket" );
        request.put( "Connection", "Upgrade" );
        request.put( "Sec-WebSocket-Key1", generateKey() );
        request.put( "Sec-WebSocket-Key2", generateKey() );

        if( !request.hasFieldValue( "Origin" ) ) {
            request.put( "Origin", "random" + reuseableRandom.nextInt() );
        }

        byte[] key3 = new byte[ 8 ];
        reuseableRandom.nextBytes( key3 );
        request.setContent( key3 );
        return request;

    }
private static String generateKey() {
        Random r = new Random();
        long maxNumber = 4294967295L;
        long spaces = r.nextInt( 12 ) + 1;
        int max = new Long( maxNumber / spaces ).intValue();
        max = Math.abs( max );
        int number = r.nextInt( max ) + 1;
        long product = number * spaces;
        String key = Long.toString( product );
        // always insert atleast one random character
        int numChars = r.nextInt( 12 ) + 1;
        for( int i = 0 ; i < numChars ; i++ ) {
            int position = r.nextInt( key.length() );
            position = Math.abs( position );
            char randChar = (char) ( r.nextInt( 95 ) + 33 );
            // exclude numbers here
            if( randChar >= 48 && randChar <= 57 ) {
                randChar -= 15;
            }
            key = new StringBuilder( key ).insert( position, randChar ).toString();
        }
        for( int i = 0 ; i < spaces ; i++ ) {
            int position = r.nextInt( key.length() - 1 ) + 1;
            position = Math.abs( position );
            key = new StringBuilder( key ).insert( position, "\u0020" ).toString();
        }
        return key;
    }

意思就是生成有数字,字母,空格的字符串。

public HandshakeState acceptHandshakeAsServer( ClientHandshake handshakedata ) {
        if( handshakedata.getFieldValue("Upgrade" ).equals("WebSocket") 
&& handshakedata.getFieldValue("Sec-WebSocket-Key1" ).length()>0 
&& !handshakedata.getFieldValue( "Sec-WebSocket-Key2" ).isEmpty() && handshakedata.hasFieldValue( "Origin" ) )
            return HandshakeState.MATCHED;
        return HandshakeState.NOT_MATCHED;
    }

也就是说,服务端只要验证握手请求有Upgrade,Sec-WebSocket-Key1,Sec-WebSocket-Key2,Origin这几个字段就可以了,然后服务端回一个握手请求,握手成功。而服务端接收到这个这个握手请求,没有取这个content的。

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://127.0.0.1     (来源网页地址)
Sec-WebSocket-Location: ws://localhost:8080/WebSocket/LiveVideo

对应的代码如下:

public HandshakeBuilder postProcessHandshakeResponseAsServer( ClientHandshake request, ServerHandshakeBuilder response ) throws InvalidHandshakeException {
        response.setHttpStatusMessage( "WebSocket Protocol Handshake" );
        response.put( "Upgrade", "WebSocket" );
        response.put( "Connection", request.getFieldValue( "Connection" ) ); // to respond to a Connection keep alive
        response.put( "Sec-WebSocket-Origin", request.getFieldValue( "Origin" ) );
        String location = "ws://" + request.getFieldValue( "Host" ) + request.getResourceDescriptor();
        response.put( "Sec-WebSocket-Location", location );
        String key1 = request.getFieldValue( "Sec-WebSocket-Key1" );
        String key2 = request.getFieldValue( "Sec-WebSocket-Key2" );
        byte[] key3 = request.getContent();
        if( key1 == null || key2 == null || key3 == null || key3.length != 8 ) {
            throw new InvalidHandshakeException( "Bad keys" );
        }
        response.setContent( createChallenge( key1, key2, key3 ) );
        return response;
    }

“WebSocket Protocol Handshake”这个完全是response.setHttpStatusMessage( “WebSocket Protocol Handshake” );代码写上去的,握手的时候,把客户端发的content进行了一些运算然后发回去了。
再看一下握手成功之后发消息。

    public HandshakeState acceptHandshakeAsClient( ClientHandshake request, ServerHandshake response ) {
        if( failed ) {
            return HandshakeState.NOT_MATCHED;
        }

        try {
            if( !response.getFieldValue( "Sec-WebSocket-Origin" ).equals( request.getFieldValue( "Origin" ) ) || !basicAccept( response ) ) {
                return HandshakeState.NOT_MATCHED;
            }
            byte[] content = response.getContent();
            if( content == null || content.length == 0 ) {
                throw new IncompleteHandshakeException();
            }
            if( Arrays.equals( content, createChallenge( request.getFieldValue( "Sec-WebSocket-Key1" ), request.getFieldValue( "Sec-WebSocket-Key2" ), request.getContent() ) ) ) {
                return HandshakeState.MATCHED;
            } else {
                return HandshakeState.NOT_MATCHED;
            }
        } catch ( InvalidHandshakeException e ) {
            throw new RuntimeException( "bad handshakerequest", e );
        }
    }

key1,key2的值进行了一些运算跟发送的内容相同,服务器就会处理那个消息。算法如下:

public static byte[] createChallenge( String key1, String key2, byte[] key3 ) throws InvalidHandshakeException {
        byte[] part1 = getPart( key1 );
        byte[] part2 = getPart( key2 );
        byte[] challenge = new byte[ 16 ];
        challenge[ 0 ] = part1[ 0 ];
        challenge[ 1 ] = part1[ 1 ];
        challenge[ 2 ] = part1[ 2 ];
        challenge[ 3 ] = part1[ 3 ];
        challenge[ 4 ] = part2[ 0 ];
        challenge[ 5 ] = part2[ 1 ];
        challenge[ 6 ] = part2[ 2 ];
        challenge[ 7 ] = part2[ 3 ];
        challenge[ 8 ] = key3[ 0 ];
        challenge[ 9 ] = key3[ 1 ];
        challenge[ 10 ] = key3[ 2 ];
        challenge[ 11 ] = key3[ 3 ];
        challenge[ 12 ] = key3[ 4 ];
        challenge[ 13 ] = key3[ 5 ];
        challenge[ 14 ] = key3[ 6 ];
        challenge[ 15 ] = key3[ 7 ];
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance( "MD5" );
        } catch ( NoSuchAlgorithmException e ) {
            throw new RuntimeException( e );
        }
        return md5.digest( challenge );
    }
    private static byte[] getPart( String key ) throws InvalidHandshakeException {
        try {
            long keyNumber = Long.parseLong( key.replaceAll( "[^0-9]", "" ) );
            long keySpace = key.split( "\u0020" ).length - 1;
            if( keySpace == 0 ) {
                throw new InvalidHandshakeException( "invalid Sec-WebSocket-Key (/key2/)" );
            }
            long part = new Long( keyNumber / keySpace );
            return new byte[]{ (byte) ( part >> 24 ), (byte) ( ( part << 8 ) >> 24 ), (byte) ( ( part << 16 ) >> 24 ), (byte) ( ( part << 24 ) >> 24 ) };
        } catch ( NumberFormatException e ) {
            throw new InvalidHandshakeException( "invalid Sec-WebSocket-Key (/key1/ or /key2/)" );
        }
    }
算法步骤key1的逻辑:取出key1中的数字keyNumber跟空格的个数keySpace,相除之后得到long型part,转成byte,例如最后算出来part=0xa123e456,得到的就是[a1,23,e4,56]。key2的算法也一下,把key1,key2,key3串起来就是。
总结,websocket协议网络上一抓一大把,没有的版本不尽相同,对协议没有进行归纳,看起来很头疼。现在协议最新的是draft_17也是最多的。draft_76比draft_17复杂多了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值