red5源码分析---5

red5源码分析—服务器接收connect命令

在前两章《red5源码分析—3》中提到,在客户端与服务器握手期间,当客户端接收到red5服务器发送过来的S2信息后,会在connectionOpened函数中向服务器发送”connect”命令,为了方便分析,这里再贴一下客户端的这部分代码,

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

下面就来看服务器是如何处理这个”connect”命令的。服务器接收到的消息首先经过RTMPEIoFilter服务器,根据《red5源码分析—4》的分析可知,这时候连接的状态已经变为STATE_CONNECTED,假设数据不加密,就直接到下一个过滤器,其他的过滤器这里就不分析了,最后会到达RTMPMinaIoHandler的messageReceived函数,

    public void messageReceived(IoSession session, Object message) throws Exception {
        String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
        RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
        if (conn != null) {
            if (message != null) {
                if (message instanceof Packet) {
                    byte state = conn.getStateCode();
                    if (state != RTMP.STATE_DISCONNECTING && state != RTMP.STATE_DISCONNECTED) {
                        conn.handleMessageReceived((Packet) message);
                    } else {

                    }
                }
            }
        } else {
            forceClose(session);
        }
    }

RTMPMinaIoHandler的messageReceived函数根据sessionId获取到RTMPMinaConnection后,就调用其handleMessageReceived函数进行下一步处理。RTMPMinaConnection的handleMessageReceived函数定义在其父类RTMPConnection中,

    public void handleMessageReceived(Packet message) {
        final byte dataType = message.getHeader().getDataType();
        switch (dataType) {
            case Constants.TYPE_PING:
            case Constants.TYPE_ABORT:
            case Constants.TYPE_BYTES_READ:
            case Constants.TYPE_CHUNK_SIZE:
            case Constants.TYPE_CLIENT_BANDWIDTH:
            case Constants.TYPE_SERVER_BANDWIDTH:
                try {
                    handler.messageReceived(this, message);
                } catch (Exception e) {

                }
                break;
            default:
                if (executor != null) {
                    final String messageType = getMessageType(message);
                    try {
                        final long packetNumber = packetSequence.incrementAndGet();
                        if (executorQueueSizeToDropAudioPackets > 0 && currentQueueSize.get() >= executorQueueSizeToDropAudioPackets) {
                            if (message.getHeader().getDataType() == Constants.TYPE_AUDIO_DATA) {
                                return;
                            }
                        }
                        if (maxHandlingTimeout > 0) {
                            message.setExpirationTime(System.currentTimeMillis() + maxHandlingTimeout);
                        }
                        int channelId = message.getHeader().getChannelId();
                        ReceivedMessageTask task = new ReceivedMessageTask(sessionId, message, handler, this);
                        task.setPacketNumber(packetNumber);
                        ReceivedMessageTaskQueue newChannelTasks = new ReceivedMessageTaskQueue(channelId, this);
                        ReceivedMessageTaskQueue currentChannelTasks = tasksByChannels.putIfAbsent(channelId, newChannelTasks);
                        if (currentChannelTasks != null) {
                            currentChannelTasks.addTask(task);
                        } else {
                            newChannelTasks.addTask(task);
                        }
                    } catch (Exception e) {

                    }
                } else {

                }
        }
    }

handleMessageReceived函数首先从消息message中获取dataType,根据《red5源码分析—3》可知,dataType默认为TYPE_INVOKE。executorQueueSizeToDropAudioPackets用于判断是否要丢弃音频数据包,maxHandlingTimeout用来设置每个数据包的最大处理时间,最后创建了一个ReceivedMessageTask并根据channelId通过addTask添加进ReceivedMessageTaskQueue中,

    public void addTask(ReceivedMessageTask task) {
        tasks.add(task);
        Packet packet = task.getPacket();
        if (packet.getExpirationTime() > 0L) {
            task.runDeadlockFuture(new DeadlockGuard(task));
        }
        if (listener != null) {
            listener.onTaskAdded(this);
        }
    }

ReceivedMessageTask实现了Java的Callable借口,因此最终会启用一个线程并执行其call函数,

    public Packet call() throws Exception {
        taskThread = Thread.currentThread();
        Red5.setConnectionLocal(conn);
        try {
            handler.messageReceived(conn, packet);
            packet.setProcessed(true);
        } finally {
            Red5.setConnectionLocal(null);
        }
        return packet;
    }

call函数继续调用了handler的messageReceived函数,这里的handler是在sessionCreated中设置的RTMPHandler,其messageReceived定义在其父类BaseRTMPHandler中,

    public void messageReceived(RTMPConnection conn, Packet packet) throws Exception {
        if (conn != null) {
            IRTMPEvent message = null;
            try {
                message = packet.getMessage();
                final Header header = packet.getHeader();
                final Number streamId = header.getStreamId();
                final Channel channel = conn.getChannel(header.getChannelId());
                final IClientStream stream = conn.getStreamById(streamId);
                conn.setStreamId(streamId);
                conn.messageReceived();
                message.setSource(conn);
                final byte headerDataType = header.getDataType();
                switch (headerDataType) {
                    case TYPE_AGGREGATE:
                    case TYPE_AUDIO_DATA:
                    case TYPE_VIDEO_DATA:
                        message.setSourceType(Constants.SOURCE_TYPE_LIVE);
                        if (stream != null) {
                            ((IEventDispatcher) stream).dispatchEvent(message);
                        }
                        break;
                    case TYPE_FLEX_SHARED_OBJECT:
                    case TYPE_SHARED_OBJECT:
                        onSharedObject(conn, channel, header, (SharedObjectMessage) message);
                        break;
                    case TYPE_INVOKE:
                    case TYPE_FLEX_MESSAGE:
                        onCommand(conn, channel, header, (Invoke) message);
                        IPendingServiceCall call = ((Invoke) message).getCall();
                        if (message.getHeader().getStreamId().intValue() != 0 && call.getServiceName() == null && StreamAction.PUBLISH.equals(call.getServiceMethodName())) {
                            if (stream != null) {
                                ((IEventDispatcher) stream).dispatchEvent(message);
                            }
                        }
                        break;
                    case TYPE_NOTIFY: 
                    case TYPE_FLEX_STREAM_SEND:
                        if (((Notify) message).getData() != null && stream != null) {
                            ((IEventDispatcher) stream).dispatchEvent(message);
                        } else {
                            onCommand(conn, channel, header, (Notify) message);
                        }
                        break;
                    case TYPE_PING:
                        onPing(conn, channel, header, (Ping) message);
                        break;
                    case TYPE_BYTES_READ:
                        onStreamBytesRead(conn, channel, header, (BytesRead) message);
                        break;
                    case TYPE_CHUNK_SIZE:
                        onChunkSize(conn, channel, header, (ChunkSize) message);
                        break;
                    case Constants.TYPE_CLIENT_BANDWIDTH:
                        onClientBandwidth(conn, channel, (ClientBW) message);
                        break;
                    case Constants.TYPE_SERVER_BANDWIDTH:
                        onServerBandwidth(conn, channel, (ServerBW) message);
                        break;
                    default:
                }
                if (message instanceof Unknown) {

                }
            } catch (Throwable t) {

            }
            if (message != null) {
                message.release();
            }
        }
    }

RTMPMinaConnection的messageReceived函数用来做统计,因此这里不分析。下面获得dataType并根据该dataType进行相应的处理,”connect”命令对应的dataType为TYPE_INVOKE,因此调用onCommand进行处理,onCommand定义在RTMPHandler中。因为OnCommand较长,这里分为几个部分分析,

一. OnCommand第一部分

    protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) {
        final IServiceCall call = command.getCall();
        final String action = call.getServiceMethodName();
        if ("_result".equals(action) || "_error".equals(action)) {
            handlePendingCallResult(conn, (Invoke) command);
            return;
        }
        boolean disconnectOnReturn = false;
        boolean connected = conn.isConnected();
        if (connected) {
            ...
        } else {
            if (StreamAction.CONNECT.equals(action)) {
                final Map params = command.getConnectionParams();
                String host = getHostname((String) params.get("tcUrl"));
                String path = (String) params.get("app");
                if (path.indexOf("?") != -1) {
                    int idx = path.indexOf("?");
                    params.put("queryString", path.substring(idx));
                    path = path.substring(0, idx);
                }
                params.put("path", path);
                conn.setup(host, path, params);
                try {
                    IGlobalScope global = server.lookupGlobal(host, path);
                    if (global != null) {
                        final IContext context = global.getContext();
                        IScope scope = null;
                        try {
                            scope = context.resolveScope(global, path);
                            if (scope.getDepth() < 1 && !globalScopeConnectionAllowed) {
                                call.setStatus(Call.STATUS_ACCESS_DENIED);
                                if (call instanceof IPendingServiceCall) {
                                    IPendingServiceCall pc = (IPendingServiceCall) call;
                                    StatusObject status = getStatus(NC_CONNECT_REJECTED);
                                    status.setDescription("Global scope connection disallowed on this server.");
                                    pc.setResult(status);
                                }
                                disconnectOnReturn = true;
                            }
                            if (scope != null) {
                                ...
                            }
                        } catch (ScopeNotFoundException err) {
                            ...
                            disconnectOnReturn = true;
                        } catch (ScopeShuttingDownException err) {
                            ...
                            disconnectOnReturn = true;
                        }
                    } else {
                        ...
                        disconnectOnReturn = true;
                    }
                } catch (RuntimeException e) {
                    ...
                    disconnectOnReturn = true;
                }
                if (new Double(3d).equals(params.get("objectEncoding"))) {
                    ...
                }
            } else {
                conn.close();
            }
        }
        if (command instanceof Invoke) {
            ...
        }
    }

这里传入的action就是”connect”,并且这时候的RTMPMinaConnection连接并没有和任务red5的scope关联,因此connected为false。再往下调用setup函数将主机名host、应用路径path和参数params设置进connection中。接着调用lookupGlobal获取全局Scope,定义在Server中,

    public IGlobalScope lookupGlobal(String hostName, String contextPath) {
        String key = getKey(hostName, contextPath);
        while (contextPath.indexOf(SLASH) != -1) {
            key = getKey(hostName, contextPath);
            String globalName = mapping.get(key);
            if (globalName != null) {
                return getGlobal(globalName);
            }
            final int slashIndex = contextPath.lastIndexOf(SLASH);
            contextPath = contextPath.substring(0, slashIndex);
        }
        key = getKey(hostName, contextPath);
        String globalName = mapping.get(key);
        if (globalName != null) {
            return getGlobal(globalName);
        }
        key = getKey(EMPTY, contextPath);
        globalName = mapping.get(key);
        if (globalName != null) {
            return getGlobal(globalName);
        }
        key = getKey(hostName, EMPTY);
        globalName = mapping.get(key);
        if (globalName != null) {
            return getGlobal(globalName);
        }
        key = getKey(EMPTY, EMPTY);
        return getGlobal(mapping.get(key));
    }

lookupGlobal函数简而言之,就是通过主机名和路径名获取key值,利用key值获取全局scope。

再继续看onCommand函数,获取全局scope后,就调用其context的resolveScope获取本次服务对应的scope。这里的context定义在red5-default.xml中,对应的类为org.red5.server.Context,下面来看它的resolveScope函数,

    public IScope resolveScope(IScope root, String path) {
        return scopeResolver.resolveScope(root, path);
    }

Context的scopeResolver会被Spring自动注入为org.red5.server.scope.ScopeResolver,其resolveScope如下,

    public IScope resolveScope(IScope root, String path) {
        IScope scope = root;
        if (StringUtils.isNotEmpty(path)) {
            final String[] parts = path.split("/");
            for (String child : parts) {
                if (StringUtils.isEmpty(child)) {
                    continue;
                }
                if (!scope.hasChildScope(child) && !scope.equals(root)) {
                    scope.createChildScope(child);
                }
                scope = scope.getScope(child);
                if (scope == null) {
                    throw new ScopeNotFoundException(scope, child);
                }
                if (scope instanceof IScope && scope.getType().equals(ScopeType.APPLICATION) && ((WebScope) scope).isShuttingDown()) {
                    throw new ScopeShuttingDownException(scope);
                }
            }
        }
        return scope;
    }

resolveScope函数首先解析应用路径,对路径里的每个元素调用createChildScope创建子Scope,并返回该Scope,

    public boolean createChildScope(String name) {
        if (children.hasName(name)) {

        } else {
            return addChildScope(new Builder(this, ScopeType.ROOM, name, false).build());
        }
        return false;
    }

    public boolean addChildScope(IBasicScope scope) {
        boolean added = false;
        if (scope.isValid()) {
            try {
                if (!children.containsKey(scope)) {
                    added = children.add(scope);
                } else {

                }
            } catch (Exception e) {

            }
        } else {

        }
        if (added && scope.getStore() == null) {
            try {
                if (scope instanceof Scope) {
                    ((Scope) scope).setPersistenceClass(persistenceClass);
                }
            } catch (Exception error) {

            }
        }
        return added;
    }

这里主要就是调用children的add函数添加一个构造完的Scope,注意构造的Scope的默认类型为ScopeType.ROOM。children的类型为ConcurrentScopeSet,其add函数如下

        public boolean add(IBasicScope scope) {     
            boolean added = false;
            if (!containsKey(scope)) {
                if (hasHandler()) {
                    IScopeHandler hdlr = getHandler();
                    if (!hdlr.addChildScope(scope)) {
                        return false;
                    }
                } else {

                }
                try {
                    if (!containsKey(scope)) {
                        added = (super.put(scope, Boolean.TRUE) == null);
                        if (added) {
                            subscopeStats.increment();
                        } else {

                        }
                    } else {

                    }
                } catch (Exception e) {

                }
                if (added && scope instanceof Scope) {
                    Scope scp = (Scope) scope;
                    if (scp.start()) {

                    } else {

                    }
                }
            }
            return added;
        }

add函数首先调用getHandler获取handler,

    public IScopeHandler getHandler() {
        if (handler != null) {
            return handler;
        } else if (hasParent()) {
            return getParent().getHandler();
        } else {
            return null;
        }
    }

Scope刚被创建时,handler为null,因此这里会向其父Scope获取handler,所有Scope的最上层Scope都为GlobalScope,GlobalScope的handler为org.red5.server.CoreHandler(查看red5-default.xml),但是CoreHandler的addChildScope没有做任何动作,直接返回true。
再往下看,接下来就是调用其父类ConcurrentHashMap的put函数进行添加,再调用increment增加Scope的统计计数,最后调用Scope的start函数,代码如下

    public boolean start() {
        boolean result = false;
        if (enabled && !running) {
            if (handler != null) {
            } else {
                handler = parent.getHandler();
            }
            try {
                if (handler != null) {
                    result = handler.start(this);
                } else {
                    result = true;
                }
            } catch (Throwable e) {

            } finally {
                ((Server) getServer()).notifyScopeCreated(this);
            }
            running = result;
        }
        return result;
    }

这里获取父类的handler并赋值,从前面来看这里的handler就是CoreHandler,其start函数也是什么也没做,直接返回true。再往下就是调用Server类的notifyScopeCreated函数,通知有Scope创建了。这里就不往下看了。
回到addChildScope函数中,创建并成功添加新建的Scope后,就调用setPersistenceClass设置持久化类,根据red5-default.xml的配置,这里为org.red5.server.persistence.FilePersistence。
回到最前面的resolveScope函数中,创建完Scope后,就调用getScope获得该Scope并返回。

    public IScope getScope(String name) {
        IBasicScope child = children.getBasicScope(ScopeType.UNDEFINED, name);
        if (child != null) {
            if (child instanceof IScope) {
                return (IScope) child;
            }
        }
        return null;
    }

    public IBasicScope getBasicScope(ScopeType type, String name) {
        boolean skipTypeCheck = ScopeType.UNDEFINED.equals(type);
        if (skipTypeCheck) {
            for (IBasicScope child : keySet()) {
                if (name.equals(child.getName())) {
                    return child;
                }
            }
        } else {
            for (IBasicScope child : keySet()) {
                if (child.getType().equals(type) && name.equals(child.getName())) {
                    return child;
                }
            }
        }
        return null;
    }

这段代码很简单,这里就不分析了。

OnCommand第二部分

在获取了GlobalScope并在该GlobalScope下添加了相应的Scope后,就需要通过connect连接该Scope,继续往下看

    protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) {
        ...
        if (connected) {
            ...
        } else {
            if (StreamAction.CONNECT.equals(action)) {
                ...
                try {
                    IGlobalScope global = server.lookupGlobal(host, path);
                    if (global != null) {
                        final IContext context = global.getContext();
                        IScope scope = null;
                        try {
                            ...
                            if (scope != null) {
                                boolean okayToConnect;
                                try {
                                    if (call.getArguments() != null) {
                                        okayToConnect = conn.connect(scope, call.getArguments());
                                    } else {
                                        okayToConnect = conn.connect(scope);
                                    }
                                    if (okayToConnect) {
                                        call.setStatus(Call.STATUS_SUCCESS_RESULT);
                                        if (call instanceof IPendingServiceCall) {
                                            IPendingServiceCall pc = (IPendingServiceCall) call;
                                            StatusObject result = getStatus(NC_CONNECT_SUCCESS);
                                            result.setAdditional("fmsVer", Red5.getFMSVersion());
                                            result.setAdditional("capabilities", Red5.getCapabilities());
                                            result.setAdditional("mode", Integer.valueOf(1));
                                            result.setAdditional("data", Red5.getDataVersion());
                                            pc.setResult(result);
                                        }
                                        conn.ping(new Ping(Ping.STREAM_BEGIN, 0, -1));
                                        disconnectOnReturn = false;
                                    } else {
                                        ...
                                    }
                                } catch (ClientRejectedException rejected) {
                                    ...
                                }
                            }
                        } catch (ScopeNotFoundException err) {
                            ...
                            disconnectOnReturn = true;
                        } catch (ScopeShuttingDownException err) {
                            ...
                            disconnectOnReturn = true;
                        }
                    } else {
                        ...
                        disconnectOnReturn = true;
                    }
                } catch (RuntimeException e) {
                    ...
                    disconnectOnReturn = true;
                }
                if (new Double(3d).equals(params.get("objectEncoding"))) {
                    ...
                }
            } else {
                conn.close();
            }
        }
        if (command instanceof Invoke) {
            ...
        }
    }

这里假设call.getArguments()返回null,对应的connect函数如下,定义在BaseConnection中,

    public boolean connect(IScope newScope) {
        return connect(newScope, null);
    }

connect函数会依次调用其父类的connect函数,先来看最上层的connect如何处理的,

    public boolean connect(IScope newScope, Object[] params) {
        scope = (Scope) newScope;
        return scope.connect(this, params);
    }

因此这里就是简单调用新建的Scope进行连接,

    public boolean connect(IConnection conn, Object[] params) {
        if (enabled) {
            if (hasParent() && !parent.connect(conn, params)) {
                return false;
            }
            if (hasHandler() && !getHandler().connect(conn, this, params)) {
                return false;
            }
            if (!conn.isConnected()) {
                return false;
            }
            final IClient client = conn.getClient();
            if (hasHandler() && !getHandler().join(client, this)) {
                return false;
            }
            if (!conn.isConnected()) {
                return false;
            }
            if (clients.add(client) && addEventListener(conn)) {
                connectionStats.increment();
                IScope connScope = conn.getScope();
                if (this.equals(connScope)) {
                    final IServer server = getServer();
                    if (server instanceof Server) {
                        ((Server) server).notifyConnected(conn);
                    }
                }
                return true;
            }
        } else {

        }
        return false;
    }

首先调用该Scope的父Scope进行连接,其父类的连接函数依然为该函数,因此不往下看。接着调用handler的connect函数进行连接,从前面的分析可知,这里的handler就是CoreHandler,其connect函数如下

    public boolean connect(IConnection conn, IScope scope, Object[] params) {
        boolean connect = false;
        String id = conn.getSessionId();
        IScope connectionScope = conn.getScope();
        if (connectionScope != null) {
            IClientRegistry clientRegistry = connectionScope.getContext().getClientRegistry();
            if (clientRegistry != null) {
                IClient client = conn.getClient();
                if (client == null) {
                    if (!clientRegistry.hasClient(id)) {
                        if (conn instanceof RTMPTConnection) {
                            client = new Client(id, (ClientRegistry) clientRegistry);
                            clientRegistry.addClient(client);
                            conn.setClient(client);
                        } else if (conn instanceof RTMPConnection) {
                            client = clientRegistry.newClient(params);
                            conn.setClient(client);
                        }
                    } else {
                        client = clientRegistry.lookupClient(id);
                        conn.setClient(client);
                    }
                } else {
                    conn.setClient(client);
                }
                IConnectionManager connManager = RTMPConnManager.getInstance();
                if (conn instanceof RTMPTConnection) {
                    connManager.setConnection((RTMPTConnection) conn);
                } else if (conn instanceof RTMPConnection) {
                    connManager.setConnection((RTMPConnection) conn);
                } else {

                }
                conn.initialize(client);
                connect = true;
            } else {

            }
        } else {

        }
        return connect;
    }

这里connection中获得的Scope即前面创建好的Scope,其getContext函数会一直向上返回父类的Context,

    public IContext getContext() {
        if (!hasContext() && hasParent()) {
            return parent.getContext();
        } else {
            return context;
        }
    }

因此最上层就是GlobalScope,其getContext返回的是org.red5.server.Context,其对应的getClientRegistry返回org.red5.server.ClientRegistry。
再往下,假设conn中取出的client为null,并且ClientRegistry中没有对应id的信息,这里就会构造一个客户端Client,将刚刚创建的Client添加进ClientRegistry和RTMPMinaConnection中(conn)。
再往下,RTMPConnManager的setConnection函数并没有实质性功能。最后调用了RTMPMinaConnection的initialize函数进行初始化,定义在RTMPMinaConnection的父类BaseConnection中,

    public void initialize(IClient client) {
        if (this.client != null && this.client instanceof Client && !this.client.equals(client)) {
            ((Client) this.client).unregister(this, false);
        }
        this.client = client;
        if (this.client instanceof Client && !((Client) this.client).isRegistered(this)) {
            ((Client) this.client).register(this);
        }
    }

这里其实就是调用Client的register函数,

    protected void register(IConnection conn) {
        if (conn != null) {
            IScope scope = conn.getScope();
            if (scope != null) {
                connections.add(conn);
            } else {

            }
        } else {

        }
    }

这里就是将RTMPMinaConnection注册到Client中的一个集合connections中。
分析完CoreHandler中的connect函数后,再回头看Scope中的connect函数,再往下会继续调用CoreHandler的join函数,该函数简单返回true。再往下就是添加监听器,并同时服务器Server有新的连接了。
到此,分析完了RTMPMinaConnection父类的父类BaseConnection的connect函数,现在回过头看RTMPMinaConnection的父类RTMPConnection的connect函数

    public boolean connect(IScope newScope, Object[] params) {
        try {
            boolean success = super.connect(newScope, params);
            if (success) {
                stopWaitForHandshake();
                startRoundTripMeasurement();
            } else {

            }
            return success;
        } catch (ClientRejectedException e) {
            ...
        }
    }

当BaseConnection的connect函数成功返回后,这里首先调用stopWaitForHandshake取消握手,再调用startRoundTripMeasurement,

    private void startRoundTripMeasurement() {
        if (scheduler != null) {
            if (pingInterval > 0) {
                try {
                    keepAliveTask = scheduler.scheduleAtFixedRate(new KeepAliveTask(), pingInterval);
                } catch (Exception e) {

                }
            }
        } else {

        }
    }

这里其实就是启动了一个KeepAliveTask任务用来保持长连接,后面的章节会结合ping命令来分析这个任务。
最后再来看RTMPMinaConnection自己的connect函数,代码如下,

    public boolean connect(IScope newScope, Object[] params) {
        boolean success = super.connect(newScope, params);
        if (success) {
            final Channel two = getChannel(2);
            two.write(new ServerBW(defaultServerBandwidth));
            two.write(new ClientBW(defaultClientBandwidth, (byte) limitType));
            if (client != null) {
                if (bandwidthDetection && !client.isBandwidthChecked()) {
                    client.checkBandwidth();
                }
            } else {

            }
            registerJMX();
        } else {

        }
        return success;
    }

这里首先会调用Channel 2的write函数向客户端返回服务器和客户端的带宽信息,接着调用checkBandwidth检测客户端的带宽大小,最后调用registerJMX注册到JMX中。这里只看checkBandwidth函数,

    public void checkBandwidth() {
        bandwidthChecked = true;
        ServerClientDetection detection = new ServerClientDetection();
        detection.checkBandwidth(Red5.getConnectionLocal());
    }
    public void checkBandwidth(IConnection conn) {
        calculateClientBw(conn);
    }
    public void calculateClientBw(IConnection conn) {
        this.conn = conn;
        Random rnd = new Random();
        rnd.nextBytes(payload);
        rnd.nextBytes(payload1);
        startBytesWritten = conn.getWrittenBytes();
        startTime = System.nanoTime();
        callBWCheck("");
    }
    private void callBWCheck(Object payload) {
        IConnection conn = Red5.getConnectionLocal();
        Map statsValues = new HashMap();
        statsValues.put("count", packetsReceived.get());
        statsValues.put("sent", packetsSent.get());
        statsValues.put("timePassed", timePassed);
        statsValues.put("latency", latency);
        statsValues.put("cumLatency", cumLatency);
        statsValues.put("payload", payload);
        if (conn instanceof IServiceCapableConnection) {
            packetsSent.incrementAndGet();
            ((IServiceCapableConnection) conn).invoke("onBWCheck", new Object[] { statsValues }, this);
        }
    }

这里最后会出发客户端的onBWCheck函数进行测量。
分析完了所有的connect函数,回到OnCommand的第二部分,继续往下看如果连接成功就需要构造返回信息,包括服务器的版本号等等。并且调用RTMPMinaConnection的ping测试和客户端的速度。

第三部分

第三部分就很简单了,这里就是将输出的结果call发送回客户端。

    protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) {
        ...
        if (connected) {
            ...
        } else {
            ...
        }
        if (command instanceof Invoke) {
            if ((source.getStreamId().intValue() != 0) && (call.getStatus() == Call.STATUS_SUCCESS_VOID || call.getStatus() == Call.STATUS_SUCCESS_NULL)) {
                return;
            }
            boolean sendResult = true;
            if (call instanceof IPendingServiceCall) {
                IPendingServiceCall psc = (IPendingServiceCall) call;
                Object result = psc.getResult();
                if (result instanceof DeferredResult) {
                    ...
                }
            }
            if (sendResult) {
                Invoke reply = new Invoke();
                reply.setCall(call);
                reply.setTransactionId(command.getTransactionId());
                channel.write(reply);
                if (disconnectOnReturn) {
                    conn.close();
                }
            }
        }
    }

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