Remoting聊天程序

一直只是了解Remoting,没做什么实际的东西,今天就用Remoting做一个简单的聊天工功能。
以下是聊天截图

我们要实现的功能很简单,只有两个:
1、客户端注册并接收用户列表。
2、客户端选择客户端进行聊天。

先看看设计流程图:

先作准备工作,准备好用户及用户列表:

/// <summary>
/// 用户信息
/// 客户端注册后由服务器生成UserId回发给注册客户端
/// </summary>
[Serializable]
public class User
{
public string UserId
{
get;
set;
}

public string IPAddress
{
get;
set;
}

public string Port
{
get;
set;
}

public string Name
{
get;
set;
}

public DateTime RegisterDate
{
get;
set;
}
}

/// <summary>
/// 用户列表
/// 每当有用户注册或退出即将用户列表发送给所有客户端
/// </summary>
[Serializable]
public class UserList
{
private static List<User> lisUsers = new List<User>();

/// <summary>
/// 获取用户
/// </summary>
public static User GetUser(string uid)
{
return lisUsers.Find(t => t.UserId == uid);
}

/// <summary>
/// 添加用户
/// </summary>
public static void Add(User usr)
{
if (GetUser(usr.UserId) != null)
{
return;
}

lisUsers.Add(usr);
}

/// <summary>
/// 移除用户
/// </summary>
public static void Remove(string uid)
{
User usr = lisUsers.Find(t => t.UserId == uid);
lisUsers.Remove(usr);
}

/// <summary>
/// 获取所有用户
/// </summary>
public static List<User> GetList()
{
return lisUsers;
}
}

接下来开始进行主程序设计,系统提供两种类型的服务,一类是由客户端发起,服务器响应。另一类是服务器端主动发起的服务。
先从注册入手,客户端先注册用户列表请求服务,然后发起注册请求,服务器接收到请求后向所有用户回发用户列表。
因此,出现了两个服务。
客户端发起的注册服务:

/// <summary>
/// 客户端注册服务
/// 发送向服务器
/// </summary>
public class RegisterService : MarshalByRefObject
{
public delegate void ClientRegisterEventHandler(object sender, UserEventArgs e);
public static event ClientRegisterEventHandler OnRegister = null;

/// <summary>
/// 用户注册
/// </summary>
public void Register(User usr)
{
if (OnRegister != null)
{
UserEventArgs e = new UserEventArgs();
e.User = usr;
OnRegister(this, e);
}
}

public override object InitializeLifetimeService()
{
return null;
}
}

/// <summary>
/// 用户事件参数
/// </summary>
[Serializable]
public class UserEventArgs : EventArgs
{
public UserEventArgs()
{

}

public User User
{
get;
set;
}
}

服务器发送用户列表服务:

public delegate void ServerUserListSendEventHandler(object sender, UserListEventArgs e);

/// <summary>
/// 发送用户列表服务
/// 每当有用户注册或退出时将用户列表发送给所有客户端
/// </summary>
public class UserListServices : MarshalByRefObject
{
public event ServerUserListSendEventHandler OnUserListSend = null;

/// <summary>
/// 用户列表发送给所有客户端
/// </summary>
public void SendRegisterInfo()
{
if (OnUserListSend != null)
{
ServerUserListSendEventHandler tempHandler = null;
foreach (Delegate del in OnUserListSend.GetInvocationList())
{
try
{
tempHandler = del as ServerUserListSendEventHandler;
UserListEventArgs e = new UserListEventArgs();
e.UserList = UserList.GetList();
tempHandler(this, e);
}
catch
{
OnUserListSend -= tempHandler;
}
}
}
}

public override object InitializeLifetimeService()
{
return null;
}
}

/// <summary>
/// 用户列表事件参数
/// </summary>
[Serializable]
public class UserListEventArgs : EventArgs
{
public UserListEventArgs()
{

}

public List<User> UserList
{
get;
set;
}
}

服务器发布服务:

private UserListServices usrListObj = null;             //发送用户列表服务

//发布客户端注册服务,同时侦听客户端注册
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RegisterService), "ClientRegister", WellKnownObjectMode.Singleton);
ListenRegister();

//发布注册用户列表发送服务
usrListObj = new UserListServices();
ObjRef refUsrListObj = RemotingServices.Marshal(usrListObj, "UserListSend");


/// <summary>
/// 侦听客户端注册服务事件
/// </summary>
private void ListenRegister()
{
RegisterService.OnRegister += new RegisterService.ClientRegisterEventHandler(OnRegister);
}

/// <summary>
/// 用户注册
/// </summary>
void OnRegister(object sender, UserEventArgs e)
{
//注册用户加入用户列表
UserList.Add(e.User);

//回发用户列表
usrListObj.SendRegisterInfo();
}

客户端发起注册请求:

//发送注册信息
RegisterService client = (RegisterService)Activator.GetObject(typeof(RegisterService), "http://localhost:10010/ClientRegister");
client.Register(usr);

接收用户列表:
usrListObj.OnUserListSend += new ServerUserListSendEventHandler(OnUserListSend);

// <summary>
/// 获取服务器回发注册信息
/// </summary>
void OnUserListSend(object sender, UserListEventArgs e)
{
//绑定列表显示
}

第一步工作已经完成,来进行第二步设计,也就是聊天主功能。
客户端从用户列表选择用户,打开一个聊天窗口,发送消息,服务器接收到消息进行消息转发,由另一客户端进行消息接收。
服务器同样提供两个服务,由客户端发起的消息发送服务,服务发起的消息转发服务。
客户端消息发送服务:

/// <summary>
/// 客户端发送消息服务
/// </summary>
public class SendMessageService : MarshalByRefObject
{
public delegate void ClientMessageSendEventHandler(object sender, ChatMessageEventArgs e);
public static event ClientMessageSendEventHandler OnMessageSend = null;

/// <summary>
/// 发送消息
/// </summary>
public void SendMessage(User from, User to, string msg)
{
if (OnMessageSend != null)
{
ChatMessageEventArgs e = new ChatMessageEventArgs();
e.UserFrom = from;
e.UserFrom = to;
e.Message = msg;
OnMessageSend(this, e);
}
}

public override object InitializeLifetimeService()
{
return null;
}
}

/// <summary>
/// 聊天消息事件参数
/// </summary>
[Serializable]
public class ChatMessageEventArgs : EventArgs
{
public ChatMessageEventArgs()
{

}

public User UserFrom
{
get;
set;
}

public User UserTo
{
get;
set;
}

public string Message
{
get;
set;
}
}

服务器发布服务:

//发布用户发送消息服务,同时侦听客户端消息发送
RemotingConfiguration.RegisterWellKnownServiceType(typeof(SendMessageService), "ClientMessageSend", WellKnownObjectMode.Singleton);
ListenClientMessageSend();

/// <summary>
/// 侦听客户端消息发送事件
/// </summary>
private void ListenClientMessageSend()
{
SendMessageService.OnMessageSend += new SendMessageService.ClientMessageSendEventHandler(ClientMessageSend);
}

再来设计服务器消息转发服务,这里碰到了一个问题,那就是服务器发起的服务被客户端注册时,服务器记录的是所有的客户委托,因此在进行消息转发的时候会将消息发送给所有用户,也就是所谓的广播,但现在的要求是只想把消息发送给指定的用户,目前来看好像没有办法实现该功能。
因此,得先解决这个问题才行,有一种思路就是建立客户端委托列表,如下:
把客户端的委托与客户端进行绑定,在服务器中建立客户委托列表,在进行消息发送的时候从委托列表中获取指定客户端委托,调用客户委托完成消息交互。

进行实施的话同样从服务入手,客户端发送委托,服务器进行接收并保存。
服务器设计委托列表:

/// <summary>
/// 用户委托列表
/// 存于服务器端用于服务器回发注册信息查找指定客户端
/// </summary>
public static class UserDelegateList
{
//用户 - 委托类型 - 委托
private static Dictionary<string, Dictionary<Enums.ClientDelegateType, Delegate>> dictRegister = new Dictionary<string, Dictionary<Enums.ClientDelegateType, Delegate>>();

/// <summary>
/// 添加客户端委托
/// </summary>
public static void Add(string usrInfo, Enums.ClientDelegateType type, Delegate del)
{
if (!dictRegister.ContainsKey(usrInfo))
{
dictRegister.Add(usrInfo, new Dictionary<Enums.ClientDelegateType, Delegate>());
}

if (!dictRegister[usrInfo].ContainsKey(type))
{
dictRegister[usrInfo].Add(type, del);
}
}

public static void Remove(string usrInfo)
{
if (dictRegister.ContainsKey(usrInfo))
{
dictRegister.Remove(usrInfo);
}
}

/// <summary>
/// 获取委托
/// </summary>
public static Delegate GetDelegate(string usrInfo, Enums.ClientDelegateType type)
{
if (!dictRegister.ContainsKey(usrInfo))
{
return null;
}

if (!dictRegister[usrInfo].ContainsKey(type))
{
return null;
}

return dictRegister[usrInfo][type];
}

/// <summary>
/// 获取所有委托
/// </summary>
public static Dictionary<string, Dictionary<Enums.ClientDelegateType, Delegate>> GetList()
{
return dictRegister;
}
}

public class Enums
{
/// <summary>
/// 客户端委托类型
/// </summary>
public enum ClientDelegateType
{
/// <summary>
/// 聊天委托
/// </summary>
Chat,

/// <summary>
/// 心跳委托
/// </summary>
HeartHit
}
}

客户端委托发送服务:

public delegate void ClientDelegateSendEventHandler(object sender, UserDelegateEventArgs e);

/// <summary>
/// 发送客户端委托
/// 服务器记录客户端委托,用于回发信息给指定客户端
/// </summary>
public class SendDelegateService : MarshalByRefObject
{
public static event ClientDelegateSendEventHandler OnDelegateSend = null;

/// <summary>
/// 发送委托
/// </summary>
public void Send(User usr, Enums.ClientDelegateType type, Delegate del)
{
if (OnDelegateSend != null)
{
UserDelegateEventArgs e = new UserDelegateEventArgs();
e.User = usr;
e.Type = type;
e.UserDelegate = del;
OnDelegateSend(this, e);
}
}

public override object InitializeLifetimeService()
{
return null;
}
}

/// <summary>
/// 用户委托事件参数
/// </summary>
[Serializable]
public class UserDelegateEventArgs : EventArgs
{
public UserDelegateEventArgs()
{

}

public User User
{
get;
set;
}

public Enums.ClientDelegateType Type
{
get;
set;
}

public Delegate UserDelegate
{
get;
set;
}
}

服务器发布服务:

//发布客户端委托注册事件,同时侦听客户端发送委托
RemotingConfiguration.RegisterWellKnownServiceType(typeof(SendDelegateService), "ClientDelegateSend", WellKnownObjectMode.Singleton);
ListenClientDelegateSend();

/// <summary>
/// 侦听客户端发送委托服务事件
/// </summary>
private void ListenClientDelegateSend()
{
SendDelegateService.OnDelegateSend += new ClientDelegateSendEventHandler(OnDelegateSend);
}

/// <summary>
/// 记录用户委托
/// </summary>
void OnDelegateSend(object sender, UserDelegateEventArgs e)
{
UserDelegateList.Add(string.Format("{0}:{1}", e.User.IPAddress, e.User.Port), e.Type, e.UserDelegate);
}

客户端主动发送委托登记,以消息接收为例:

//发送聊天信息接收委托
SendDelegateService sendDelObj = (SendDelegateService)Activator.GetObject(typeof(SendDelegateService), "http://localhost:10010/ClientDelegateSend");
ChatMessageSendEventHandler chatDel = new ChatMessageSendEventHandler(OnChatMessageSend);
sendDelObj.Send(usr, ServerCenter.Enums.ClientDelegateType.Chat, chatDel);

/// <summary>
/// 侦听服务器发送聊天消息服务
/// </summary>
private void OnChatMessageSend(object sender, ChatMessageEventArgs e)
{
//显示聊天窗口,接收消息
}

客户端委托已经记录完毕,最后看看服务器的聊天服务:

public delegate void ChatMessageSendEventHandler(object sender, ChatMessageEventArgs e);

/// <summary>
/// 聊天服务
/// </summary>
public class ChatService : MarshalByRefObject
{
public event ChatMessageSendEventHandler OnChatMessageSend = null;

/// <summary>
/// 发送消息服务
/// </summary>
public void ChatMessageSend(User from, User to, string msg)
{
if (OnChatMessageSend != null)
{
ChatMessageSendEventHandler del = UserDelegateList.GetDelegate(string.Format("{0}:{1}", to.IPAddress, to.Port), Enums.ClientDelegateType.Chat) as ChatMessageSendEventHandler;
if (del == null)
{
return;
}

try
{
ChatMessageEventArgs e = new ChatMessageEventArgs();
e.UserFrom = from;
e.UserTo = to;
e.Message = msg;
del(this, e);
}
catch
{
//发送消息失败
OnChatMessageSend -= del;
}
}
}

public override object InitializeLifetimeService()
{
return null;
}
}

服务器发布聊天消息发送服务:

//发布聊天消息发送服务
chatObj = new ChatService();
ObjRef refChatObj = RemotingServices.Marshal(chatObj, "SendMessageToUser");

当接收到客户消息时进行消息转发:

/// <summary>
/// 发送消息给指定客户端
/// </summary>
void ClientMessageSend(object sender, ChatMessageEventArgs e)
{
chatObj.ChatMessageSend(e.UserFrom, e.UserTo, e.Message);
}

到此为止,聊天程序已经完成了,我们可以时行注册并选择用户开始聊天了。
最后再进行服务器心跳设计,检测客户端是否已断开连接,具体的实施与前面基本一致,由服务器主动发送检测信息,这里不再详细描述。

 Demo下载

posted @ 2011-10-22 09:55  flysoul  阅读(2308)  评论(6编辑  收藏