zoukankan      html  css  js  c++  java
  • 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下载

  • 相关阅读:
    兼容ie6:zindex
    分割网址/链接/url变量
    JS:给select添加option选项
    如果用QQ邮箱订阅一个网站或博客?
    input无边框
    有些标签的innerHTML属性是只读的
    满屏透明层
    文本框光标不居中?
    PHP:json_decode解析JSON数据
    开放平台/API
  • 原文地址:https://www.cnblogs.com/FlySoul/p/2220991.html
Copyright © 2011-2022 走看看