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));
}
}
这个函数判断也有问题,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
[解决办法]
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一直被占用,直到占完。
请问实际的使用中线程会休眠多久? 如果while sleep 休眠太久是会block 线程的。
另外,lock (clients) 中间的代码会执行多久?
你可以在这两段中间插入Interlocked 计数,看看实际的情况如何。
我在想我不要这个lock (clients)可不可以,本来这个clients就只是一个list,用来存放所有的client而已,只是用来记录连接数和最终的一个client的释放,如果我不记录连接数应该就不需要clients,这样就不需要lock方法了,应该是可以的吧?
请问实际的使用中线程会休眠多久? 如果while sleep 休眠太久是会block 线程的。
另外,lock (clients) 中间的代码会执行多久?
你可以在这两段中间插入Interlocked 计数,看看实际的情况如何。
我在想我不要这个lock (clients)可不可以,本来这个clients就只是一个list,用来存放所有的client而已,只是用来记录连接数和最终的一个client的释放,如果我不记录连接数应该就不需要clients,这样就不需要lock方法了,应该是可以的吧?
这个呢?你没看?
看了,改成这样的代码了,你看看有问题吗?
//修改判断连接的方法
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;
}