首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

TCP通信中线程数不断增加的有关问题

2014-01-22 
TCP通信中线程数不断增加的问题本帖最后由 jiangting1986 于 2014-01-16 21:08:54 编辑最近一直被这个问题

TCP通信中线程数不断增加的问题
本帖最后由 jiangting1986 于 2014-01-16 21:08:54 编辑 最近一直被这个问题纠结着,好难过啊,实在找不到了,求助各位大神。
具体的情况是这样的:现在有一个中心服务器,用来接收外面的设备通过网络上传的数据,然后进行处理,外面有20个设备,每个设备都有一张手机卡,发送信息到服务器上,正常的时候应该是建立20个TCP连接,线程数应该也不会比20个多太多的。但是现在遇到了问题,软件运行了一段时间之后,从任务管理器中查看,该软件的进程中竟然包含了1000个左右的线程,然后及时TCP连接还在,却再也无法处理连接发送过来的数据了,设备逐个处于掉线状态,这太不正常了,通过netstat查看TCP连接数,保持为20个,这到底是什么问题导致线程数不断增加的呢,有没有人碰到过,求分析,下面给出代码。


 /// <summary>
        /// 开始侦听,这个方法是启动侦听的方法
        /// </summary>
        void StartListen()
        {
            try
            {

                log.InfoFormat("运行接收服务。");

                var ServiceName = "PSPIPDRSCWinService";

                myListenerAndExcutor = MonitorServerCommand.GetInstance();
                ServiceName = System.Configuration.ConfigurationManager.AppSettings["ServiceName"];

                myListenerAndExcutor.allDone = allDone;
                myListenerAndExcutor.Port = System.Configuration.ConfigurationManager.AppSettings["ServicePort"].ConvertStrToInt(7100);
                ;


                if (myListenerAndExcutor == null)
                {
                    myListenerAndExcutor = MonitorServerCommand.GetInstance();

                }

                myListenerThread = new Thread(new ThreadStart(myListenerAndExcutor.ListenClientConnect));
                myListenerThread.Name = "PSPITcpLienster";
                
                //开始监听
                myListenerThread.Start();
            }
            catch (Exception ex)
            {

                log.FatalFormat("运行接收服务时错:{0}——{1}", ex.InnerException == null ? ex.Message : (ex.InnerException.Message ?? ex.Message), DateTime.Now);
                //throw;
            }

        }


//上面代码中线程调用的ListenClientConnect()方法,问题应该就出在这个地方才对
/// <summary>
        /// 异步执行
        /// </summary>
        public void ListenClientConnect()
        {
            //开始监听
            //myListener.Start();
            tcpListener = new TcpListener(SharedUtilities.Net.NetworkTools.GetLocalIP(), port);
            tcpListener.Start();
            while (!isStop)
            {
                try
                {
                    allDone.Reset();
                    //引用在异步操作完成时调用回调方法
                    AsyncCallback callBack = new AsyncCallback(AcceptTcpClientCallBack);

                    tcpListener.BeginAcceptTcpClient(callBack, tcpListener);


                    //开始一个异步操作接受传入的连接尝试
                    //myListener.BeginAcceptTcpClient(callBack, myListener);

                    //阻塞当前线程,直到收到客户连接信号
                    allDone.WaitOne();

                     
                }
                catch (Exception ex)
                {
                    log.ErrorFormat("侦听服务出错:{0}——{1}", ex.InnerException == null ? ex.Message : (ex.InnerException.Message ?? ex.Message), DateTime.Now);


                }
            }

            try
            {

                if (clients != null && clients.Count > 0)
                {
                    foreach (var client in clients)
                    {
                        try
                        {
                            client.Value.Close();

                            //20140102 by mjq
                            //client.Close();
                           
                        }
                        catch
                        {
                        }
                    }
                }
                CloseSocket();
            }
            catch
            {
            }

            // isStop = true;

        }


//ListenClientConnect()方法中异步执行的AcceptTcpClientCallBack()方法
/// <param name="ar"></param>
        private void AcceptTcpClientCallBack(IAsyncResult ar)
        {
            allDone.Set();

            TcpListener myListener = (TcpListener)ar.AsyncState;

            TcpClient client = null;// myListener.EndAcceptTcpClient(ar);
            try
            {
                
                client = myListener.EndAcceptTcpClient(ar);
               


                lock (clients)
                {
                    
                    //client.NoDelay = true;//获取或设置一个值,该值在发送或接收缓冲区未满时禁用延迟。 果 NoDelay 为 false,则直到 TcpClient 收集到相当数量的输出数据之后,它才会通过网络发送数据包。 
                    LingerOption lingerOption = new LingerOption(true, 10);

                    client.LingerState = lingerOption;


                    byte[] inOptionValues = new byte[sizeof(uint) * 3];
                    BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
                    BitConverter.GetBytes((uint)60000).CopyTo(inOptionValues, sizeof(uint));
                    BitConverter.GetBytes((uint)3000).CopyTo(inOptionValues, sizeof(uint) * 2);
                    client.Client.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);



                    byte[] buf = new byte[1024];
                    var ns_f = client.GetStream();
                    var count_f = ns_f.Read(buf, 0, buf.Length);
                    string login_str = System.Text.Encoding.Default.GetString(buf, 0, count_f).Replace("\r\n", "").ToUpper();
                    string mId = "";
                    if (login_str.StartsWith(HeartBitTag))
                    {
                        mId = login_str.Replace("LOGIN:", "").Replace("LOG", "").Substring(0, 2);

                    }
                    else
                    {
                        string pat = @"FFFFFFFFFF[0-9|A-E]";
                        Regex r = new Regex(pat, RegexOptions.IgnoreCase);

                        Match m = r.Match(login_str);
                        if (m.Success)
                        {
                            Group g = m.Groups[0];

                            Capture c = g.Captures[0];
                            login_str = login_str.Substring(c.Index, MsgStrLength);

                            int id = string.Format("{0:00}", login_str.Substring(GPRSStartTag.Length + GPRSAddr * 2, GPRSDeviceID * 2)).HexStringToInt();
                            if (id < 10)


                                mId = "0" + id;
                            else
                                mId = "" + id;
                        }
                    }

                    if (!mId.IsNullOrEmptyOrSpace())
                    {
                        if (clients.ContainsKey(mId))
                        {
                            IEnumerator<KeyValuePair<string, TcpClient>> kvp = clients.GetEnumerator();
                            kvp.MoveNext();
                            while (!kvp.Current.Key.Equals(mId))
                                kvp.MoveNext();
                            TcpClient drop_client = kvp.Current.Value;
                            drop_client.Close();
                            clients.Remove(mId);
                        }

                        clients.Add(mId, client);
                       
                    }

                }
                

                Debug.WriteLine(string.Format("接受一次连接:{0}——{1}:当前连接数:{2}", client.Client.RemoteEndPoint, DateTime.Now, clients.Count));


                
                Action<TcpClient> ac = (c) => ReadThread(c);
                ac.BeginInvoke(client, null, null);


            }
            catch (Exception ex)
            {
                try
                {
                    if (client != null)
                    {
                        client.Close();
                        client = null;

                    }
                }
                catch { }
                Debug.WriteLine(string.Format("接受连接时出错:{0}——{1}", ex.InnerException == null ? ex.Message : (ex.InnerException.Message ?? ex.Message), DateTime.Now));


              
            }
        }



希望各位大侠能帮我分析一下,我之前用线程调试的时候发现有很多线程都没有释放掉,已经不存在发送数据了的,却没有释放线程,我还没有找到原因。
[解决办法]
只需要一个线程对端口进行监听!
[解决办法]
 Action<TcpClient> ac = (c) => ReadThread(c);<<==你这句是连接了一个客户端就开一个线程吧。
[解决办法]
这代码怪怪的,都异步模式了,就不应该自己再启动线程了
[解决办法]
引用:
 Action<TcpClient> ac = (c) => ReadThread(c);<<==你这句是连接了一个客户端就开一个线程吧。

这是刚一个客户端的一个消息发来了,就无限地胡乱繁殖子线程了。
[解决办法]
引用:
这代码怪怪的,都异步模式了,就不应该自己再启动线程了


是的,各种貌似跟线程沾边的写法钱都胡乱招呼在一起,就好像一个花痴把所有带花的东西(包括棉被和雨披)都带在身上,然后在大街上行走。

[解决办法]
已经跟客户机连好管道了
var ns_f = client.GetStream();
何必再
Action<TcpClient> ac = (c) => ReadThread(c);
ac.BeginInvoke(client, null, null); 
var ns = client.GetStream(); 
不如改这样看一下,是不是不要这么多线程了呢?
//Action<TcpClient> ac = (c) => ReadThread(c);
//ac.BeginInvoke(client, null, null); 
ReadThread(ns_f);
void ReadThread(NetworkStream networkStream)

[解决办法]
引用:
Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

   Action<TcpClient> ac = (c) => ReadThread(c);
                ac.BeginInvoke(client, null, null);
这种是异步的,是线程的


那这个线程是什么时候释放的?我的线程一直在增加


执行完就自动释放,你里面是不是写死循环了,贴出来代码不全


里面是没有死循环的,因为程序可以执行几天,如果是死循环的话会很快就崩溃才对啊。。下面的那个代码就是这样子了,单纯的处理数据而已


        while (IsConnected(client) && !isStop) 
      估计是这个造成了死循环,
       把 IsConnected 函数发出来,可嫩连接已经断开了,不能判断出来


这个函数是这样的:
 public bool IsConnected(TcpClient tcpClient)
        {
            return tcpClient != null && tcpClient.Connected;
        }



 if (client.Available == 0)                    {                                                Thread.Sleep(200);                                                 continue;                    }                     var count = ns.Read(buff, 0, buff.Length);

这个判断也不太好,
直接var count = ns.Read(buff, 0, buff.Length);
如果count =0 就认为断开了


您好,其实这里不是判断client断开的,只是在client没有上报数据的时候让这个线程休眠一会,不然会导致cpu一直被占用,直到占完。



public bool IsConnected(TcpClient tcpClient)        {            return tcpClient != null && tcpClient.Connected;        }


 这个函数判断也有问题,tcpClient.Connected 反应的是上次的状态
 http://zhidao.baidu.com/link?url=nMq1qdMlS9iochxw5PPch1ilFR5UAqJEG6fQ2Za_RN06HGN9dNrQy5dCwetoY-7z9yf8jgxEf_DXUrpoeZNjwa 
这个呢?你没看?
[解决办法]
public bool IsConnected(TcpClient tcpClient)        {            return tcpClient != null && tcpClient.Connected;        }
这个函数判断也有问题,tcpClient.Connected 反应的是上次的状态
http://zhidao.baidu.com/link?url=nMq1qdMlS9iochxw5PPch1ilFR5UAqJEG6fQ2Za_RN06HGN9dNrQy5dCwetoY-7z9yf8jgxEf_DXUrpoeZNjwa
[解决办法]



引用:
Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

Quote: 引用:

   Action<TcpClient> ac = (c) => ReadThread(c);
                ac.BeginInvoke(client, null, null);
这种是异步的,是线程的


那这个线程是什么时候释放的?我的线程一直在增加


执行完就自动释放,你里面是不是写死循环了,贴出来代码不全


里面是没有死循环的,因为程序可以执行几天,如果是死循环的话会很快就崩溃才对啊。。下面的那个代码就是这样子了,单纯的处理数据而已


        while (IsConnected(client) && !isStop) 
      估计是这个造成了死循环,
       把 IsConnected 函数发出来,可嫩连接已经断开了,不能判断出来


这个函数是这样的:
 public bool IsConnected(TcpClient tcpClient)
        {
            return tcpClient != null && tcpClient.Connected;
        }



 if (client.Available == 0)                    {                                                Thread.Sleep(200);                                                 continue;                    }                     var count = ns.Read(buff, 0, buff.Length);

这个判断也不太好,
直接var count = ns.Read(buff, 0, buff.Length);
如果count =0 就认为断开了


您好,其实这里不是判断client断开的,只是在client没有上报数据的时候让这个线程休眠一会,不然会导致cpu一直被占用,直到占完。


这里不要使用Thread.Sleep(200)。使用timer。
[解决办法]
引用:
Quote: 引用:


请问实际的使用中线程会休眠多久? 如果while sleep 休眠太久是会block 线程的。
另外,lock (clients) 中间的代码会执行多久?
你可以在这两段中间插入Interlocked 计数,看看实际的情况如何。 

我在想我不要这个lock (clients)可不可以,本来这个clients就只是一个list,用来存放所有的client而已,只是用来记录连接数和最终的一个client的释放,如果我不记录连接数应该就不需要clients,这样就不需要lock方法了,应该是可以的吧?

如果一定要使用lock的话,建议使用.net 4.5 里面的 SemaphoreSlim.WaitAsync()
[解决办法]
引用:
Quote: 引用:


请问实际的使用中线程会休眠多久? 如果while sleep 休眠太久是会block 线程的。
另外,lock (clients) 中间的代码会执行多久?
你可以在这两段中间插入Interlocked 计数,看看实际的情况如何。 

我在想我不要这个lock (clients)可不可以,本来这个clients就只是一个list,用来存放所有的client而已,只是用来记录连接数和最终的一个client的释放,如果我不记录连接数应该就不需要clients,这样就不需要lock方法了,应该是可以的吧?


  我认为lock还是有必要的,不过你的代码不够“漂亮”,
   应该尽量减少Lock 的时间
   也就是说与clients无关的操作应该放到lock之外
    
    与clients无关操作。。。。。
    lock(clients)
   {  
   }
    还有 你clients是什么类型的,建议使用hasttalbe,
   那么你判断是否存不存在就不用循环了 ,直接用clients[mid]就可以了
    
[解决办法]
 > 不过你的代码不够“漂亮”,

楼上真客气,这代码,不入流的初级程序员的简单粗暴的COPY-PASTE,
实在是烂的可以,读得我头痛。

找个入门的人重写好了。
[解决办法]
引用:
Quote: 引用:


这个呢?你没看?


看了,改成这样的代码了,你看看有问题吗?


        //修改判断连接的方法


        public bool IsClosed(TcpClient tcpClient)
        {
            bool closed = false;
            byte[] testByte = new byte[1];
            try
            {

                //使用Peek测试连接是否仍存在

                if (tcpClient.Connected && tcpClient.Client.Poll(0, SelectMode.SelectRead))
                    closed = tcpClient.Client.Receive(testByte, SocketFlags.Peek) == 0;
            }
            catch (SocketException se)
            {
                closed = true;

            }
            return closed;
        }



        直接这样不可以吗?
     try
{
   return  tcpClient.Client.Poll(100, SelectMode.SelectRead);//100毫秒
}catch
{ return false;
}
SocketFlags.Peek这个我没用过
tcpClient.Client.Receive(testByte, SocketFlags.Peek) 

热点排行