读书人

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

发布时间: 2014-01-22 00:03:39 作者: rapoo

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)

读书人网 >C#

热点推荐