基于Tcp通信的聊天程序微风IM(c#开源) -技术分析(一) 用户管理

在微风IM中,如果用户上线了,其他用户的用户列表中,此用户状态更新为上线状态,如果用户下线了,此用户的头像会变成灰色。

我们看一下相关的代码:

首先是客户端代码(1):

  UserInfo userInfo = new UserInfo();
                    userInfo.UserID = txtUserID.Text.Trim();
                    userInfo.Password = txtPassword.Text.Trim();

                    //发送契约类给服务器端,并获取返回的结果
                    UserLoginContract loginContract = newTcpConnection.SendReceiveObject<UserInfo, UserLoginContract>("UserLogin", "ResUserLogin", 8000, userInfo);

                    //如果登陆成功
                    if (loginContract.Message =="success")
                    {

                        跳转到主窗口
                        this.DialogResult = DialogResult.OK;
                    }
              

服务器端有与登陆相对应的处理方法

注册处理方法:

 NetworkComms.AppendGlobalIncomingPacketHandler<UserInfo>("UserLogin", IncomingLoginHandler);

处理方法:

 //处理用户登录 networkcomms框架会自动把收到的字节反序列化为对应的UserInfo类型的数据
        private void IncomingLoginHandler(PacketHeader header, Connection connection, UserInfo userInfo)
        {

            try
            {
                //从数据库中验证登录信息
                UserLoginContract resContract = DoRcUsers.Login(userInfo.UserID, userInfo.Password);                //把验证的结果返回给客户端
                connection.SendObject("ResUserLogin", resContract);

                 //如果客户端用户成功登陆,我们把此用户加入到用户管理器中
                if (resContract.Message == "success")
                {
                    lock (syncLocker)
                    {

                        //同一账号登陆,先退出已经登陆的客户端
                        if (userManager.ContainsKey(userInfo.UserID))
                        {
                            //如果此用户ID已经登陆,找到与此用户ID对应的网络连接,关闭此连接,                //关闭客户端用户连接,我们采用了一个间接的方式,即给客户端用户发一下让其自动退出的消息,客户端用户接到此消息后,会退出。服务器端的心跳检测机制会把                            //客户端退出的连接检查出来,并从系统中删除。
                            foreach (Connection conn in NetworkComms.GetExistingConnection(userManager[userInfo.UserID], ConnectionType.TCP))
                            {
                                conn.SendObject("CloseConnection", "msg");

                            }
                            //如果用户已经登陆,删除之
                            userManager.Remove(userInfo.UserID);

                        }
                        //注册新的用户 把新登陆的用户添加到用户管理器
                        if (!userManager.ContainsKey(userInfo.UserID))
                        {
                            userManager.Add(userInfo.UserID, connection.ConnectionInfo.NetworkIdentifier);
                        }

                    }

                    //用户上线后,通知其他用户                    //这个方法负责通知其他用户,当前用户登陆了,你那边可以把头像点亮了
                    UserStateNotify(userInfo.UserID, true);

                }
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "IncomingLoginHandler");
            }
        }

我们来看一下负责通知其他用户的这个方法

    // 某客户端用户的状态改变后,通知其他用户
        private void UserStateNotify(string userID, bool onLine)
        {
            try
            {
                //用户状态契约类
                UserStateContract userState = new UserStateContract();
                userState.UserID = userID;
                userState.OnLine = onLine;

                IList<ShortGuid> allUserID;

                lock (syncLocker)
                {
                    //获取所有用户字典中的用户ID  用户字典中的用户也就是所有的在线用户                     //allUserID 获取的是所有用户的网络ID  每一个客户端连接都对应一个网络ID 用于唯一标识一个网络连接。
                    allUserID = new List<ShortGuid>(userManager.Values);
                }

                //给所有用户发送某用户的在线状态
                foreach (ShortGuid netID in allUserID)
                {                    //根据网络ID获取网络连接
                    List<Connection> result = NetworkComms.GetExistingConnection(netID, ConnectionType.TCP);

                    if (result.Count > 0 && result[0].ConnectionInfo.NetworkIdentifier == netID)
                    {                        //给网络连接发送通知,有新的用户上线了,以及新用户的信息,客户端收到此消息后会把用户图标点亮
                        result[0].SendObject("UserStateNotify", userState);
                    }
                }
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "MainForm.UserStateNotify");
            }
        }

再来看一下服务器端的用户管理器

  //在线用户字典
        Dictionary<string, ShortGuid> userManager = new Dictionary<string, ShortGuid>();

<string,ShortGuid> string 用来存放用户ID ,ShortGuid用来存放当前用户网络连接对应的唯一网络ID。(通过此网络ID可以找到相应的Tcp连接,并通过连接发送消息给客户端)。

再回过头来看一下客户端收到某用户上线消息相关的代码:

首先客户端注册用户上线消息

  NetworkComms.AppendGlobalIncomingPacketHandler<UserStateContract>("UserStateNotify", IncomingUserStateNotify);

处理方法

   private void IncomingUserStateNotify(PacketHeader header, Connection connection, UserStateContract userStateContract)
        {             //如果是用户上线
            if (userStateContract.OnLine)
            {
                lock (syncLocker)
                {                     //设定此用户的状态属性,属性更新后,用户状态会跟着更新
                    Common.GetDicUser(userStateContract.UserID).State = OnlineState.Online;
                }
            }
            else
            {
                lock (syncLocker)
                {
                    Common.GetDicUser(userStateContract.UserID).State = OnlineState.Offline;
                }
            }
        }

接着来讲客户端用户登陆,登陆后,跳转到主界面

在主界面窗口中,获取我的好友列表

   public void GetAllMyFriend()
        {
            //获取之前先清空用户字典
            Common.AllUserDic.Clear();
            if (Common.AllUserDic.Count == 0)
            {
                //向服务器端发送信息并获取结果  获取的用户信息中包含用户状态
                UserListContract userListContract = Common.TcpConn.SendReceiveObject<string, UserListContract>("GetFriends", "ResGetFriends", 5000, Common.UserID);

                //遍历加载好友
                foreach (UserContract user in userListContract.UserList)
                {
                    //把用户添加到字典中
                    //根据性别 分别使用不同的图标
                    if (user.IsMale)
                    {
                        Common.AddDicUser(user.UserID, new User(user.UserID, user.Name, user.Declaring, user.IsMale == true ? UserSex.Male : UserSex.Female, Properties.Resources.q1, "电话", "电子邮件", user.OnLine == true ? OnlineState.Online : OnlineState.Offline));
                    }
                    else
                    {
                        Common.AddDicUser(user.UserID, new User(user.UserID, user.Name, user.Declaring, user.IsMale == true ? UserSex.Male : UserSex.Female, Properties.Resources.q2, "电话", "电子邮件", user.OnLine == true ? OnlineState.Online : OnlineState.Offline));

                    }
                }
            }
        }

服务器端对应的处理代码:

首先注册处理方法:

     //客户端获取好友列表
            NetworkComms.AppendGlobalIncomingPacketHandler<string>("GetFriends", IncomingGetFriends);

处理方法:

        //客户端获取某用户的好友列表的服务器端处理方法
        private void IncomingGetFriends(PacketHeader header, Connection connection, string userID)
        {
            try
            {                //从数据库获取所有好友
                IList<UserContract> userContractList = DoRcUsers.GetAllMyFriends();

                UserListContract listContract = new UserListContract(userContractList);

                lock (syncLocker)
                {                    //遍历服务器上的用户管理器,如果用户在线,则设置用户状态为在线状态
                    foreach (UserContract theuser in userContractList)
                    {
                        //判断其他好友是否在线
                        if (userManager.ContainsKey(theuser.UserID))
                        {
                            theuser.OnLine = true;
                        }
                    }
                }

                connection.SendObject<UserListContract>("ResGetFriends", listContract);
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "IncomingGetFriends");
            }
        }

 /// <summary>
    /// 用户信息契约类
    /// </summary>
    [ProtoContract]
    public class UserContract
    {
        //用户ID
        [ProtoMember(1)]
        public string UserID { get; set; }
        //用户名
        [ProtoMember(2)]
        public string Name { get; set; }
        //用户描述
        [ProtoMember(3)]
        public string Declaring { get; set; }
        //性别
        [ProtoMember(4)]
        public bool IsMale { get; set; }
        //初始在线状态
        [ProtoMember(5)]
        public bool OnLine { get; set; }

        public UserContract() { }

        public UserContract(string userID, string userName, string underWrite, bool male,bool onLine)
        {
            this.UserID = userID;
            this.Name = userName;
            this.Declaring = underWrite;
            this.IsMale = male;
            this.OnLine = onLine;
        }
    }

UserContract契约类

 /// <summary>
    /// 用户信息列表,比如可以传递我的所有好友信息
    /// </summary>
    [ProtoContract]
    public class UserListContract
    {
        [ProtoMember(1)]
        public IList<UserContract> UserList { get; set; }

        //下面这段代码主要是为了防止列表为空,如果列表为空,不加入下面这段代码,序列化会有问题
        [DefaultValue(false), ProtoMember(2)]
        private bool IsEmptyList
        {
            get { return UserList != null && UserList.Count == 0; }
            set { if (value) { UserList = new List<UserContract>(); } }
        }

        public UserListContract() { }

        public UserListContract(IList<UserContract> userList)
        {
            this.UserList = userList;
        }

    }

UserListContract契约类

至此,用户登陆基本讲清楚了

www.networkcomms.cn

www.cnblogs.com/networkcomms

时间: 02-10

基于Tcp通信的聊天程序微风IM(c#开源) -技术分析(一) 用户管理的相关文章

基于Tcp通信的聊天程序微风IM(c#开源) -技术分析(三) 客户端下线

在微风IM中,当某个客户端下线后,其他客户端能够感知到此用户已经下线,并把其头像图标变成灰色. 感知连接的掉线,是networkcomms框架内置的功能,服务器通过心跳检测得知某连接掉线,会从networkcomms内部维护的连接列表中删除此连接,并触发相应的委托. 我们要处理某连接掉线,只需要注册  NetworkComms.AppendGlobalConnectionCloseHandler 方法即可 服务器端代码如下: //如果某客户端离线,触发此方法 NetworkComms.Appen

c#编写的基于TCP通信的微风IM 版本3 新年新UI

在微风 IM 版本2中我们实现了局域网内的p2p通信,具体见: [开源下载]c#编写的聊天程序微风IM 版本2 增加局域网P2P通信 前面有朋友说微风IM的UI有点朴素,也确实,于是到网上去淘了件新衣服. 新的UI来自于网上开源程序,由"翱翔的雄鹰"老师编写的完全开源的QQ2010.(c# WinForm).新的UI中有许多自定义控件,我从其中学到了很多Winfrom控件制作的知识. 比如,带边框的文本框  文本框 鼠标经过时,显示边框的按钮  按钮 鼠标经过时显示边框效果的Check

基于TCP通信的套接字

基于tcp通信的套接字必须先启动服务器再去启动客户端去链接服务器 客户端 服务端 这只是基于tcp通信的一个简单的套接字  只能进行一次的通信 服务端                                                                                                 客户端 这是能多次通信的套接字 在阻塞前添加一个循环就能实现一个链接循环 服务端 客户端 这是模拟ssh实现远程执行命令 粘包问题 二.两种情况下会发生粘包

基于开源技术的上网行为管理方案实现案例

基于开源技术的上网行为管理方案实现案例 互联网已经成为人们工作.生活过程不可或缺的工具.在企业普遍存在着电脑和互联网络滥用的严重问题,网购.各种直播.电影.P2P工具下载等与工作无关的行为占用了有限的带宽,影响了工作,作为企业如何监管?购买商业软件?我看,未必适合你的公司. 选择开源工具怎么样?下面我为大家介绍两款开源工具及案例.用以实现用户上网内容审计和行为监控.这些技术的基础基于嗅探技术,所以大家在浏览下文时,一定要对网络嗅探技术有深刻理解. 1.开源工具Xplico Xplico工作原理是

基于TCP的字符串传输程序

---恢复内容开始--- LINUX中的网络编程是通过SOCKET接口来进行的. Socket(套接字) Socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了.Socket的定义类似于文件句柄的定义.下面的流程图大概描述了基于TCP协议的网络编程过程.同学们只需要对大概流程有一个初步认识即可,暂时不必深究每个函数的意义.因为在后面的实训子任务中,每一个函数的具体内容和使用方法都会讲到. TCP协议socket流程图 基本套接字

第二十五、二十六天:基于UDP的网路聊天程序

连续四天学习套接字的编程,可见套接字的重要性了.基于TCP和UDP分别写了两个程序.一是利用TCP实现一个服务器对多个客户端,客户端你发送信息,服务器就从事先准备好的五个字符串中随机回复一条.另一个是利用UDP实现两个人的对话,对话时可以是多个信息同时输入. 先是第一个程序.要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过accept函数接受并创建一个线程来处理.线程的创建函数为int pthread_create(pthread_t * thread, pth

java总用Socket 基于tcp通信模拟

/* * 基于TCP协议的socket通信,实现用户登录 */ public class ServerMain1 { public static void main(String[] args) { try { //1.创建一个服务器端的Socket,ServerSocket ,指定绑定的端口,并监听此端口 ServerSocket serverSocket=new ServerSocket(8888);//端口已设置1023以后的端口 //2.调用accept()方法开始监听,等待客户端连接

【开源下载】c#编写的聊天程序微风IM 版本2 增加局域网P2P通信

新年第一天 恭祝大家新年快乐 一直有朋友问P2P相关的问题,最近有时间在微风IM的基础上,实现了P2P通信,共享给大家,希望大家批评指正. 源码下载 (只包含源码,无插入式广告:)  数据库下载   数据库与第一版相同没有变化 我们知道在网络通信中,如果所有的通信都通过服务器转发,会增加服务器的负担,如果实现了P2P,客户端之间直接通讯,比如聊天或者传送文件时不再通过服务器,而是客户端之间直接通信,将会有效的减轻服务器的负担,提高程序的效率. 本节相关的P2P,指的是通过TCP协议,在局域网中实

【c#源码】基于TCP通信的客户端断线重连

源码下载 在CS程序中,断线重连应该是一个常见的功能. 此处的断线重连主要指的是服务器端因为某种故障,服务器端程序或者系统进行了重新启动,客户端能够自动探测到服务器端掉线,并尝试重新进行连接 本程序基于来自英国的开源c#通信框架的networkcomms(2.3.1版本) 先看一下效果 初始状态: 当服务器端程序关闭后,客户端会自动探测到,并在客户端显示相关信息 然后,我们设定为每隔5秒重连一次,可以自定义设置重连的次数,比如说重连50次,如果还没有重连成功,则放弃重连 然后我们重新启动服务器端