问题描述
在网上搜索到的资料上说引用
程序执行BeginAcceptTcpClient方法后,即在线程池中自动创建需要的线程,同时在自动创建的线程中监听客户端连接请求。
不知道是不是这样。请教大神其中的细节,或者出处。谢谢!
解决方案
解决方案二:
这肯定是有误导成分的。系统级的东西是事件驱动的,哪里跟初学者一样去搞什么线程进行“死循环、阻塞、额外监听”呢。这个方法注册了回调。当系统收到消息后,才会创建(或者说使用)I/O子线程回调。
解决方案三:
谢谢。现在的程序如下//这是一个新线程privatevoidAcceptConnect(){//获取本机所有IP地址DDIPAddress[]ip=Dns.GetHostAddresses(Dns.GetHostName());listener=newTcpListener(ip[0],51888);//开始侦听客户端listener.Start();//然后新线程始终在这里循环while(isExit==false){try{//等.WaitOne()来阻塞线程。allDone.Reset();//引用在异步操作完成时调用的回调方法AsyncCallbackcallback=newAsyncCallback(AcceptTcpClientCallback);listBoxStatus.Invoke(setListBoxCallback,"开始等待客户连接");//开始一个异步操作接受传入的连接尝试listener.BeginAcceptTcpClient(callback,listener);//阻塞当前线程,直到收到客户连接信号allDone.WaitOne();}catch(Exceptionerr){listBoxStatus.Invoke(setListBoxCallback,err.Message);break;}}//}
然后系统在接收到一个客户端连接后,自动调用回调函数如下privatevoidAcceptTcpClientCallback(IAsyncResultar){try{//允许线程需要进行allDone.Set();TcpListenermyListener=ar.AsyncStateasTcpListener;TcpClientclient=myListener.EndAcceptTcpClient(ar);//偿试以非同步方式接入连接,并建立新的TcpClient处理与远程主机通讯//UI刷新listBoxStatus.Invoke(setListBoxCallback,"已接受客户连接:"+client.Client.RemoteEndPoint);comboBox1.Invoke(setComboBoxCallback,client.Client.RemoteEndPoint.ToString());//读取数据ReadWriteObjectreadWriteObject=newReadWriteObject(client);clientList.Add(readWriteObject);SendString(readWriteObject,"服务器已经接受连接,请通话");readWriteObject.netStream.BeginRead(readWriteObject.readBytes,0,readWriteObject.readBytes.Length,ReadCallback,readWriteObject);//每接收一个客户端,加1number_thread=number_thread+1;textBox1.Invoke(setLableThreadCallback,number_thread.ToString());//在UI线程上显示}catch(Exceptionerr){listBoxStatus.Invoke(setListBoxCallback,err.Message);return;}}
这里readWriteObject发送、接收信息,与客户端进行通讯。这样连续接入10个客户端,并且每个客户端进行通讯。请问,此时这10个客户端是共用一个线程,还是系统给它们各自开辟线程进行“发送、接收”操作的呢?
解决方案四:
另外,如果这样的程序结构,当客户端多达100个时,通讯会不会变慢、变卡?我理解是如果是“系统给它们各自开辟线程进行“发送、接收”操作”,那就不会变卡。我测试过在本机上开辟20个客户端与之通讯(传送1页A4纸字符),很通畅。
解决方案五:
对于readWriteObject.netStream.BeginRead的工作方式,我理解不透它。因为从程序代码上后,每次每个客户端接入时,只调用过它一次,并没有第二次、第三次调用它。而每个客户端与服务端连接上后,就可以任意次通讯了。为什么?
解决方案六:
我从来不在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线程的事情,更不应该“循环、阻塞”线程。
解决方案七:
引用4楼abc2363789187的回复:
对于readWriteObject.netStream.BeginRead的工作方式,我理解不透它。因为从程序代码上后,每次每个客户端接入时,只调用过它一次,并没有第二次、第三次调用它。而每个客户端与服务端连接上后,就可以任意次通讯了。为什么?
看不到你的readCallback中的代码,不知道程序设计的逻辑是什么。如果你看不到继续调用它,那么可能是这个程序的设计者:1.是在写一个“短链接”通讯程序,因此只要“读”数据然后“写”数据,之后就把连接关闭了。2.他没有考虑一次Read可能不能读取完整的消息的问题,他以为一定可以再一次Read时得到客户端发来的所有数据。这第一点,是可以理解的。而第二点,可能是个bug,当网路不是很好的时候就会出现读取数据不足、而需要多次Read的问题。
解决方案八:
引用3楼abc2363789187的回复:
另外,如果这样的程序结构,当客户端多达100个时,通讯会不会变慢、变卡?我理解是如果是“系统给它们各自开辟线程进行“发送、接收”操作”,那就不会变卡。我测试过在本机上开辟20个客户端与之通讯(传送1页A4纸字符),很通畅。
异步处理方式,即使你的服务端使用(仅仅)一个端口连接10万个在线客户端,可能占用线程也就是10几个。而使用线程少,基本上就说明你的程序设计得不纠缠CPU了。