ICE 创建 聊天室小程序

  ICE的优势是作为通讯中间件可支持跨平台的通讯,目前支持C++、C#、JAVA、Python、Ruby等多种语言。

一 、示例描述

    搭建一个简单的聊天室,服务端使用C++实现,客户端也使用C++实现,中间以ICE构建通讯通道。聊天室实现的功能包括:用户登录/注销、发送消息、接受消息。

二 、准备Slice文件

    Slice作为ICE本身特有的语言,成为跨平台通讯的桥梁。通过Slice文件描述需要实现的接口,再由ICE提供的各个语言编译器生成各语言的接口类,各语言分别负责根据接口类实现属于本身的逻辑细节即可,据说这种思想是从老牌的通讯中间件CORBA中借鉴过来的。

    聊天室的Slice文件定义如下:

module ChatRoom{
	interface CAbsMsgReceiver{//lient端实现,Server端调用的回调函数接口
		//这是因为Server端在向Client端发送信息的时候,必须知道Client端的通讯地址
		void OnMsg(string sender, string msg);
	};

	interface CAbsConnAcceptor{//Server端实现,Client端调用。实现了登录、注销和发送信息的接口函数
		bool Login(string user, CAbsMsgReceiver* receiver);
		void Logout(string user);
		void Send(string user, string msg);
	};

	//Server端用来存储Client端的用户名和回调函数句柄的容器
	dictionary<string, CAbsMsgReceiver*> dicClient;
};

 分析上面的Slice文件如下:

1、 在命名空间为ChatRoom下定义了两个类(接口),CAbsConnAcceptor和CAbsMsgReceiver。

2、 CAbsConnAcceptor主要是Server端实现,Client端调用。实现了登录、注销和发送信息的接口函数。

3、 CAbsMsgReceiver主要是Client端实现,Server端调用的回调函数接口。这是因为Server端在向Client端发送信息的时候,必须知道Client端的通讯地址。因此需要这个回调函数类。

4、 dicClient主要是Server端用来存储Client端的用户名及回调函数句柄的。

三 、实现Server端

    通过Slice2Cpp程序将Slice文件生成Message.h和Message.cpp,将两个文件加入到工程中,同时新建一个类CConnAcceptor继承CAbsCoonAcceptor,实现CAbsCoonAcceptor的三个虚拟函数接口,这个类被称为Server端的Servant类。代码如下:


#include <Ice/Ice.h>
#include "Message.h"
using namespace ChatRoom;

#include <string>
#include <iostream>
#include <iomanip>
using namespace std;


class CConnAcceptor : public CAbsConnAcceptor{
public:
	CConnAcceptor();
	~CConnAcceptor();
	virtual bool Login(const ::std::string&, const ::ChatRoom::CAbsMsgReceiverPrx&, const ::Ice::Current&);
	virtual void Logout(const ::std::string&, const ::Ice::Current&);
	virtual void Send(const ::std::string&, const ::std::string&, const ::Ice::Current&);
private:
	ChatRoom::dicClient mapUser;
	void BoardCast(const ::std::string user, const ::std::string msg);
	bool Notify(const ::std::string& user, const ::std::string& msg, const ::ChatRoom::CAbsMsgReceiverPrx& receiver);
};


CConnAcceptor::CConnAcceptor(){}
CConnAcceptor::~CConnAcceptor(){}
bool CConnAcceptor::Login(const ::std::string& user, const ::ChatRoom::CAbsMsgReceiverPrx& receiver, const ::Ice::Current&){
	if(mapUser.find(user) != mapUser.end())			return false;//防止同一用户的重复登录
	cout << user << "登录成功!" << endl;
	mapUser.insert(ChatRoom::dicClient::value_type(user, receiver));
	BoardCast(user, "欢迎进入聊天室,希望大家踊跃发言!");
	return true;
}
void CConnAcceptor::Logout(const ::std::string& user, const ::Ice::Current& = ::Ice::Current()){
	cout << user << "注销成功!" << endl;
	mapUser.erase(user);
}
void CConnAcceptor::Send(const ::std::string& user, const ::std::string& msg, const ::Ice::Current&){
	BoardCast(user, msg);
}
void CConnAcceptor::BoardCast(const ::std::string user, const ::std::string msg){
	for(ChatRoom::dicClient::iterator it = mapUser.begin(); it != mapUser.end(); it++){
		Notify(it->first, msg, it->second);
	}
}
bool CConnAcceptor::Notify(const ::std::string& user, const ::std::string& msg, const ::ChatRoom::CAbsMsgReceiverPrx& receiver){
	bool bNormal = true;
	try{ receiver->OnMsg(user, msg); }
	catch(::std::exception& e){ bNormal = false; }
	return bNormal;
}


class CMyApp : virtual public Ice::Application{
public:
	virtual int run(int, char* []){
		shutdownOnInterrupt();// Terminate cleanly on receipt of a signal
		Ice::ObjectAdapterPtr spObjAdapter = communicator()->createObjectAdapterWithEndpoints("ServerAdapter", "default -h 127.0.0.1 -p 5432");//Create an object adapter
		ChatRoom::CAbsConnAcceptorPtr spAcceptor = new CConnAcceptor();
		spObjAdapter->add(spAcceptor, communicator()->stringToIdentity("Acceptor"));
		spObjAdapter->activate();
		communicator()->waitForShutdown();
		if(interrupted())			cerr << appName() << ": received signal, shutting down" << endl;
		return 0;
	}
};


int main(int argc, char* argv[])
{
	CMyApp oMyApp;
	int iRet = oMyApp.main(argc, argv);
	system("Pause");
	return iRet;
}

 注意有两个私有函数BoardCast和Notify为从Server端广播消息至Client端时用到的。

四 、实现Client端

    通过Slice2Cpp程序将Slice文件生成Message.h和Message.cpp,将两个文件加入到工程中,同时新建一个类CMsgReceiver继承CAbsCMsgReceiver,实现CAbsCMsgReceiverOnMsg函数。新建Client类,实现Client端与Server端之间的通讯逻辑,并生成CAbsCMsgReceiver类的本地代理类,发送到Server端实现发送消息的回调逻辑。代码如下:


#include <Ice/Ice.h>
#include "Message.h"
using namespace ChatRoom;

#include <string>
#include <iostream>
#include <iomanip>
using namespace std;

class CMsgReceiver : public CAbsMsgReceiver{
	virtual void OnMsg(const ::std::string& sender, const ::std::string& msg, const ::Ice::Current&);
};
void CMsgReceiver::OnMsg(const ::std::string& sender, const ::std::string& msg, const ::Ice::Current&){
	cout << sender << "˵ : \" " << msg << "\"" << endl;
}

class CMyApp : virtual public Ice::Application{
public:
	virtual int run(int, char* []){
		shutdownOnInterrupt();// Terminate cleanly on receipt of a signal

		 Ice::ObjectPrx pAcceptorProxy = communicator()->stringToProxy("Acceptor:default -h 127.0.0.1 -p 5432");  
        CAbsConnAcceptorPrx spAcceptor = CAbsConnAcceptorPrx::checkedCast(pAcceptorProxy);  
        if(!spAcceptor)          throw "Bad AcceptorProxy !";  

		Ice::ObjectAdapterPtr spObjAdapter = communicator()->createObjectAdapterWithEndpoints("ClientAdapter", "default -h 127.0.0.1 -p 2345");//Create an object adapter
		ChatRoom::CAbsMsgReceiverPtr spUser = new CMsgReceiver();
		spObjAdapter->add(spUser, communicator()->stringToIdentity("Receiver"));
		
		 Ice::ObjectPrx pReceiverProxy = communicator()->stringToProxy("Receiver:default -h 127.0.0.1 -p 2345");  
        CAbsMsgReceiverPrx spReceiver = CAbsMsgReceiverPrx::checkedCast(pReceiverProxy);  
        if(!spReceiver)          throw "Bad ReceiverProxy !";  

		spAcceptor->Login("ZhangSan", spReceiver);
		spAcceptor->Send("ZhangSan", "Hello, Everyone");
		spAcceptor->Logout("ZhangSan");
	}
};


int main(int argc, char* argv[])
{
	CMyApp oMyApp;
	int iRet = oMyApp.main(argc, argv);
	system("Pause");
	return iRet;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值