eXile 的专栏

[T] ICE实例学习:Let's Chat! (2) 实现服务器


服务器实现:

       服务器使用C++。注意它的结构:类 ChatRoom 实现了大部分的应用逻辑。为了支持推模型与拉模型,服务器实现了类ChatSession 和类 PollingChatSession。 ChatRoom 调用 ChatRoomCallbackAdapter 对象的 send 函数来传递客户消息,该对象隐藏了两种模型之间的差异。

ChatRoom 实现:

      ChatRoom是一个普通的C++对象,而不是一个Servant.
// C++ 
class ChatRoomCallbackAdapter { /*  */ }; 
typedef IceUtil::Handle
<ChatRoomCallbackAdapter> ChatRoomCallbackAdapterPtr; 
 
class ChatRoom : public IceUtil::Shared 

public
    
void reserve(const string&); 
    
void unreserve(const string&); 
    
void join(const string&const ChatRoomCallbackAdapterPtr&); 
    
void leave(const string&); 
    Ice::Long send(
const string&const string&); 
 
private
    typedef map
<string, ChatRoomCallbackAdapterPtr> ChatRoomCallbackMap; 
 
    ChatRoomCallbackMap _members; 
    
set<string> _reserved; 
    IceUtil::Mutex _mutex; 
}; 
typedef IceUtil::Handle
<ChatRoom> ChatRoomPtr;

      成员_reserverd是一个字符串集合,它存储已经建立回话,但是还没有加入聊天室的客户名。_members存储当前聊天室的所有用户(已经调用过join函数的用户)。

     成员函数 reserve 和 unreserve 维护 _reserved 集合。

// C++ 
void 
ChatRoom::reserve(
const string& name) 

    IceUtil::Mutex::Lock sync(_mutex); 
    
if(_reserved.find(name) != _reserved.end() || _members.find(name) != _members.end()) 
    { 
        
throw string("The name " + name + " is already in use."); 
    } 
    _reserved.insert(name); 

 
void 
ChatRoom::unreserve(
const string& name) 

    IceUtil::Mutex::Lock sync(_mutex); 
    _reserved.erase(name); 
}


     join操作添加用户到聊天室。

// C++ 
void 
ChatRoom::join(
const string& name, const ChatRoomCallbackAdapterPtr& callback) 

    IceUtil::Mutex::Lock sync(_mutex); 
    IceUtil::Int64 timestamp 
= IceUtil::Time::now().toMilliSeconds(); 
    _reserved.erase(name); 
 
    Ice::StringSeq names; 
    ChatRoomCallbackMap::const_iterator q; 
    
for(q = _members.begin(); q != _members.end(); ++q) 
    { 
        names.push_back((
*q).first); 
    } 
 
    callback
->init(names); 
 
    _members[name] 
= callback; 
 
    UserJoinedEventPtr e 
= new UserJoinedEvent(timestamp, name); 
    
for(q = _members.begin(); q != _members.end(); ++q) 
    { 
        q
->second->join(e); 
    } 
}


      send实现,同join实现非常类似:

// C++ 
Ice::Long 
ChatRoom::send(
const string& name, const string& message) 

    IceUtil::Mutex::Lock sync(_mutex); 
    IceUtil::Int64 timestamp 
= IceUtil::Time::now().toMilliSeconds(); 
 
    MessageEventPtr e 
= new MessageEvent(timestamp, name, message); 
    
for(ChatRoomCallbackMap::iterator q = _members.begin(); q != _members.end(); ++q) 
    { 
        q
->second->send(e); 
    } 
    
return timestamp; 
}

 

 类 ChatRoomCallbackAdapter

// C++ 
class ChatRoomCallbackAdapter : public IceUtil::Shared 

public
    
virtual void init(const Ice::StringSeq&= 0
    
virtual void join(const UserJoinedEventPtr&= 0
    
virtual void leave(const UserLeftEventPtr&= 0
    
virtual void send(const MessageEventPtr&= 0
};

推模式 CallbackAdapter 实现:
class SessionCallbackAdapter : public ChatRoomCallbackAdapter 

public
    SessionCallbackAdapter(
const ChatRoomCallbackPrx& callback, const ChatSessionPrx& session)    : _callback(callback), _session(session) 
    { 
    } 
 
    
void init(const Ice::StringSeq& users) 
    { 
        _callback
->init_async(new AMICallback<AMI_ChatRoomCallback_init>(_session), users); 
    } 
 
    
void join(const UserJoinedEventPtr& e) 
    { 
        _callback
->join_async(new AMICallback<AMI_ChatRoomCallback_join>(_session), 
                              e
->timestamp, 
                              e
->name); 
    } 
 
    
void leave(const UserLeftEventPtr& e) 
    { 
        _callback
->leave_async(new AMICallback<AMI_ChatRoomCallback_leave>(_session), 
                               e
->timestamp, 
                               e
->name); 
    } 
 
    
void send(const MessageEventPtr& e) 
    { 
        _callback
->send_async(new AMICallback<AMI_ChatRoomCallback_send>(_session), 
                              e
->timestamp, 
                              e
->name, 
                              e
->message); 
    } 
 
private
    
const ChatRoomCallbackPrx _callback; 
    
const ChatSessionPrx _session; 
};

      看一下SessionCallbackAdapter的四个成员函数,当异步调用完成时,都使用类AMICallback来接收通知。它的定义如下:
template<class T> class AMICallback : public T 

public
    AMICallback(
const ChatSessionPrx& session) : _session(session) 
    { 
    }
    
virtual void ice_response() 
    { 
    } 
 
    
virtual void ice_exception(const Ice::Exception&
    { 
        
try 
        { 
            _session
->destroy(); // Collocated 
        } 
        
catch(const Ice::LocalException&
        { 
        } 
    } 
 
private
    
const ChatSessionPrx _session; 
};
       当用户回调操作抛出异常,服务器立即销毁客户会话,即把该用户赶出聊天室。这是因为,一旦客户的回调对象出现了一次异常,它以后也就不可能再正常。


推模式会话创建

     现在来看一下会话创建。推模式的客户使用Glacier2,所以要使用Glacier2的会话创建机制。Glacier2 允许用户通过提供一个Glacier2::SessionManager对象的代理来自定义会话创建机制。通过设置Glacier2.SessionManager属性来配置Gloacier2,就可以使用自己的会话管理器。会话管理器除了一个trivial构造函数(设置聊天室指针),只有一个操作,create,Glacier2调用它来代理应用的会话创建。 create 操作必须返回一个会话代理(类型为Glacier2::Session*)。实现如下:
Glacier2::SessionPrx 
ChatSessionManagerI::create(
const string& name,
                            
const Glacier2::SessionControlPrx&
                            
const Ice::Current& c) 

    
string vname; 
    
try 
    { 
        vname 
= validateName(name); 
        _chatRoom
->reserve(vname); 
    } 
    
catch(const string& reason) 
    { 
       
throw CannotCreateSessionException(reason); 
    } 
 
    Glacier2::SessionPrx proxy; 
    
try 
    { 
        ChatSessionIPtr session 
= new ChatSessionI(_chatRoom, vname); 
        proxy 
= SessionPrx::uncheckedCast(c.adapter->addWithUUID(session)); 
 
        Ice::IdentitySeq ids; 
        ids.push_back(proxy
->ice_getIdentity()); 
        sessionControl
->identities()->add(ids); 
    } 
    
catch(const Ice::LocalException&
    { 
        
if(proxy) 
        { 
            proxy
->destroy(); 
        } 
        
throw CannotCreateSessionException("Internal server error"); 
    } 
    
return proxy; 
}

     首先调用一个简单的帮助函数 validateName, 来检查传递的用户名是否包含非法字符,并把它转为大写,然后调用 reserver函数把它加到聊天室的_reserved集合中。我们要监视这些操作抛出的消息,并把它转化为Glacide2::CannotCreateSessionException异常,即在create操作的异常规范声明的异常。
     接着实例化一个ChatSessionI对象(见下面)来创建会话。注意这个会话使用UUID作为对象标识,所以保证标识符唯一。
    最后,添加这个新创建的会话标识,Gllacier2只通过它来转发经过这个会话的请求。实际上,“只转发经过这个会话的并且只到这个会话的请求”,这是一种安全的办法:如果有恶意客户能猜出另一个客户会话的标识,它也不能向别的对象发送请求(可能在除了聊天服务器之外的服务器上)。如果出错,就销毁刚创建的会话对象,这样避免了资源泄露。
       这就是利用Glacier2创建会话的全部。如果你希望使用Glacier2的认证机制,可以设置属性Glacier2.PermissionsVerifier为执行认证的对象代理。(Glacier2提供一个内置的权限验证器,NullPermissionsVerifier,可以检查用户名和密码)。
       图:会话创建交互图(略)

       ChatSessionI类实现了ChatSession接口。
class ChatSessionI : public ChatSession 

public
    ChatSessionI(
const ChatRoomPtr&const string&); 
 
    
virtual void setCallback(const ChatRoomCallbackPrx&const Ice::Current&); 
    
virtual Ice::Long send(const string&const Ice::Current&); 
    
virtual void destroy(const Ice::Current&); 
 
private
    
const ChatRoomPtr _chatRoom; 
    
const string _name; 
    ChatRoomCallbackAdapterPtr _callback; 
    
bool _destroy; 
    IceUtil::Mutex _mutex; 
}; 
typedef IceUtil::Handle
<ChatSessionI> ChatSessionIPtr;
         构造函数设置聊天室和用户名,并把_destroy设置为False.
      
        由于Glacier2::create操作不允许传递代理,必须把创建会话和设置回调分成两步。这是setCallback的实现;
void 
ChatSessionI::setCallback(
const ChatRoomCallbackPrx& callback, const Ice::Current& c) 

    IceUtil::Mutex::Lock sync(_mutex); 
    
if(_destroy) 
    { 
        
throw Ice::ObjectNotExistException(__FILE__, __LINE__); 
    } 
 
    
if(_callback || !callback) 
    { 
        
return
    } 
 
    Ice::Context ctx; 
    ctx[
"_fwd"= "o"
    _callback 
= new SessionCallbackAdapter(callback->ice_context(ctx), 
                                           ChatSessionPrx::uncheckedCast( 
                                               c.adapter
->createProxy(c.id))); 
    _chatRoom
->join(_name, _callback); 
}

      注意,在使用join传递代理之前,向客户代理添加了一个值为 "o" 的_fwd上下文。它提示Glacier使用单向调用来转发客户回调。这样比双向调用更加有效。因为所有的回调操作均为void返回值,所以可以单向调用。
     服务器的回调为普通的双向调用。这样当出错时可以通知服务器。当客户端出错时,这个对结束客户会话很有用。

     一旦客户调用了setCallback,就可以接收聊天室的各种行为通知。下为send实现:

Ice::Long 
ChatSessionI::send(
const string& message, const Ice::Current&

    IceUtil::Mutex::Lock sync(_mutex); 
    
if(_destroy) 
    { 
        
throw Ice::ObjectNotExistException(__FILE__, __LINE__); 
    } 
    
if(!_callback) 
    { 
        
throw InvalidMessageException("You cannot send messages until you joined the chat."); 
    } 
    
string
    
try 
    { 
        msg 
= validateMessage(message); 
    } 
    
catch(const string& reason) 
    { 
        
throw InvalidMessageException(reason); 
    } 
    
return _chatRoom->send(_name, msg); 
}

 
    客户要离开聊天室,只要调用 destory.

void 
ChatSessionI::destroy(
const Ice::Current& c) 

    IceUtil::Mutex::Lock sync(_mutex); 
    
if(_destroy) 
    { 
        
throw Ice::ObjectNotExistException(__FILE__, __LINE__); 
    } 
    
try 
    { 
        c.adapter
->remove(c.id);
        
if(_callback == 0
        { 
            _chatRoom
->unreserve(_name); 
        } 
        
else 
        { 
            _chatRoom
->leave(_name); 
        } 
    } 
    
catch(const Ice::ObjectAdapterDeactivatedException&
    { 
        
// No need to clean up, the server is shutting down. 
    } 
    _destroy 
= true
}

 

 

posted on 2009-03-26 00:54 eXile 阅读(3161) 评论(0)  编辑 收藏 引用 所属分类: 网络开发ICE


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

常用链接

留言簿(18)

随笔分类

随笔档案

服务器编程

搜索

最新评论

阅读排行榜

评论排行榜