前面的一篇文章分析了Dealer类型的Socket,它的行为是非常简单的,
它会维护一个链表,在链表的前面是当前活动的pipe,因为pipe连接的是下面的Session,那么活动的pipe也就意味着活动的StreamEngine,那么也就意味着活动的连接,
对于执行send方法,也非常的简单,就是在活动的pipe部分,通过循环的方式,选出一个pipe,将数据写进去就好了,因为可以看成是循环的遍历,所以对于数据的发送,这里也可以理解为比较简单额负载均衡了。。
这里要分析Req类型的Socket,它直接继承自Dealer类型的Socket,所以其实它的行为与Dealer类型的Socket的行为非常的类似,但是对有一个最为不一样的地方:
每一次通信,Req类型的Socket都必须按照send,recv的顺序来执行,首先执行send方法,将request发送出去,然后再执行recv方法接收返回的数据,这里顺序还不能出错。。。。
那么其实也就简化了数据的通信,强制要求同一时刻只能通过一个StreamEngine来收发数据。。。
首先来看看Req定义的两个重要的属性:
private boolean receiving_reply; //标志位,如果是ture的话,表示request已经发送了,正在等待reponse
private boolean message_begins; //如果是true的话,那么表示这里还需要发送第一个标志的空msg
首先是receiving_reply属性,如果它为true的话,那么表示request已经发送了, 接下来要执行recv,也就是接收返回的数据,如果返回数据还没有接收,就继续send的话,那么会报错。。。
message_begins属性,每一次发送首先都会发送一个空的msg,当做标志,每一接收返回,都会首先接收一个空的msg标志,用这个标志位来表示这个空的msg是否已经发送或者接收。。。
接下来来看看在Req中定义的send和recv方法吧:
//在socketBase终将会具体的调用这个方法来发送数据,其实是通过pipe将数据发到Session端
public boolean xsend(Msg msg_)
{
//如果已经发送了数据而且在等待返回,这个时候不能连续发送数据
if (receiving_reply) {
throw new IllegalStateException("Cannot send another request");
}
// First part of the request is the request identity.
if (message_begins) { //先发送一个空的标志message
Msg bottom = new Msg();
bottom.set_flags(Msg.more); //表示在这个msg后面还有msg接着要发送
boolean rc = super.xsend(bottom); //调用dealer的方法来具体的发送数据
if (!rc) { //第一个标志message发送失败
return rc;
}
message_begins = false; //将标志位设置为false,用于表示最开的空的msg已经发送了
}
boolean more = msg_.has_more(); //获取more标志位用于判断当前要发送的msg后面是否还有接着的msg要发送
boolean rc = super.xsend(msg_); //还是调用父类的方法发送数据
if (!rc)
return rc;
// If the request was fully sent, flip the FSM into reply-receiving state.
if (!more) {
receiving_reply = true; //这里表示一个reqeust已经发送了,需要等待返回的response
message_begins = true; //将标志位从新设置为true,这里more标志位已经没有了,那么表示request的send过程已经完成,那么接下来的recv过程首先也需要接收一个空的msg
}
return true;
}
@Override
//接收数据的方法
protected Msg xrecv()
{
// If request wasn't send, we can't wait for reply.
if (!receiving_reply) { //如果都没有发送request,那还接收response干啥
throw new IllegalStateException("Cannot wait before send");
}
Msg msg_ = null;
// First part of the reply should be the original request ID.
if (message_begins) { //首先接收标志msg
msg_ = super.xrecv(); //调用父类来接收msg
if (msg_ == null)
return null;
// TODO: This should also close the connection with the peer!
if ( !msg_.has_more() || msg_.size() != 0) { //判断是否是标志msg,否则的话表示出错了
while (true) {
msg_ = super.xrecv();
assert (msg_ != null);
if (!msg_.has_more())
break;
}
errno.set(ZError.EAGAIN);
return null;
}
message_begins = false; //将标志位设置为false,表示最开始用于标识的空的msg已经接受了
}
//标志msg接收完了之后再接收其余的msg
msg_ = super.xrecv();
if (msg_ == null)
return null;
// If the reply is fully received, flip the FSM into request-sending state.
if (!msg_.has_more()) {
receiving_reply = false;
message_begins = true; //这里recv的数据已经接受完了,那么将这个标志位复位
}
return msg_;
}
其实代码还是很简单吧,主要的接收和发送都还是通过Dealer的方法来实现的,这里无非是多了一点空的标志msg的处理而已。。。基本上的内容都在注释里面交代清楚了。。。。
总的来说还是很简单吧,不过Req对Session进行了扩展。。。具体如下:
public static class ReqSession extends Dealer.DealerSession {
enum State {
identity,
bottom,
body
};
State state;
public ReqSession(IOThread io_thread_, boolean connect_,
SocketBase socket_, final Options options_,
final Address addr_) {
super(io_thread_, connect_, socket_, options_, addr_);
state = State.identity; //最开始的状态是标志,因为连接一开始建立,就将会发送identity,之后就在bottom和body里面循环了
}
@Override
//该方法用于将数据写到pipe里面,这样上层的socket就可以收到了
public int push_msg (Msg msg_)
{
switch (state) {
case bottom:
if (msg_.flags () == Msg.more && msg_.size () == 0) { //标志msg的特性,肯定是more标志位还有size为0,
state = State.body; //接下来就是要接收真正的数据了
return super.push_msg (msg_); //调用sessionBase的方法,这里其实是将数据写到pipe里面去发送给socket
}
break;
case body:
if (msg_.flags () == Msg.more)
return super.push_msg (msg_);
if (msg_.flags () == 0) {
state = State.bottom; //回归bottom
return super.push_msg (msg_);
}
break;
case identity: //一开始与response连接上之后,就会接收identity
if (msg_.flags () == 0) {
state = State.bottom; //接下来应该要接收最开始的标志msg
return super.push_msg (msg_);
}
break;
}
throw new IllegalStateException(state.toString());
}
public void reset ()
{
super.reset ();
state = State.identity;
}
}
}
这里对于push_msg方法添加了一些状态的扩展。。。可能它的唯一用处就是过滤掉那些无效的msg吧。。。只将有效的正确的msg通过pipe从session交给上层的socket,从而让用户代码接收到。。。