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

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘欣的博客

你将成为第一个打赏博主的人!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值