red5源码分析---4

red5源码分析—服务器端握手

上一节分析了red5客户端握手的过程,这一节开始分析red5服务器端握手的过程。
和客户端的分析类似,在red5服务器端TCP连接建立后,会调用sessionCreated函数,在该函数中会注册RTMPEIoFilter过滤器,TCP连接建立后,所有的消息就会经过该过滤器,握手的消息也不例外,下面就来看该过滤器的messageReceived函数,

    public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
        String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
        if (sessionId != null) {
            RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
            if (conn == null) {
                throw new Exception();
            }
            RTMP rtmp = conn.getState();
            final byte connectionState = conn.getStateCode();
            IoBuffer message = (IoBuffer) obj;
            InboundHandshake handshake = null;
            switch (connectionState) {
                case RTMP.STATE_CONNECT:
                    ...
                    break;
                case RTMP.STATE_HANDSHAKE:
                    ...
                    break;
                case RTMP.STATE_CONNECTED:
                    ...
                    break;
                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

STATE_CONNECT是客户端和服务器还没握手前该连接所处的状态,

    public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
            ...
            switch (connectionState) {
                case RTMP.STATE_CONNECT:
                    handshake = (InboundHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE);
                    handshake.addBuffer(message);
                    int c0c1Size = handshake.getBufferSize();
                    if (c0c1Size >= (Constants.HANDSHAKE_SIZE + 1)) {
                        IoBuffer buf = handshake.getBufferAsIoBuffer();
                        byte connectionType = buf.get();
                        handshake.setHandshakeType(connectionType);
                        byte[] dst = new byte[Constants.HANDSHAKE_SIZE];
                        buf.get(dst);
                        rtmp.setState(RTMP.STATE_HANDSHAKE);
                        int remaining = buf.remaining();
                        if (remaining > 0) {
                            handshake.addBuffer(buf);
                            log.trace("Stored {} bytes for later decoding", remaining);
                        }
                        IoBuffer s1 = handshake.decodeClientRequest1(IoBuffer.wrap(dst));
                        if (s1 != null) {
                            session.write(s1);
                        } else {
                            conn.close();
                        }
                    }
                    break;
                case RTMP.STATE_HANDSHAKE:
                    ...
                    break;
                case RTMP.STATE_CONNECTED:
                    ...
                    break;
                case RTMP.STATE_ERROR:
                case RTMP.STATE_DISCONNECTING:
                case RTMP.STATE_DISCONNECTED:
                    break;
                default:
                    throw new IllegalStateException("Invalid RTMP state: " + connectionState);
            }
        }
    }

InboundHandshake是在《red5源码分析—2》中当服务器端TCP连接建立后Mina框架调用sessionCreated时设置进session中的,下面的代码就和协议相关进行数据处理,如果收到了客户端的C0C1信息,就调用InboundHandshake的decodeClientRequest1方法生成第一次握手信息S1并发送,然后将RTMPMinaConnection的状态设置成STATE_HANDSHAKE。

第二种状态STATE_HANDSHAKE

STATE_HANDSHAKE状态代表第二次握手状态,进入该状态并收到客户端发来的C2消息时,就代表握手过程即将完成,下面来看

    public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
            ...
            switch (connectionState) {
                case RTMP.STATE_CONNECT:
                    ...
                    break;
                case RTMP.STATE_HANDSHAKE:
                    handshake = (InboundHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE);
                    handshake.addBuffer(message);
                    int c2Size = handshake.getBufferSize();
                    if (c2Size >= Constants.HANDSHAKE_SIZE) {
                        IoBuffer buf = handshake.getBufferAsIoBuffer();
                        byte[] dst = new byte[Constants.HANDSHAKE_SIZE];
                        buf.get(dst);
                        if (handshake.decodeClientRequest2(IoBuffer.wrap(dst))) {
                            rtmp.setState(RTMP.STATE_CONNECTED);
                            if (handshake.useEncryption()) {
                                rtmp.setEncrypted(true);
                                session.setAttribute(RTMPConnection.RTMPE_CIPHER_IN, handshake.getCipherIn());
                                session.setAttribute(RTMPConnection.RTMPE_CIPHER_OUT, handshake.getCipherOut());
                            } 
                            session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE);
                            session.getFilterChain().addAfter("rtmpeFilter", "protocolFilter", new ProtocolCodecFilter(new RTMPMinaCodecFactory()));
                            if (buf.hasRemaining()) {
                                nextFilter.messageReceived(session, buf);
                            }
                        } else {
                            conn.close();
                            break;
                        }
                    }
                    break;
                case RTMP.STATE_CONNECTED:
                    ...
                    break;
                case RTMP.STATE_ERROR:
                case RTMP.STATE_DISCONNECTING:
                case RTMP.STATE_DISCONNECTED:
                    break;
                default:
                    throw new IllegalStateException("Invalid RTMP state: " + connectionState);
            }
        }
    }

这里主要处理客户端发过来的C2消息,生成S2消息,将连接状态设置为STATE_CONNECTED,从session中移除InboundHandshake,节省内存,因为握手过程结束了就用不到了。

第三种状态STATE_CONNECTED

当握手过程结束后,所有发自客户端的消息就会进入该状态,

    public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception {
            ...
            switch (connectionState) {
                case RTMP.STATE_CONNECT:
                    ...
                    break;
                case RTMP.STATE_HANDSHAKE:
                    ...
                    break;
                case RTMP.STATE_CONNECTED:
                    if (!rtmp.isEncrypted()) {
                        nextFilter.messageReceived(session, message);
                    } else {
                        Cipher cipher = (Cipher) session.getAttribute(RTMPConnection.RTMPE_CIPHER_IN);
                        if (cipher != null) {
                            if (log.isDebugEnabled()) {
                                log.debug("Decrypting message: {}", message);
                            }
                            byte[] encrypted = new byte[message.remaining()];
                            message.get(encrypted);
                            message.clear();
                            message.free();
                            byte[] plain = cipher.update(encrypted);
                            IoBuffer messageDecrypted = IoBuffer.wrap(plain);
                            nextFilter.messageReceived(session, messageDecrypted);
                        }
                    }
                    break;
                case RTMP.STATE_ERROR:
                case RTMP.STATE_DISCONNECTING:
                case RTMP.STATE_DISCONNECTED:
                    break;
                default:
                    throw new IllegalStateException("Invalid RTMP state: " + connectionState);
            }
        }
    }

在该状态下,接收到的消息有两种选择,第一种要进行加密的处理,这里就不仔细看了,如果不需要加密的处理,就直接调用nextFilter的messageReceived函数,这属于nextFilter在mina框架中最后就会调用RTMPMinaIoHandler的messageReceived函数。
分析到这里,客户端已经与服务器端建立了TCP连接,并且结束了RTMP的握手过程,但是在处理业务逻辑前,客户端和服务器还需要就带宽,RTMP chunk size即数据块大小进行沟通,下面的章节就开始分析这些源代码。

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