问题描述
现在有一个项目是这种业务关系:一个pc需要以socket方式连接到至少10个服务端,然后服务端会不定时发送数据回来,pc端就将这些数据保存到数据库,用户就以wpf或者winform方式查看。受socket方式现在,需要单开线程进行查询有没有数据收到,从而进行处理,我试了几种方式,但结果都使电脑卡得连歌都不能正常播放。1.直接给每个socket连接开一个线程,这样就至少有10个线程,反正运行起来机会不能动2.以threadpool方式,并没有感觉到效果3.以select模型实现,效果倒是有,但是不大以上所有方式在控制台程序中还是不错,一转到winform就不行了。在这里向各位大神请教一下还有没有其他方式解决,什么方案都可以提。我这里想了一个改变架构的方式,编写控制台程序在一台服务器上(不懂服务器),专门负责解析数据,pc就只是从服务器调去数据,这样应该能最大解决pc性能问题,但是这样需要调动的资源就多了不知还有没有其他方法????
解决方案
解决方案二:
如果你非要用多线程,多线程里要加sleep,不要无限死循环其实你完全可以用异步方式编程,BeginReceive,有数据来了就会调用回调函数了不要开10个线程死等
解决方案三:
引用1楼Z65443344的回复:
如果你非要用多线程,多线程里要加sleep,不要无限死循环其实你完全可以用异步方式编程,BeginReceive,有数据来了就会调用回调函数了不要开10个线程死等
谢谢!!!找时间试一下不知道beginreceive的内部机制,就怕和select模型一样,将那些socket加入select模型只需要一个线程就能解决,但是最终结果却很失望。另外sleep是当头一棒,把这个给忘了,这个项目实时性要求不高,20s一次应该都没问题,还可以把缓存搞大点
解决方案四:
异步方式编程的机制就是基于windows消息循环,线程池动态执行,回调机制它可能会动态的开个线程去执行,也可能根本就是用当前线程去执行的(如果当前线程很空闲的话),总之你控制不了也不需要关心它到底被哪个线程去执行了,执行完就会调用回调函数好让你执行后续的代码你可以在BeginReceive之后,回调函数里,接收到数据了,然后存数据库,然后继续BeginReceive等待下一次的数据到来
解决方案五:
你需要的仅仅是10个tcpclient而已..
解决方案六:
写过连接很多的代码可能你没用不过你可以参考下.foreach(variteminRunData){varsocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);socket.Connect("",9999);var注册信息="5A-"+item.Key+"-E6-A5";socket.Send(注册信息.ToByte校验完成());System.Threading.Thread.Sleep(100);socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,newAsyncCallback(ReceiveMessage),socket);Console.WriteLine("建立"+item.Key+"连接");}staticvoidReceiveMessage(IAsyncResultar){varsocket=ar.AsyncStateasSocket;try{varlength=socket.EndReceive(ar);byte[]reallData=newbyte[length];Array.Copy(buffer,reallData,length);doWork(newobjectState(){s=socket,data=reallData});}catch(Exception){}finally{socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,newAsyncCallback(ReceiveMessage),socket);}}staticvoiddoWork(objecto){varrs=oasobjectState;byte[]data=rs.data;Socketar=rs.s;varcmd=data.ToHexString();}
都是客户端有数据自己判断是谁发送的就行了.
解决方案七:
最终也就是dict<string,int>serverserver.forsocket.con(key,value)socket.BeginReceive(xx,xx,xx,xx,newcallback(xxoo).null)voidxxoo(reslt){//所有服务端的数据都在这个方法里..通过socketserver的IP来判断是那个服务器}
简单的写下不是很麻烦把我上面的整理下就可以了.而且不涉及到什么"多线程"当然这异步的东西你要在UI上面显示还是得做点别的
解决方案八:
分明自己是服务器,却楞说成客户端。如果你是客户端,为何不能主动访问服务器?同步监听应采用select模型,不需要sleep以免误事(加入你刚挂起,数据就来了呢?)select本身就会挂起循环,直到通讯状态发生变化也可使用异步方式,不了解细节的话,有点不敢用绝对不要用demo方式,那只是帮助你理解通讯的过程。毫无实用价值
解决方案九:
引用7楼xuzuning的回复:
分明自己是服务器,却楞说成客户端。如果你是客户端,为何不能主动访问服务器?同步监听应采用select模型,不需要sleep以免误事(加入你刚挂起,数据就来了呢?)select本身就会挂起循环,直到通讯状态发生变化也可使用异步方式,不了解细节的话,有点不敢用绝对不要用demo方式,那只是帮助你理解通讯的过程。毫无实用价值
他所有的服务端都是推送的方式,所以他这个确实是客户端,而不是服务器
解决方案十:
Dictionary<string,int>server=newDictionary<string,int>();server.Add("192.168.1.1",2222);server.Add("192.168.1.2",2222);server.Add("192.168.1.3",2222);server.Add("192.168.1.4",2222);server.ToList().AsParallel().ForAll(item=>{varclient=newSystem.Net.Sockets.TcpClient();client.Connect(item.Key,item.Value);client.Client.BeginReceive(buffer,0,buffer.Length,System.Net.Sockets.SocketFlags.None,newAsyncCallback(ReceiveMessage),client);});voidReceiveMessage(IAsyncResultar){varsocket=ar.AsyncStateasSocket;try{varlength=socket.EndReceive(ar);byte[]reallData=newbyte[length];Array.Copy(buffer,reallData,length);ThreadPool.QueueUserWorkItem(newWaitCallback((state)=>{doWork(newobjectState(){s=socket,data=reallData});}));}catch(Exception){}finally{socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,newAsyncCallback(ReceiveMessage),socket);}}staticvoiddoWork(objecto){varrs=oasobjectState;byte[]data=rs.data;Socketar=rs.s;}
至于你后面提到的解决方案如果你需要看历史数据那么把数据存放起来一定是一个不错的选择.如果实时性的话真的不需要服务器了你可以本地直接连接10台服务器拿数据没有必要自己做一个服务器然后在转发到本地...就相当于你的软件作为客户端连接服务器但是实际上服务器作为10个tcp服务器的客户端又去连接他们
解决方案十一:
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Net.Sockets;usingSystem.Threading;namespaceConsoleApplication2{classProgram{staticbyte[]buffer=newbyte[1024];staticvoidMain(string[]args){Dictionary<string,int>server=newDictionary<string,int>();server.Add("192.168.1.119",9949);server.Add("192.168.1.105",9949);server.ToList().AsParallel().ForAll(item=>{Console.WriteLine("con");varclient=newTcpClient();client.Connect(item.Key,item.Value);client.Client.BeginReceive(buffer,0,buffer.Length,System.Net.Sockets.SocketFlags.None,newAsyncCallback(ReceiveMessage),client);});Console.ReadLine();}staticvoidReceiveMessage(IAsyncResultar){varsocket=ar.AsyncStateasTcpClient;try{varlength=socket.Client.EndReceive(ar);byte[]reallData=newbyte[length];Array.Copy(buffer,reallData,length);ThreadPool.QueueUserWorkItem(newWaitCallback((state)=>{doWork(newobjectState(){s=socket,data=reallData});}));}catch(Exceptionex){Console.WriteLine(ex.Message);}finally{socket.Client.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,newAsyncCallback(ReceiveMessage),socket);}}staticvoiddoWork(objecto){varrs=oasobjectState;vara=string.Join("-",rs.data.Select(d=>d.ToString("X2")).ToArray());Console.WriteLine(a);}}}publicclassobjectState{publicTcpClients{get;set;}publicbyte[]data{get;set;}}
解决方案十二:
上面的代码,我用了2个"服务器"做了测试通过了你想要的结果..1`成功连接服务器并且在2个服务器上分别看到1条来自同一个客户端的长连接..2`使用服务器分别给这2个长连接发送数据客户端都可以收到并且执行了doWork方法.到此你的业务走完成了.但是你的需求不仅如此..因为你要通过不同服务器发送来的数据做不同的操作..所以objectState.s字段就可以拿到服务器的信息(ip)就可以做你想要的了..
解决方案十三:
引用11楼diaodiaop的回复:
上面的代码,我用了2个"服务器"做了测试通过了你想要的结果..1`成功连接服务器并且在2个服务器上分别看到1条来自同一个客户端的长连接..2`使用服务器分别给这2个长连接发送数据客户端都可以收到并且执行了doWork方法.到此你的业务走完成了.但是你的需求不仅如此..因为你要通过不同服务器发送来的数据做不同的操作..所以objectState.s字段就可以拿到服务器的信息(ip)就可以做你想要的了..
你的解决方案效果挺好,很明显,我还加大到20个socket都没什么影响。另外发现一个问题起始那一两次数据接收是乱序的,后面的数据都按创建连接对象的反向顺序调用回调的
解决方案十四:
引用7楼xuzuning的回复:
分明自己是服务器,却楞说成客户端。如果你是客户端,为何不能主动访问服务器?同步监听应采用select模型,不需要sleep以免误事(加入你刚挂起,数据就来了呢?)select本身就会挂起循环,直到通讯状态发生变化也可使用异步方式,不了解细节的话,有点不敢用绝对不要用demo方式,那只是帮助你理解通讯的过程。毫无实用价值
谢谢提醒,但是我这边确实是客户端,只是这个概念在这里正好容易让人混淆。我这个服务器并不是指那种大型服务器,而只是一个带socketservice的网络设备
解决方案十五:
顶一下,我这边正好也要做这样的项目,学习了