red5源码分析---3

red5源码分析—客户端握手

从《red5源码分析—1》中可知,客户端会在RTMPMinaIoHandler的sessionOpened函数中向服务器发送第一次握手请求,即TCP连接建立好了,就开始建立RTMP连接了。
《red5源码分析—1》提到过,在客户端的sessionCreate中,向mina框架的过滤器添加了RTMPEIoFilter过滤器,用来与服务器之间进行RTMP协议的握手,现在来看看这个过滤器是怎么处理RTMP协议的握手过程的。

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) {
            throw new Exception("Receive on unavailable connection - session id: " + sessionId);
        }
        if (log.isTraceEnabled()) {

        }
        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);
        }
    }

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

第一种状态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);
    }

这里主要做了四件事情,第一是将RTMPMinaConnection的状态设置为STATE_CONNECTED,第二是从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);
    }

首先从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

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) {
                        if (log.isDebugEnabled()) {

                        }
                        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:
                throw new IllegalStateException("Invalid RTMP state: " + connectionState);
        }
    }

当与服务器完成连接后,就会进入STATE_CONNECTED状态。在该状态下,接收到的消息有两种选择,第一种要进行加密的处理,这里就不仔细看了,如果不需要加密的处理,就直接调用nextFilter的messageReceived函数,这属于nextFilter在mina框架中最后就会调用RTMPMinaIoHandler的messageReceived函数。
下一章分析服务器端的握手过程。

你可能感兴趣的:(red5源码分析---3)