谢谢。现在的程序如下
        //这是一个新线程
        private void AcceptConnect() 
        {
            //获取本机所有IP地址DD
            IPAddress[] ip = Dns.GetHostAddresses(Dns.GetHostName());
            listener = new TcpListener(ip[0], 51888);
            
            //开始侦听客户端
            listener.Start();               //然后新线程始终在这里循环
            while (isExit == false) 
            {
                try
                {
                    //等.WaitOne()来阻塞线程。
                    allDone.Reset();                    //引用在异步操作完成时调用的回调方法
                    AsyncCallback callback = new AsyncCallback(AcceptTcpClientCallback);                    listBoxStatus.Invoke(setListBoxCallback, "开始等待客户连接");                    //开始一个异步操作接受传入的连接尝试
                    listener.BeginAcceptTcpClient(callback, listener);                    //阻塞当前线程,直到收到客户连接信号
                    allDone.WaitOne();
                }
                catch (Exception err) 
                {
                    listBoxStatus.Invoke(setListBoxCallback, err.Message);
                    break;
                }
            } 
            //
        }
然后系统在接收到一个客户端连接后,自动调用回调函数如下
        private void AcceptTcpClientCallback(IAsyncResult ar) 
        {
            try
            {
                //允许线程需要进行
                allDone.Set();                TcpListener myListener = ar.AsyncState as TcpListener;                 TcpClient client = myListener.EndAcceptTcpClient(ar);   //偿试以非同步方式接入连接,并建立新的TcpClient处理与远程主机通讯                //UI刷新
                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);                //每接收一个客户端,加1 
                number_thread = number_thread + 1;
                textBox1.Invoke(setLableThreadCallback, number_thread.ToString());//在UI线程上显示
            }
            catch (Exception err) 
            {
                listBoxStatus.Invoke(setListBoxCallback, err.Message);
                return;
            }
        }
这里readWriteObject发送、接收信息,与客户端进行通讯。这样连续接入10个客户端,并且每个客户端进行通讯。请问,此时这10个客户端是共用一个线程,还是系统给它们各自开辟线程进行 “发送、接收”操作的呢?

解决方案 »

  1.   

    对于readWriteObject.netStream.BeginRead的工作方式,我理解不透它。
    因为从程序代码上后,每次每个客户端接入时,只调用过它一次,并没有第二次、第三次调用它。
    而每个客户端与服务端连接上后,就可以任意次通讯了。为什么?
      

  2.   

    我从来不在 AcceptConnect 中写 while 语句、WaitOne 语句。写了它们,显然你的程序的变得复杂了。你的代码,使得父线程被阻塞,然后等回调时父线程才继续执行(继续while循环),这完全是画蛇添足的写法,与其这样纠结还不如直接写成同步Accept。实际上,AcceptConnect 在注册完异步回调之后可以直接结束。然后等回调时在调用一次 AcceptConnect。至于说你问“这10个客户端是共用一个线程,还是系统给它们各自开辟线程进行 “发送、接收”操作的”,这也类似,这根本就是被误导出来的问题。10各连接所调用的AcceptTcpClientCallback过程,可能是在一个线程、也可能用了5个线程,也可能用了10个线程,完全看系统线程池怎么调配。但是有一点的明确的,就是调用时肯定是互不相干的,比如说第一个线程还没有放回系统线程池的时候肯定不会被线程池用于处理第二个异步Accept回调。而在 AcceptTcpClientCallback 方法中,也不过是向 NetworkStream 注册了异步 Read 方法,然后它就结束了。它既没有 while 循环,也没有阻塞。迅速结束 AcceptTcpClientCallback 方法,就能让此  I/O  线程立刻回归线程池。 等系统接收到消息到来,又是系统从线程池中分配出 I/O 线程来调用之前注册的 ReadCallback 委托。此时至于说是跟其它处理用了同一个线程,还是不是同一个线程,或者同一个用户的不同处理用了同一个线程还是1000个不同的线程,这都是线程池调度的事情。反正只要前一个线程的方法没有结束,系统线程池就不得不另外分配其它线程。而你的 ReadCallback 委托处理中同样也是这样,如果你是异步处理消息的,从而立刻结束ReadCallback委托方法,那么就能让迅速地释放 I/O 线程给系统池,从而提高通讯程序并发。总之,只有事件发生时才会占用线程。可不是纠结那么11个线程先在那里死循环着!
    你的 AcceptConnect 中不应该写“循环、阻塞”的语句。你在异步Read的程序中写的是对的,没有搞什么循环、阻塞。过程代码立刻结束,把 I/O 线程立即释放(回线程池),这是很重要的事情。绝不能占着 I/O 线程在那里做一堆 Worker 线程的事情,更不应该“循环、阻塞”线程。
      

  3.   


    看不到你的 readCallback 中的代码,不知道程序设计的逻辑是什么。如果你看不到继续调用它,那么可能是这个程序的设计者:
    1. 是在写一个“短链接”通讯程序,因此只要“读”数据然后“写”数据,之后就把连接关闭了。
    2. 他没有考虑一次 Read 可能不能读取完整的消息的问题,他以为一定可以再一次 Read 时得到客户端发来的所有数据。这第一点,是可以理解的。而第二点,可能是个 bug,当网路不是很好的时候就会出现读取数据不足、而需要多次Read的问题。
      

  4.   


    异步处理方式,即使你的服务端使用(仅仅)一个端口连接10万个在线客户端,可能占用线程也就是10几个。而使用线程少,基本上就说明你的程序设计得不纠缠CPU了。