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下载

  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/FlySoul/p/2220991.html
Copyright © 2011-2022 走看看