使用ConcurrentQueue并发集合来设计网络服务器端

来源:互联网 发布:mac 不能玩炉石传说么 编辑:程序博客网 时间:2024/05/29 10:20

HP-SOCKET 很好的网络底层引擎,让我可以直接做服务器的其它部分。


HP-SOCKET 把客户端的请求放到了事件响应中,客户端接入服务器会通知到相应的函数:


OnAccept  客户端连接服务器触发
OnSend    服务器送数据给客户端触发
OnReceive  服务器收到客户端数据触发
OnClose   客户端关闭服务器触发
OnError   网络错误触发


网络服务器端,只需从这些触发的函数中收集数据,然后发回客户端即可。


客户端是多个对应于一个服务器的。


在OnReceive中,可能同时有很多的客户端都在给服务器发数据。


服务器我使用记录集来存放客户端对象,其他业务数据对象可使用记录集来存放。因为C#的对象查询LINQ功能对记录集的操作太完美了。

对于记录集List的操作,服务器设计专门的单线程来保证数据更新的一致性。


这种场景是,多个客户端请求和一个单一的对记录集的操作。


使用ConcurrentQueue并发集合刚好可以应付这种清空。



ConcurrentQueue并发集合来构成一个【队列】,它支持多个生产者产生数据放【队列】中,一个消费者从【队列】中一次一次的拿走数据。按照先进先出。

这里,我把多个客户端的请求放到【队列】中,启动一个单一线程,从【队列】中取一个请求,去更新记录集,并接下来处理业务逻辑更新记录集。

这样可以始终做到更新记录集的数据一致性,因为就只有一个线程在更新他们。









        //100ms 一次操作        private void c1()        {                                    while (true)            {                Thread.Sleep(100);                                        try                {                        //操作1  从操作队列中取最上面的来更新【服务器对象集合】                        ObjOpertion n = null;                        if (opertions.TryDequeue(out n))                        {                            opertionTotal--;                            label8.Text = opertionTotal.ToString();                                                           if (n.Kind == 100)                                      {                                         clients.Add(new ClientInfo { ConnId = n.ConnId, IpAddress = n.IpAddress, Port = n.Port, USER = "", Rtxt = "", Stxt = "", logontime = DateTime.Now.ToString() });                                        addMsg(string.Format(" > [{0},OnAccept] ", n.ConnId));                                     }                                else if (n.Kind == 200)                                    {                                            var a = (from ClientInfo in clients where ClientInfo.ConnId == n.ConnId select ClientInfo).FirstOrDefault();                                            //这个ID收了多少次计数                                                                                         a.Receives++;                                             // 从pData中获取数据,叠加放rtxt字符串中                                             a.Rtxt += n.Rtxt;                                                                    }                                else if (n.Kind == 300)                                    {                                        var a = (from ClientInfo in clients where ClientInfo.ConnId == n.ConnId select ClientInfo).FirstOrDefault();                                        a.Sends++;                                    }                                else if (n.Kind == 400)                                    {                                        // 客户离开了,删除这个client对象                                          var a = (from ClientInfo in clients where ClientInfo.ConnId == n.ConnId select ClientInfo).FirstOrDefault();                                          clients.Remove(a);                                          addMsg(string.Format(" > [{0},OnClose]", n.ConnId));                                    }                                                                  }                        //操作2 基于业务逻辑来更新【服务器对象集合】                        foreach (ClientInfo a in clients)                        {                                                //查找有没有"."                            if ( (!String.IsNullOrWhiteSpace(a.Rtxt))  &&   ( a.Rtxt.IndexOf('.') > -1) )                            {                                //取"."前的字符串,并按"|"分割                                string thistime_str = a.Rtxt.Substring(0, a.Rtxt.IndexOf('.'));                                string[] s = thistime_str.Split('|');                                ////////////////////////////                                //1|user|pass                                ////////////////////////////                                if ((sint(s[0]) == 1) && (a.USER.Length == 0)) //空用户,才能认证                                {                                                                                            //查询密码表                                    var sn = (from USERPASS in users where USERPASS.USER == s[1] && USERPASS.PASS == s[2] select USERPASS).FirstOrDefault();                                    var other = (from ClientInfo in clients where ClientInfo.USER == s[1] select ClientInfo).FirstOrDefault();                                    if ((sn != null) && (other == null)) //有用户密码记录,并不允许多次连接                                        {                                                                                a.USER = sn.USER;                                            addMsg(string.Format(" > [{0},login success]", a.ConnId ) );                                            sendToClient(a.ConnId, "101|you are welcome.");                                        }                                     else                                        {                                            sendToClient(a.ConnId, "101|login authentication failure.");                                            server.Disconnect(a.ConnId);                                            addMsg(string.Format(" > [{0}, login authentication failure]", a.ConnId));                                        }                                                                             }                                                            ////////////////////////////////////////////////                                //如果是2|, 就是消息文本数据    2|othername|txt                                ////////////////////////////////////////////////                                else if ((sint(s[0]) == 2) && (a.USER.Length>0) )                                {                                    var other = (from ClientInfo in clients where ClientInfo.USER == s[1] select ClientInfo).FirstOrDefault();                                                                                            if (other != null)                                    {                                        sendToClient(other.ConnId, "102|" + a.USER + "|" + s[2] + ".");    //转发送其它客户端   102|name|txt                                                                    }                                    else                                     {                                        sendToClient(a.ConnId, "102|" + s[1] + "|不在线,数据无法转发."); //告诉本客户端,数据无法转发                                      }                                }                                ////////////////////////////////////////////////                                //如果是9|, 就是心跳数据    发回客户端 109|客户端对象个数.                                ////////////////////////////////////////////////                                else if ((sint(s[0]) == 9) && (a.USER.Length > 0))                                {                                    sendToClient(a.ConnId, "109|" + clients.Count.ToString() + ".");                                    }                                ////////////////////////////////////////////////                                //即使是无效的指令,也执行一次后,清除该指令                                ////////////////////////////////////////////////                                a.Rtxt = a.Rtxt.Substring(a.Rtxt.IndexOf('.') + 1);                                                                                                            }                                                   }                        //操作3   服务器对象操作,list 的删除操作比较特别                       // if (delid.ToInt64() > 0)                       // {                       //     delid = new IntPtr(0);                        // }                                             }            catch (Exception ex)            {                addMsg(ex.Message);            }                             }        }




源代码下载

http://download.csdn.net/detail/ot512csdn/9361567




0 0
原创粉丝点击