异步的TCP服务器程序

xxgclj 2013-04-02 09:23:46
我做了个TCP服务器程序,用来监听各个客户端的连接,并与之通信。照着书上用异步的方式写了程序,以下是代码。


private void AcceptConnect()
{
IPAddress[] ip = Dns.GetHostAddresses(Dns.GetHostName());
listener = new TcpListener(ip[0], 51888);
listener.Start();
listBoxStatus.Invoke(setListBoxCallback, "开始等待客户连接");
while (isExit == false)
{
try
{
allDone.Reset();
AsyncCallback callback = new AsyncCallback(AcceptTcpClientCallback);
listener.BeginAcceptTcpClient(callback,listener);
allDone.WaitOne();
}
catch (Exception ex)
{
listBoxStatus.Invoke(setListBoxCallback,ex.Message);
break;
}
}
}

private void AcceptTcpClientCallback(IAsyncResult ar)
{
try
{
allDone.Set();
TcpListener myListener = (TcpListener)ar.AsyncState;
TcpClient client = myListener.EndAcceptTcpClient(ar);
listBoxStatus.Invoke(setListBoxCallback, "已接受客户连接:" + client.Client.RemoteEndPoint);
comboBox1.Invoke(setComboBoxCallback,client.Client.RemoteEndPoint.ToString());
ReadWriteObject readWriteObject = new ReadWriteObject(client);
clientList.Add(readWriteObject);
SendString(readWriteObject,"服务器已经接受连接");
readWriteObject.netStream.BeginRead(readWriteObject.readBytes,0,readWriteObject.readBytes.Length,ReadCallback,readWriteObject);
}
catch(Exception ex)
{
listBoxStatus.Invoke(setListBoxCallback, ex.Message);
}
}
private void ReadCallback(IAsyncResult ar)
{
try
{
ReadWriteObject readWriteObject = (ReadWriteObject)ar.AsyncState;
int count = readWriteObject.netStream.EndRead(ar);
richTextBoxReceive.Invoke(setRichTextBoxCallback,string.Format("[来自{0}]{1}",readWriteObject.client.Client.RemoteEndPoint,str));
if(isExit==false)
{
readWriteObject.InitReadArray();
readWriteObject.netStream.BeginRead(readWriteObject.readBytes, 0, readWriteObject.readBytes.Length, ReadCallback, readWriteObject);
}
}
catch(Exception ex)
{
//listBoxStatus.Invoke(setListBoxCallback,ex.Message);
}
}


基本按这个代码来的,小有改动。运行结果达到预期,但是发现两个问题,一个是CPU占用率会一直上升到100%,二是内存一直在涨。
第一个问题,我加了Thread.Sleep(1)解决问题。第二个问题我认为是出在AcceptConnection里,因为客户端会每隔1分钟都给我发个连接,我的服务器程序没有判断功能,对重复的连接也进行操作了,致使在没有新客户的情况下也新开线程。事实也是如此,通过任务管理器也可以看出我这个程序的线程数一直在增加。我在AcceptTcpClientCallback里做了个判断,对于重复的IP和端口直接返回,但是我不知道AcceptConnection里的callback如何释放掉。

不知我的理解对否,求解答。
...全文
396 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
xxgclj 2013-04-10
  • 打赏
  • 举报
回复
我终于发现问题在哪儿了。是设备没完全按照协议来做,有部分改动。以前就发现根协议有部分不同,今天又发现一个设备自己加的,就是它也要求服务器发心跳包(协议上只要求设备发心跳包)。如果服务器无心跳包,它会在一定时间后断开连接。 虽然浪费了我一些时间在找问题上,不过也使我对TCP方面加深了些理解。
xxgclj 2013-04-10
  • 打赏
  • 举报
回复
设备断开了连接,导致我读数据一直在那儿循环。而且因为设备断开了连接(我没发现,我当时以为它心跳包没按协议一直发),它会在一定时间后再发起连接,我的线程就又打开一个,如此反复。
xxgclj 2013-04-09
  • 打赏
  • 举报
回复
引用 12 楼 zanfeng 的回复:
接收零字节的问题。是.net的BUG.直接无视就可以了。
今天发现了这句。 如果当前使用的是面向连接的 Socket,那么 Receive 方法将会读取所有可用的数据,直到达到缓冲区的大小为止。如果远程主机使用 Shutdown 方法关闭了 Socket 连接,并且所有可用数据均已收到,则 Receive 方法将立即完成并返回零字节。 http://msdn.microsoft.com/zh-cn/library/8s4y8aff(v=vs.80).aspx
blackboycpp 2013-04-09
  • 打赏
  • 举报
回复
我基本已解决,今晚把代码发给你
xxgclj 2013-04-08
  • 打赏
  • 举报
回复
引用 12 楼 zanfeng 的回复:
接收零字节的问题。是.net的BUG.直接无视就可以了。
不是吧。我无视,可是程序处理它啊。我目前只有在ReadCallback里加上Thread.Sleep(),才能把CPU占用率降下来。
足球中国 2013-04-08
  • 打赏
  • 举报
回复
接收零字节的问题。是.net的BUG.直接无视就可以了。
xxgclj 2013-04-08
  • 打赏
  • 举报
回复
引用 8 楼 sp1234 的回复:
凡是“貌似”是多线程、异步的程序,我习惯于先看看有没有些while语句。如果写了,你就知道可能是多累赘、多垃圾了。 while语句跟阻塞是“苍蝇和蛐”,总是在一起的。正因为你错误地写了while语句,这个错误引起了你再用更错误的阻塞方式来对付它。所以我说它们是“苍蝇和蛐”的关系。 删掉你的while语句。类似于这样(不保证语法正确,理解就好了)C# code?……
我试用你的方式改写了程序,还不错,我感觉比用allDone好理解一些。 另: 我发现了当初CPU占用率一直增长的原因,是出在ReadCallback里,是它一直在反复自调用。我追踪了下,除了正常接收数据外,程序还在无休止地接收长度为0的报文。如果说是客户端设计地不好,可是我用WireShark分析时,并没有检测到这些长度为0的报文啊,求解。
  • 打赏
  • 举报
回复
上面 AcceptTcpClientCallback(); 这一条是多余的,请删除掉!
  • 打赏
  • 举报
回复
凡是“貌似”是多线程、异步的程序,我习惯于先看看有没有些while语句。如果写了,你就知道可能是多累赘、多垃圾了。 while语句跟阻塞是“苍蝇和蛐”,总是在一起的。正因为你错误地写了while语句,这个错误引起了你再用更错误的阻塞方式来对付它。所以我说它们是“苍蝇和蛐”的关系。 删掉你的while语句。类似于这样(不保证语法正确,理解就好了)
        private void AcceptConnect() 
        {
            IPAddress[] ip = Dns.GetHostAddresses(Dns.GetHostName());
            listener = new TcpListener(ip[0], 51888);
            listener.Start();
            listBoxStatus.Invoke(setListBoxCallback, "开始等待客户连接");
            AcceptTcpClientCallback();
            listener.BeginAcceptTcpClient(AcceptTcpClientCallback,listener);
        }

        private void AcceptTcpClientCallback(IAsyncResult ar) 
        {
            try 
            {
                TcpListener myListener = (TcpListener)ar.AsyncState;
                TcpClient client = myListener.EndAcceptTcpClient(ar);
                listBoxStatus.Invoke(setListBoxCallback, "已接受客户连接:" +                       client.Client.RemoteEndPoint);
                comboBox1.Invoke(setComboBoxCallback,client.Client.RemoteEndPoint.ToString());
                ReadWriteObject readWriteObject = new ReadWriteObject(client);
                clientList.Add(readWriteObject);
                SendString(readWriteObject,"服务器已经接受连接");
                readWriteObject.netStream.BeginRead(readWriteObject.readBytes,0,readWriteObject.readBytes.Length,ReadCallback,readWriteObject);
            }
            catch(Exception ex)
            {
                listBoxStatus.Invoke(setListBoxCallback, ex.Message);
            }
        listener.BeginAcceptTcpClient(AcceptTcpClientCallback,listener);
        }
本来是赶紧整洁的代码,就好像漂亮的姑娘,带上一个while式的垃圾帽子就显得臃肿和俗气了。 你的ReadCallback也是类似,如果需要连续读取数据,那么在这个方法内部应该调用BeginRead,而不是在 AcceptTcpClientCallback 内部搞什么 while 语句和阻塞。
  • 打赏
  • 举报
回复
通信的话你最好新建一个线程,要不通信会占用你的UI线程。
xxgclj 2013-04-02
  • 打赏
  • 举报
回复
我在判断重复的连接后先加了释放Client再return,应该callback就能被系统回收了,目前看貌似可以。
gomoku 2013-04-02
  • 打赏
  • 举报
回复
AcceptConnection一般不需要异步。其实你的WaitOne也阻塞了。 至于你的问题可能部分在于客户。 客户发起了连接,如果没有及时socket.Disconnect或tcpClient.Close,那么服务方就要空等着。 在服务端ReadCallback方面,没有断线后清理连接资源的措施。加上你把资源放到clientList里面,将导致内存不能回收。
xxgclj 2013-04-02
  • 打赏
  • 举报
回复
引用 1 楼 gomoku 的回复:
AcceptConnection一般不需要异步。其实你的WaitOne也阻塞了。 至于你的问题可能部分在于客户。 客户发起了连接,如果没有及时socket.Disconnect或tcpClient.Close,那么服务方就要空等着。 在服务端ReadCallback方面,没有断线后清理连接资源的措施。加上你把资源放到clientList里面,将导致内存不能回……
如果是重复的连接我在“ReadWriteObject readWriteObject = new ReadWriteObject(client);”之前就return了。而且断线后,我会把clientList里的相应项删除,应该不会造成资源释放不了。而且断线后,读数据有异常,会退出循环,结束ReadCallback。我就是不知道new的callback怎么释放掉。
xxgclj 2013-04-02
  • 打赏
  • 举报
回复
引用 1 楼 gomoku 的回复:
AcceptConnection一般不需要异步。其实你的WaitOne也阻塞了。 至于你的问题可能部分在于客户。 客户发起了连接,如果没有及时socket.Disconnect或tcpClient.Close,那么服务方就要空等着。 在服务端ReadCallback方面,没有断线后清理连接资源的措施。加上你把资源放到clientList里面,将导致内存不能回……
客户是台设备,没法改了。有什么好的建议吗?我对这方面不太熟。
xxgclj 2013-04-02
  • 打赏
  • 举报
回复
引用 2 楼 guyuekkk11 的回复:
通信的话你最好新建一个线程,要不通信会占用你的UI线程。
AcceptConnect()是在一个新建的线程里。

111,092

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧