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

异步的TCP服务器程序解决方案

2013-04-12 
异步的TCP服务器程序我做了个TCP服务器程序,用来监听各个客户端的连接,并与之通信。照着书上用异步的方式写

异步的TCP服务器程序
我做了个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如何释放掉。

不知我的理解对否,求解答。
[解决办法]
AcceptConnection一般不需要异步。其实你的WaitOne也阻塞了。


至于你的问题可能部分在于客户。
客户发起了连接,如果没有及时socket.Disconnect或tcpClient.Close,那么服务方就要空等着。

在服务端ReadCallback方面,没有断线后清理连接资源的措施。加上你把资源放到clientList里面,将导致内存不能回收。
[解决办法]
通信的话你最好新建一个线程,要不通信会占用你的UI线程。
[解决办法]
凡是“貌似”是多线程、异步的程序,我习惯于先看看有没有些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 语句和阻塞。
[解决办法]
上面

   AcceptTcpClientCallback();

这一条是多余的,请删除掉!
[解决办法]
接收零字节的问题。是.net的BUG.直接无视就可以了。
[解决办法]
我基本已解决,今晚把代码发给你

热点排行