red5源码分析---3

red5源码分析—客户端握手

在《red5源码分析—1》中提到过,客户端会在RTMPMinaIoHandler的sessionOpened函数中执行OutboundHandshake的generateClientRequest1函数生成第一次RTMP握手请求,在建立TCP连接之后,开始建立RTMP连接。
另外,根据《red5源码分析—1》的分析,客户端的sessionCreate函数向mina框架添加了RTMPEIoFilter过滤器,用来与服务器之间进行RTMP协议的握手,现在来看看这个过滤器是怎么处理RTMP协议的握手过程的。在mina框架建立玩TCP连接后,后续的数据会直接经过各个过滤器的messageReceived函数,因此,下面来看RTMPEIoFilter的messageReceived函数,

public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
        String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
        RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
        if (conn == null) {

        }
        RTMP rtmp = conn.getState();
        final byte connectionState = conn.getStateCode();
        IoBuffer message = (IoBuffer) obj;
        OutboundHandshake handshake = null;
        switch (connectionState) {
            case RTMP.STATE_CONNECTED:
                ...
            case RTMP.STATE_CONNECT:
                ...
            case RTMP.STATE_HANDSHAKE:
                ...
            case RTMP.STATE_ERROR:
            case RTMP.STATE_DISCONNECTING:
            case RTMP.STATE_DISCONNECTED:
                break;
            default:
                throw new IllegalStateException("Invalid RTMP state: " + connectionState);
        }
    }

RTMPEIoFilter的messageReceived首先获得sessionId,然后根据该sessionId从RTMPConnManager获得RTMPMinaConnection,接着从该RTMPMinaConnection获得该连接的状态,一共有6种状态,STATE_CONNECTED表示连接完成,STATE_CONNECT表示正在连接(握手的第一状态),STATE_HANDSHAKE表示正在握手(握手的第二状态),STATE_ERROR表示发生错误,STATE_DISCONNECTING表示正在关闭连接,STATE_DISCONNECTED表示已关闭连接,RTMP的握手协议只和前三种状态有关,下面一一来看。

一. 状态STATE_CONNECT

RTMPMinaConnection在创建之后,默认的状态就为STATE_CONNECT,因此在《red5源码分析—1》中的sessionOpend中发送完第一次握手请求给服务器并返回时,这里的状态就为STATE_CONNECT。下面来看,

public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
        ...
        switch (connectionState) {
            case RTMP.STATE_CONNECTED:
                ...
            case RTMP.STATE_CONNECT:
                handshake = (OutboundHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE);
                handshake.addBuffer(message);
                int s0s1Size = handshake.getBufferSize();
                if (s0s1Size >= (Constants.HANDSHAKE_SIZE + 1)) {
                    int handshakeType = handshake.getHandshakeType();
                    if (handshakeType == 0) {
                        handshake.setHandshakeType(RTMPConnection.RTMP_NON_ENCRYPTED);
                        rtmp.setEncrypted(handshake.useEncryption());
                    }
                    IoBuffer buf = handshake.getBufferAsIoBuffer();
                    byte connectionType = buf.get();
                    if (handshake.getHandshakeType() != connectionType) {

                    }
                    byte[] dst = new byte[Constants.HANDSHAKE_SIZE];
                    buf.get(dst);
                    int remaining = buf.remaining();
                    if (remaining > 0) {
                        handshake.addBuffer(buf);
                    }
                    IoBuffer c2 = handshake.decodeServerResponse1(IoBuffer.wrap(dst));
                    if (c2 != null) {
                        conn.getState().setState(RTMP.STATE_HANDSHAKE);
                        session.write(c2);
                        if (handshake.getBufferSize() >= Constants.HANDSHAKE_SIZE) {
                            buf.clear();
                            buf = handshake.getBufferAsIoBuffer();
                            if (handshake.decodeServerResponse2(buf)) {

                            } else {

                            }
                            completeConnection(session, conn, rtmp, handshake);
                        }
                    } else {
                        conn.close();
                    }
                }
                break;
            case RTMP.STATE_HANDSHAKE:
                ...
            case RTMP.STATE_ERROR:
            case RTMP.STATE_DISCONNECTING:
            case RTMP.STATE_DISCONNECTED:
                break;
            default:
                throw new IllegalStateException("Invalid RTMP state: " + connectionState);
        }
    }

OutboundHandshake是在《red5源码分析—1》中当TCP连接建立后Mina框架调用sessionCreated时设置进session中的,下面的代码就和协议相关进行数据处理,如果收到了服务器的S0S1信息,就调用OutboundHandshake的decodeServerResponse1方法生成第二次握手信息并发送,然后将RTMPMinaConnection的状态设置成STATE_HANDSHAKE;如果收到了服务器的S0S1+S2信息,就直接判断为成功,并调用completeConnection函数,后面来看这个函数。

二. 状态STATE_HANDSHAKE

STATE_HANDSHAKE状态代表第二次握手状态,进入该状态并收到服务器消息时,就代表握手过程即将完成,下面来看

public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
        ...
        switch (connectionState) {
            case RTMP.STATE_CONNECTED:
                ...
            case RTMP.STATE_CONNECT:
                ...
            case RTMP.STATE_HANDSHAKE:
                handshake = (OutboundHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE);
                handshake.addBuffer(message);
                int s2Size = handshake.getBufferSize();
                if (s2Size >= Constants.HANDSHAKE_SIZE) {
                    IoBuffer buf = handshake.getBufferAsIoBuffer();
                    byte[] dst = new byte[Constants.HANDSHAKE_SIZE];
                    buf.get(dst);
                    int index = buf.indexOf(handshake.getHandshakeType());
                    if (index != -1) {
                        buf.position(index);
                    }
                    if (handshake.decodeServerResponse2(IoBuffer.wrap(dst))) {

                    } else {

                    }
                    completeConnection(session, conn, rtmp, handshake);
                }
                break;
            case RTMP.STATE_ERROR:
            case RTMP.STATE_DISCONNECTING:
            case RTMP.STATE_DISCONNECTED:
                break;
            default:
                throw new IllegalStateException("Invalid RTMP state: " + connectionState);
        }
    }

STATE_HANDSHAKE状态的处理过程和第一种状态STATE_CONNECT类似,主要来看completeConnection函数,当握手完成时便会调用该函数,

    private void completeConnection(IoSession session, RTMPMinaConnection conn, RTMP rtmp, OutboundHandshake handshake) {
        if (handshake.useEncryption()) {
            rtmp.setEncrypted(true);
            session.setAttribute(RTMPConnection.RTMPE_CIPHER_IN, handshake.getCipherIn());
            session.setAttribute(RTMPConnection.RTMPE_CIPHER_OUT, handshake.getCipherOut());
        }
        conn.getState().setState(RTMP.STATE_CONNECTED);
        session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE);
        session.getFilterChain().addAfter("rtmpeFilter", "protocolFilter", new ProtocolCodecFilter(new RTMPMinaCodecFactory()));
        BaseRTMPClientHandler handler = (BaseRTMPClientHandler) session.getAttribute(RTMPConnection.RTMP_HANDLER);
        handler.connectionOpened(conn);
    }

completeConnection函数主要做了四件事情,第一是将RTMPMinaConnection的状态设置为STATE_CONNECTED,表示已经与服务器建立了RTMP连接;第二是从session中移除前面握手过程使用的OutboundHandshake;第三是添加mina框架自带的ProtocolCodecFilter,该过滤器的源码在博主其他博文mina源码分析中有分析到;最后就是获得BaseRTMPClientHandler并调用其connectionOpened函数。
BaseRTMPClientHandler是在《red5源码分析—1》中RTMPClient的构造函数中设置的,其connectionOpened如下

    public void connectionOpened(RTMPConnection conn) {
        Channel channel = conn.getChannel((byte) 3);
        PendingCall pendingCall = new PendingCall("connect");
        pendingCall.setArguments(connectArguments);
        Invoke invoke = new Invoke(pendingCall);
        invoke.setConnectionParams(connectionParams);
        invoke.setTransactionId(1);
        if (connectCallback != null) {
            pendingCall.registerCallback(connectCallback);
        }
        conn.registerPendingCall(invoke.getTransactionId(), pendingCall);
        channel.write(invoke);
    }

connectionOpened函数首先从RTMPConnection中获取Channel,getChannel定义如下,

    public Channel getChannel(int channelId) {
        Channel channel = channels.putIfAbsent(channelId, new Channel(this, channelId));
        if (channel == null) {
            channel = channels.get(channelId);
        }
        return channel;
    }

因此这里就会创建一个Channel并添加进channels中。
回到connectionOpened函数中,接下来就需要向服务器发送命令”connect”,创建PendingCall、Invoke,注册回调函数connectCallback,最后调用Channel的write函数发送该命令,

    public void write(IRTMPEvent event) {
        if (!connection.isClosed()) {
            final IClientStream stream = connection.getStreamByChannelId(id);
            if (id > 3 && stream == null) {

            }
            final Number streamId = (stream == null) ? 0 : stream.getStreamId();
            write(event, streamId);
        } else {

        }
    }

由于这里stream还未创建,因此继续调用write函数,传入的streamId为0,

    private void write(IRTMPEvent event, Number streamId) {
        final Header header = new Header();
        final Packet packet = new Packet(header, event);
        header.setChannelId(id);
        int ts = event.getTimestamp();
        if (ts != 0) {
            header.setTimer(event.getTimestamp());
        }
        header.setStreamId(streamId);
        header.setDataType(event.getDataType());
        connection.write(packet);
    }

注意这里的id为前面的3,streamId为0,getDataType返回TYPE_INVOKE,然后通过mina框架发送该命令,后面的代码和mina框架有关,这里就不往下看了。

三. 状态STATE_CONNECTED

当客户端进入STATE_CONNECTED状态,就表示已经与服务器建立了RTMP连接了,

public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
        ...
        switch (connectionState) {
            case RTMP.STATE_CONNECTED:
                if (rtmp.isEncrypted()) {
                    Cipher cipher = (Cipher) session.getAttribute(RTMPConnection.RTMPE_CIPHER_IN);
                    if (cipher != null) {
                        byte[] encrypted = new byte[message.remaining()];
                        message.get(encrypted);
                        message.clear();
                        message.free();
                        byte[] plain = cipher.update(encrypted);
                        IoBuffer messageDecrypted = IoBuffer.wrap(plain);
                        if (log.isDebugEnabled()) {

                        }
                        nextFilter.messageReceived(session, messageDecrypted);
                    } else {

                    }
                } else {
                    nextFilter.messageReceived(session, obj);
                }
                break;
            case RTMP.STATE_CONNECT:
                ...
            case RTMP.STATE_HANDSHAKE:
                ...
            case RTMP.STATE_ERROR:
            case RTMP.STATE_DISCONNECTING:
            case RTMP.STATE_DISCONNECTED:
                break;
            default:
        }
    }

在STATE_CONNECTED状态下,接收到的消息有两种选择,第一种要进行加密的处理,这里就不仔细看了,如果不需要加密的处理,就直接调用nextFilter的messageReceived函数,这属于nextFilter在mina框架中最后就会调用RTMPMinaIoHandler的messageReceived函数。

总结一下,客户端在与服务器的握手过程中会经历从STATE_CONNECT到STATE_HANDSHAKE再到STATE_CONNECTED三个状态。在握手成功后,会向服务器发送”connect”命令进行第二次连接。《red5源码分析—5》会详细分析服务器如何处理”connect”命令。

下一章将分析服务器端的握手过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值