从《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表示已关闭连接,下面只看前三种状态对应的处理方式。
当创建完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状态代表第二次握手状态,进入该状态并收到服务器消息时,就代表握手过程即将完成,下面来看
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框架有关,这里就不往下看了。
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函数。
下一章分析服务器端的握手过程。