red5源码分析---6
来源:互联网 发布:阴阳师辅助软件ios 编辑:程序博客网 时间:2024/05/21 23:54
red5源码分析—客户端和服务器的命令处理
在《red5源码分析—5》中可以知道,在RTMP握手完毕后,客户端会向服务器发送connect命令,connect命令的主要作用就是要和red5服务器上的某个Scope相连接,连接完成后,会向客户端发送带宽协调的指令,ping指令,和一个带宽检测指令。下面先分析ping指令。
ping指令
服务端代码
这里先贴一下在服务器将客户端和某个Scope相连后发出的ping指令代码,
... conn.ping(new Ping(Ping.STREAM_BEGIN, 0, -1)); ...
Ping的构造函数很简单,就是一些基本的赋值,下面来看conn的ping函数,
public void ping(Ping ping) { getChannel(2).write(ping); }
因此这里就是简单的发送给客户端了。
客户端代码
客户端接收到服务器发来的ping消息后,根据前面几章的分析,最后会调用到BaseRTMPClientHandler的onPing函数,下面来看,
protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping) { switch (ping.getEventType()) { case Ping.PING_CLIENT: case Ping.STREAM_BEGIN: case Ping.RECORDED_STREAM: case Ping.STREAM_PLAYBUFFER_CLEAR: Ping pong = new Ping(); pong.setEventType(Ping.PONG_SERVER); pong.setValue2((int) (System.currentTimeMillis() & 0xffffffff)); conn.ping(pong); break; case Ping.STREAM_DRY: break; case Ping.CLIENT_BUFFER: ... break; case Ping.PING_SWF_VERIFY: ... break; case Ping.BUFFER_EMPTY: break; case Ping.BUFFER_FULL: break; default: } }
因为服务器发来的ping命令的eventType是STREAM_BEGIN,因此只看前面一部分,客户端将自己当前的毫秒数发送给服务器。下面再来看服务器端的处理。
服务端代码
服务器收到客户端的PONG_SERVER消息后,最终会进入RTMPHandler的onPing函数,代码如下
protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping) { switch (ping.getEventType()) { case Ping.CLIENT_BUFFER: ... break; case Ping.PONG_SERVER: conn.pingReceived(ping); break; default: } }
这里直接调用RTMPMinaConnection的pingReceived函数,定义如下
public void pingReceived(Ping pong) { long now = System.currentTimeMillis(); Number previousPingValue = lastPingSentOn.get() & 0xffffffff; if (pong.getValue2() == previousPingValue) { lastPingRoundTripTime.set((int) ((now & 0xffffffff) - pong.getValue2().intValue())); } else { if (getPendingMessages() > 4) { Number pingRtt = (now & 0xffffffff) - pong.getValue2().intValue(); } } lastPongReceivedOn.set(now); }
这里就是简单的赋值,记录一下本次ping的各个结果值。
长连接的处理
服务器端代码
既然说到了ping,这里就分析一下上一章中出现的KeepAliveTask,出现在startRoundTripMeasurement函数中,
... keepAliveTask = scheduler.scheduleAtFixedRate(new KeepAliveTask(), pingInterval); ...
该任务用于保持长连接,下面就来看,
public void run() { if (state.getState() == RTMP.STATE_CONNECTED) { if (running.compareAndSet(false, true)) { try { if (isConnected()) { long now = System.currentTimeMillis(); long currentReadBytes = getReadBytes(); long previousReadBytes = lastBytesRead.get(); if (currentReadBytes > previousReadBytes) { if (lastBytesRead.compareAndSet(previousReadBytes, currentReadBytes)) { lastBytesReadTime = now; } if (isIdle()) { onInactive(); } } else { long lastPingTime = lastPingSentOn.get(); long lastPongTime = lastPongReceivedOn.get(); if (lastPongTime > 0 && (lastPingTime - lastPongTime > maxInactivity) && (now - lastBytesReadTime > maxInactivity)) { onInactive(); } else { ping(); } } } else { onInactive(); } } catch (Exception e) { } finally { running.compareAndSet(true, false); } } } }
首先,如果在每次KeepAliveTask启动间隙有数据读入,就通过isIdle检查ping的状态,
public boolean isIdle() { long lastPingTime = lastPingSentOn.get(); long lastPongTime = lastPongReceivedOn.get(); boolean idle = (lastPongTime > 0 && (lastPingTime - lastPongTime > maxInactivity)); return idle; }
lastPingTime表示最近一次ping的时间,lastPongTime表示最后一次客户端相应ping的时间,它们的差就表示客户端最后一次响应ping的时间距离最近一次ping的时间有多长,当超过这个时间时,就表示服务器发送了很多ping请求但是客户端没响应,因此返回true,继而调用onInactive关闭连接。
回到KeepAliveTask中,假设服务器在KeepAliveTask启动间隙没有数据读入,就要判断是否很久没有响应ping请求了,并且已经过了很长时间没有读入数据了,“很久”的界限就是maxInactivity,如果满足,就关闭连接,如果不满足,就向客户端发送ping命令。这里不带参的ping函数如下,
public void ping() { long newPingTime = System.currentTimeMillis(); if (lastPingSentOn.get() == 0) { lastPongReceivedOn.set(newPingTime); } Ping pingRequest = new Ping(); pingRequest.setEventType(Ping.PING_CLIENT); lastPingSentOn.set(newPingTime); int now = (int) (newPingTime & 0xffffffff); pingRequest.setValue2(now); ping(pingRequest); }
这里设置了前面提到的lastPingSentOn,主要是事件类型改变为了PING_CLIENT,但是客户端的处理方式和前面事件类型为STREAM_BEGIN时一样,所以往下就不分析了。
BandWidth命令
服务器端代码
当客户端发送connect命令连接服务器scope时,在连接过程中,服务器会发送两个和带宽相关的指令回给客户端,这段代码如下,
two.write(new ServerBW(defaultServerBandwidth)); two.write(new ClientBW(defaultClientBandwidth, (byte) limitType));
因此发送了ServerBW和ClientBW两个类,它们对应的dataType分别是TYPE_SERVER_BANDWIDTH和TYPE_CLIENT_BANDWIDTH。
客户端代码
根据上面说的dataType,当带宽的信息到达客户端后,会分别调用onServerBandwidth和onClientBandwidth进行处理,
protected void onServerBandwidth(RTMPConnection conn, Channel channel, ServerBW message) { int bandwidth = message.getBandwidth(); if (bandwidth != bytesReadWindow) { ClientBW clientBw = new ClientBW(bandwidth, (byte) 2); channel.write(clientBw); } } protected void onClientBandwidth(RTMPConnection conn, Channel channel, ClientBW message) { int bandwidth = message.getBandwidth(); if (bandwidth != bytesWrittenWindow) { ServerBW serverBw = new ServerBW(bandwidth); channel.write(serverBw); } }
无论是发还是收的带宽,这里就是和本地的Window相比,如果不相等就发送给服务器。实际情况可以修改这部分代码以调整客户端带宽。
服务器端代码
服务器端接收到客户端反馈的信息后,也会调用本地的onServerBandwidth和onClientBandwidth函数继续处理,但这两个函数在服务器端是空函数,也即服务器什么也不做了。
checkBandWidth命令
服务器端代码
同样,在服务器端处理connect命令时,会发送checkBandWidth的命令,代码如下,
public void checkBandwidth() { ServerClientDetection detection = new ServerClientDetection(); detection.checkBandwidth(Red5.getConnectionLocal()); }
ServerClientDetection的构造函数为空,因此直接看checkBandwidth函数,
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(""); }
payload和payload1是两个随机数组,startBytesWritten记录该连接发出的字节数,startTime是当前纳秒数,最关键的是最后调用的callBWCheck,
private void callBWCheck(Object payload) { IConnection conn = Red5.getConnectionLocal(); Map<String, Object> statsValues = new HashMap<String, Object>(); 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); } }
这里进行相应的设置后就调用RTMPMinaConnection的invoke函数发送请求了,该函数定义如下,
public void invoke(String method, Object[] params, IPendingServiceCallback callback) { IPendingServiceCall call = new PendingCall(method, params); if (callback != null) { call.registerCallback(callback); } invoke(call); }
因此,这里注册了回调函数,然后就向客户端发送该invoke请求了。
客户端代码
客户端对应的onBWCheck函数为空,处理函数只是原数据返回,所以这里不看。
服务器端代码
服务器端对应的代码也为空,因此也是留给开发人员去添加函数进行处理了。
- red5源码分析---6
- red5源码分析(转)
- red5源码分析
- red5源码分析---1
- red5源码分析---2
- red5源码分析---3
- red5源码分析---4
- red5源码分析---5
- red5源码分析---7
- red5源码分析---8
- red5源码分析---9
- red5源码分析---10
- red5源码分析---11
- red5源码分析---12
- red5源码分析---13
- Red5源码研究一
- Red5源码研究一
- red5-server源码
- 正则表达式
- UVa11107 Lifeform
- iOS上架之内购
- Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED解决方法
- Spark 集群与数据集RDD
- red5源码分析---6
- C++11中的线程同步(简单测试)
- 【SSH网上商城项目实战07】Struts2和Json的整合
- 微信开发-SHA1算法
- 数据加序号,ireport里面
- ARM笔记之2440-中断寄存器详解[转]
- 用 Retrofit 2 简化 HTTP 请求
- python3中文乱码问题
- C语言的运行环境有哪些?哪些是值得推荐的?