red5源码分析---7

red5源码分析—客户端处理connect命令并发送createStream命令

在《red5源码分析—5》中提到过,当客户端发送connect命令后,服务器经过处理会将其connect命令返回,不同的是服务器返回的结果包含了一些连接后需要发送给客户端的信息,包括服务器版本、模式等等。当返回的信息经过服务器的发送过滤器RTMPMinaProtocolEncoder时,会调用其中的RTMPProtocolEncoder的encodeCommand函数,下面来看其中的一段代码,

    protected void encodeCommand(IoBuffer out, ICommand command) {
        Output output = new org.red5.io.amf.Output(out);
        final IServiceCall call = command.getCall();
        final boolean isPending = (call.getStatus() == Call.STATUS_PENDING);
        if (!isPending) {
            Serializer.serialize(output, call.isSuccess() ? "_result" : "_error");
        } else {
            final String action = (call.getServiceName() == null) ? call.getServiceMethodName() : call.getServiceName() + ‘.‘ + call.getServiceMethodName();
            Serializer.serialize(output, action);
        }
        ...
    }

这里可以简单看出,如果经过处理后的call的状态不是STATUS_PENDING,并且处理成功,则返回”_result”,否则返回”_error”。是否处理成功由Call的isSuccess方法判断,

    public boolean isSuccess() {
        return (status == STATUS_SUCCESS_RESULT) || (status == STATUS_SUCCESS_NULL) || (status == STATUS_SUCCESS_VOID);
    }

因此,只要Call的状态是STATUS_SUCCESS_RESULT、STATUS_SUCCESS_NULL或者STATUS_SUCCESS_VOID就可以判定为处理成功了。如果call的状态是STATUS_PENDING,则根据情况返回方法名,或者服务名加上方法名。

根据上面的分析结果回顾服务器对connect命令的处理,如果连接成功连接上服务器的某个scope后,便会设置Call的状态为STATUS_SUCCESS_RESULT,也因此返回的方法名(serviceMethodName)为_result。因此,当服务器处理完connect命令后消息返回客户端时,

便调用BaseRTMPClientHandler的onCommand函数,如下

    protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) {
        final IServiceCall call = command.getCall();
        final String methodName = call.getServiceMethodName();
        if ("_result".equals(methodName) || "_error".equals(methodName)) {
            final IPendingServiceCall pendingCall = conn.getPendingCall(command.getTransactionId());
            if (pendingCall != null) {
                if ("connect".equals(methodName)) {
                    Integer encoding = (Integer) connectionParams.get("objectEncoding");
                    if (encoding != null && encoding.intValue() == 3) {
                        conn.getState().setEncoding(IConnection.Encoding.AMF3);
                    }
                }
            }
            handlePendingCallResult(conn, (Invoke) command);
            return;
        }
        ...
    }

根据前面的分析,当服务器返回的消息到达客户端后,最后会调用handlePendingCallResult函数进行处理,handlePendingCallResult定义在BaseRTMPHandler中,

    protected void handlePendingCallResult(RTMPConnection conn, Invoke invoke) {
        final IServiceCall call = invoke.getCall();
        final IPendingServiceCall pendingCall = conn.retrievePendingCall(invoke.getTransactionId());
        if (pendingCall != null) {
            Object[] args = call.getArguments();
            if (args != null && args.length > 0) {
                pendingCall.setResult(args[0]);
            }
            Set<IPendingServiceCallback> callbacks = pendingCall.getCallbacks();
            if (!callbacks.isEmpty()) {
                HashSet<IPendingServiceCallback> tmp = new HashSet<IPendingServiceCallback>();
                tmp.addAll(callbacks);
                for (IPendingServiceCallback callback : tmp) {
                    try {
                        callback.resultReceived(pendingCall);
                    } catch (Exception e) {

                    }
                }
            }
        }
    }

首先通过retrievePendingCall根据事务ID获取pendingCall,

    public IPendingServiceCall retrievePendingCall(int invokeId) {
        return pendingCalls.remove(invokeId);
    }

该pendingCall是在TCP建立连接后的调用BaseRTMPClientHandler的connectionOpened中根据事务ID注册的(《red5源码分析—3》)。获得PendingCall后,就设置结果,这里注意一点,也就是服务器最后设置的结果results经过RTMP的编解码(amf)处理后到达这里会变为参数params(具体的可以查看编解码器RTMPProtocolEncoder和RTMPProtocolDecoder的对应关系)。最后通过resultReceived调用之前注册的回调函数,根据《red5源码分析—1》,该注册的回调函数如下,

    public void resultReceived(IPendingServiceCall call) {
        Object result = call.getResult();
        if (result instanceof ObjectMap) {
            if ("connect".equals(call.getServiceMethodName())) {
                createStream(this);
            }
        } else {
            if ("createStream".equals(call.getServiceMethodName())) {
                if (result instanceof Integer) {
                    Integer streamIdInt = (Integer) result;
                    // int streamId = streamIdInt.intValue();
                    // publish(streamId, "testgio2", "live", this);
                    invoke("getRoomsInfo", this);
                } else {
                    disconnect();
                }
            } else if ("getRoomsInfo".equals(call.getServiceMethodName())) {
                ArrayList<String> list = (ArrayList<String>) result;
                for (int i = 0; i < list.size(); i++) {
                    System.out.println(list.get(i));
                }
            }
        }
    }

因此,客户端再收到connect命令的结果后立即调用createStream来创建一个流,

    public void createStream(IPendingServiceCallback callback) {
        IPendingServiceCallback wrapper = new CreateStreamCallBack(callback);
        invoke("createStream", null, wrapper);
    }

CreateStreamCallBack和处理回调函数有关系,回头再分析。下面就是发送一个”createStream”的命令给客户端了。下一章看看服务器是如何处理”createStream”命令的。

时间: 2024-10-12 10:27:10

red5源码分析---7的相关文章

red5源码分析---10

red5源码分析-服务器处理publish命令 和前几章的分析一样,服务器接收到客户端发来的publish命令后,最终会执行RTMPHandler的onCommand函数,再参考<red5源码分析-8>的分析,最终会调用StreamService的publish方法,代码如下 public void publish(String name, String mode) { Map<String, String> params = null; if (name != null &

red5源码分析---6

red5源码分析-客户端和服务器的命令处理 在<red5源码分析-5>中可以知道,在RTMP握手完毕后,客户端会向服务器发送connect命令,connect命令的主要作用就是要和red5服务器上的某个Scope相连接,连接完成后,会向客户端发送带宽协调的指令,ping指令,和一个带宽检测指令.下面先分析ping指令. ping指令 服务端代码 这里先贴一下在服务器将客户端和某个Scope相连后发出的ping指令代码, ... conn.ping(new Ping(Ping.STREAM_BE

red5源码分析---12

red5源码分析-服务器处理视频数据 接着<red5源码分析-11>,本章假设客户端发来的是视频数据,下面就分析服务器如何处理这些数据的. 根据前面几章的分析,基于mina框架,数据到达服务器后,最终会到达RTMPHandler的messageReceived函数,messageReceived定义在RTMPHandler的父类BaseRTMPHandler中, public void messageReceived(RTMPConnection conn, Packet packet) th

red5源码分析---9

red5源码分析-客户端publish流 接着上一章的分析结果,参考<red5源码分析-7>的分析结论,当服务器返回steamId后,客户端会执行BaseRTMPClientHandler的onCommand函数,onCommand函数会根据返回的方法名"_result"开始执行handlePendingCallResult函数,handlePendingCallResult会获取之前注册的回调函数,根据<red5源码分析-7>,该回调函数就为CreateStr

red5源码分析---8

red5源码分析-服务器处理createStream命令 服务器接到createStream命令后,经过过滤器层层处理,最后会调用BaseRTMPHandler的messageReceived函数, public void messageReceived(RTMPConnection conn, Packet packet) throws Exception { if (conn != null) { IRTMPEvent message = null; try { message = pack

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

HashMap与TreeMap源码分析

1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Java这么久,也写过一些小项目,也使用过TreeMap无数次,但到现在才明白它的实现原理).因此本着"不要重复造轮子"的思想,就用这篇博客来记录分析TreeMap源码的过程,也顺便瞅一瞅HashMap. 2. 继承结构 (1) 继承结构 下面是HashMap与TreeMap的继承结构: pu

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线