QQ架构讨论

 http://groups.google.com/group/dev4server/browse_thread/thread/0d72668d11c4886b/a6d202489cabf285#a6d202489cabf285

hi, all:
  我把第一个问题选为:QQ的架构。呵呵,题目是不是有点大?QQ现在的最高在线用户数是1900万,我们来讨论一下要作一个这样的架构如何来作更好,大家积极发­言,这也是我这个周末为自己选择的思考题,呵呵。大家积极畅所欲言。
  我们讨论的问题可以包括但不限于这些内容:
  1.登录时的负载如何解决的
  2.服务器主要作哪些事,负载如何解决
  3.数据库负载如何解决 
 

 

我来这看热闹的,也没做过啥大系统,随便说两句啊:)

 

按照我们的设想,对于3,是不会使用数据库来撑大访问量的,特别是一些不需要实时更新的数据,会通过一个的Server对数据进行汇总,然后在数据库比较空闲的­时间段进行批量更新。而客户端查询相关信息,也不会直接查询数据库,而是直接连接Server来获取。对于这些Server群的负载,就有很多方法了,没做过,­就不纸上谈兵啦。

另外说说Web负载,根据HTTP协议返回的信息,QQ是使用Apache来实现Web负载的,Apache虽然定制非常方便,扩展性强,但是性能并不是最优的­。Google就实现了它自己的WebServer
GWS
(Gmail中是GFE??),跟它的GFS紧密结合起来,性能做到最好。

好了,搬个小板凳看老大们发言啊@_@


    回复作者    转发  
QQ的后台用的是MySQl数据库哦,呵呵,是不是不可思议啊?不过整个MySqL都是面目全非了,被QQ的人修改的不成人形。性能非常的高,尤其数据的读取和­存储。
QQ的服务器技术号称世界领先,呵呵,

    回复作者    转发  
只知道QQ的广告服务器是分省的,广告下发要一到两天,其他的就不知道了

登陆和登出确实是个比较麻烦的事情,要通知到所有的好友,所以用户数据必须是集中的,登陆有时候通知会失效,不知道是不是UDP的协议,听大家说说

2006/2/18, tingya <flyd...@gmail.com>:

 

> QQ的后台用的是MySQl数据库哦,呵呵,是不是不可思议啊?不过整个MySqL都是面目全非了,被QQ的人修改的不成人形。性能非常的高,尤其数据的读取和­存储。
> QQ的服务器技术号称世界领先,呵呵, 

    回复作者    转发  
 
  我认为qq登录也是差不多这样。
其登录采用udp(默认登录方式), 登录服务器(相当于NS)不需要保持连接的,负载很好做。
至于对话,qq和msn一样,都是P2P消息(可以打开msn的连接日志看到),不过msn在8.0之前,不支持离线消息。

所以,我很关键的技术问题,应该在于数据库上,一些表必须违反范式,做特别的冗余。

On 2/18/06, Roger Chen <chen...@gmail.com> wrote:

 

 

接触网络编程有一年多了, 感触颇深啊,
在此我要感谢 Roger Chen
,他帮我解答过不少的问题,谢了,

我一直在做XMPP Server 端的开发工作, 是基于Jive wildfire
来做二次开发, 由于Jabber 是采用 TCP
的方式来交换信息,也有用 HTTP
的方式,那是在5222端口被封的情况下,我们会通过HTTP
的80 断口来交换信息,基于TCP 的 Server
有一个缺点,那就是必须要保持连接,这是很浪费资源的,当达到十万
- 百万级的在线后,我认为最好的方式是基于UDP 的
Server ,那也是最灵活的,做P2P 的IM Client
也是最灵活的。

 

 

 
Skype 是做P2P IM 最好的一个,
他的方式是我最为欣赏的,不是QQ MSN
的架构能比的,真的,这种IM
的架够才是我们需要实现与学习的。

    回复作者    转发  

象QQ这样的规模是采用分布构价的,有点象DNS服务器不是完全一样,但是可以用来理解巨大的访问量可以被复数的服务器分担。QQ的服务器也应该分DS、NS、­SB三种或其他若干,其实就是在实际应用中服务器设置的比例不同,我不知道非会员是否服务器需要记录聊天记录如果不要NS负荷也不大,在线也不用实时连接的这样­NS的负荷就大幅度下降了。而P2P是QQ用户之间交换数据于服务器无关忽略不计。而离线问题,只有在一位用户已经不在线的情况下,才向服务器发送聊天记录,或­者该用户是会员在向对方发送记录的同时在向服务器发送记录,这样服务器只需要处理会员的聊天记录和暂时无法到达的聊天记录。一台服务器用10万的并发流量来说(­理论),而且10W个用户并非同时向服务器发送记录。用户登陆由DS
NS负责的,通知到所有的好友。这个由其他服务器负责,登陆、离线发生的频率更加稀疏。这样负载不会很大。其实不够了再加服务器。关键是构架可以扩展。对于数据­库我觉得他们是采用分布式数据库。QQ对用户没有汇总式查询。将一些用户的数据放在树的某的节点上。可以把每个节点设置成数据服务器。这样就把查询量分散了。所­有数据并不在一台服务器上,QQ应该是分布式的因为理论上不需要汇总数据,除非需要高效的汇总查询。

 

 

关于我的思路的优缺点:

优点是:
这样的思路类似于现实生活中电话号码的管理,它是分地区的,也就是分段的,我个人认为这样以后的扩展相对来说可能简单一点.

缺点是:
作为一个解决方案,这个思路并没有充分考虑到根据当前用户在线数来实现动态平衡的目标.比如说1~100万内的在线人数很少,而100万~200万号内在线的人­很多,那么这两个不同号段的服务器负载就会完全不一样,从而浪费了服务器资源.

克服缺点的办法:
如果要实现完全根据当前在线用户数来实现服务器负载的动态平衡,那就得将chat server与db server拨离, 让chat
server这一层完全按动态均衡的思路来作,
而db这一块的工作,可以抽象成一个数据管理层来作,但具体的用户数据存储仍然采用分段存储的方式,为不同的号段作不同的数据库存储. 而chat
server这一层的思路, 基本上也是master + chunk的方式,客户端最终仍然是与chat保持长连接.

 

hi, all:
    top说他那里现在无法访问google网站, 不知道各位能否正确访问?
    下面是代top发的他关于QQ架构的理解:

top(木……) 说:
    一个必要考虑的问题是登陆时要以地域就近的原则,提供最快的网络响应,所以实际服务应用层应与数据层耦合的.分段式管理可以运用在数据存储的分布式构架中,这是­一个数据层的概念,即如何有效的组织分布式数据.因为QQ在实际操作中在即时通讯中需要查询的数据量极少,写入数据层的信息量在所有通讯量中也占很小的比重.所­以考核负载可从服务应用层和数据层两方面来考虑.服务应用层主要考虑用户的时实性与服务器负载平衡问题.数据层主要考虑如何提供更有效的分布式存储方案.即采用­黑盒的想法,我们在设计服务层时,可意想的认为我们象一个虚拟的数据服务器提出数据请求必然会获得响应.至于服务层如何实现认为是一个黑盒,无须考虑.接着我们­只需考虑在服务层的各服务器上如何存储转发暂存取得的数据以提高效率(减少向虚拟数据服务器的查询量,减少与客户端的通讯量,减少服务层各服务器之间的通讯量为­目的)

top(木……) 说:
    其实我们只是说了一个大框架,其实在服务器的配置功能的划分服务层网络的构架上有很多细节问题.这些要针对实际运用需求而分别配置,所以,在设计之前最好先将需­求梳理归纳.不过这个工程玩大了,呵呵

    我(sodme)的观点:
    与top想法一样, 数据层是个黑盒, 相对独立. 至于其内部, 采用分段管理. 

 

 

 

 

http://topic.csdn.net/t/20060616/12/4825364.html

QQ服务器要和那么多客户端连接,服务器是怎么做到的?

楼主cppkiller(实事求是) 2006-06-16 12:40:05 在 .NET技术 / C# 提问

一个端口对一个客户吗?那得多少端口啊? 问题点数:20、回复次数:78Top

1 楼califord(远方)回复于 2006-06-16 13:06:11 得分 0

一般是用AJAX来实现的Top

2 楼woshi_zjf()回复于 2006-06-16 13:48:31 得分 0

不是吧     可能用的是机群Top

3 楼shalen520(Love will keep us alive)回复于 2006-06-16 13:51:16 得分 0

除了登陆什么的之外,一般是点对点的Top

4 楼GXY2005(民工甲:杀富济贫,打土豪分田地)回复于 2006-06-16 14:17:49 得分 0

最大端口号可以到65535,你怕什么,这才是一台服务器!机群呢Top

5 楼jack651(雨浪)回复于 2006-06-16 14:31:45 得分 0

QQ用的是多服务器群  
  Top

6 楼cppkiller(实事求是)回复于 2006-06-16 15:11:29 得分 0

<<<  
  shalen520(Love   will   keep   us   alive):  
  除了登陆什么的之外,一般是点对点的  
  >>>  
  服务器和客户端没有连接吗?好像有连接啊,要不怎么一个好友上线我的QQ中他的头像就会闪?这肯定是服务器干的。  
   
  另外,机群好像是个好主意,但如果全国上K万用户在线,那又得多少机群呢?看起来不是好的解决方案。就算QQ用户同时100万在线,那也得要多少机器啊?而这些机器就是维护一个连接,并时不时发送点消息而已,大量的消息通过各客户机之间的点对点传输了。总之,用每客户一个端口的方法,再用机群实现,不太经济。  
   
  如果是http服务器,比如新浪的,那么,可以理解它怎么对付那么多客户机。因为每个IE连上并传完页面数据后,连接就断开了。所谓无连接协议就是说的http。  
   
   
   
  Top

7 楼kingwkb(烂皮鞋)回复于 2006-06-16 15:20:04 得分 0

集群Top

8 楼kingwkb(烂皮鞋)回复于 2006-06-16 15:25:04 得分 0

没有长久连接好象  
  登陆时候发送给服务器登陆消息     服务器上记录该号登陆  
  然后客户段会没阁一段时间向服务器发送维护在线请求,假如一个Q号登陆了,但在规定时间内服务器没有收到他的维护在线响应,服务器就让他离线     因为在服务器有该号的好友列表       选取在线的像他们发送该号的离线信息       反之登陆一样  
   
  这样服务器只要维护列表就可以了!Top

9 楼MonkWang(象写情书一样写程序)回复于 2006-06-16 15:27:16 得分 0

不清楚   来了解了解Top

10 楼luhongming(andy)回复于 2006-06-16 15:28:37 得分 0

4楼的,QQ说同时在线人数已经突破2000万了。  
  照你说,就算一台机器可用端口有5000个,那也得1000台机器。Top

11 楼luhongming(andy)回复于 2006-06-16 15:31:11 得分 0

打错了,一台机器就算100000个,那就得用200台机器。Top

12 楼ld_thinking(懒得想)回复于 2006-06-16 15:32:25 得分 0

1000?  
   
  前几年传奇只怕都不止这个数了   更别提qq了Top

13 楼negat()回复于 2006-06-16 15:44:46 得分 0

udp组播登陆消息。。。  
  Top

14 楼xiaohuan(屎蜢)(球球的)回复于 2006-06-16 16:05:44 得分 0

目录服务器Top

15 楼HAVENT(夜雨流星℡)回复于 2006-06-16 16:13:05 得分 0

……楼主对通讯了解太少  
   
  服务器只需开1个端口就足够了  
   
  腾讯服务器好像多开了几个端口  
  人家服务器牛,网络宽阔  
   
  最重要的是QQ只负责维护用户列表而已,真正的通讯除了转发的都是P2P连接,除了第一次向服务器获取对方地址外就不会叨扰服务器了(当然,QQ会定期上服务器告知服务器自己还健在,顺便下载好友在线情况)Top

16 楼cbo5()回复于 2006-06-16 16:14:17 得分 0

服务器只使用1个端口就够了Top

17 楼c_jiazheng()回复于 2006-06-16 16:19:06 得分 0

p2pTop

18 楼cppkiller(实事求是)回复于 2006-06-16 16:22:53 得分 0

<<<<<  
  HAVENT(夜雨流星℡)  
  ……楼主对通讯了解太少  
   
  服务器只需开1个端口就足够了  
   
  腾讯服务器好像多开了几个端口  
  人家服务器牛,网络宽阔  
   
  最重要的是QQ只负责维护用户列表而已,真正的通讯除了转发的都是P2P连接,除了第一次向服务器获取对方地址外就不会叨扰服务器了(当然,QQ会定期上服务器告知服务器自己还健在,顺便下载好友在线情况)  
  >>>>>>>>  
  确实了解不多。  
  照你说的,服务器开一个端口,那么怎么同时接待多个客户端登陆连接的?  
  另,看来服务器维护一个列表是个好办法。  
  Top

19 楼hony113()回复于 2006-06-16 16:58:00 得分 0

我是新来的,不是很懂!   学习中。。。。。Top

20 楼heyuqing(heyuqing)回复于 2006-06-16 17:10:17 得分 0

同意"HAVENT(夜雨流星℡)"   的说法Top

21 楼ziyeping(觉醒的学生)回复于 2006-06-16 17:32:41 得分 0

和通讯服务器差不多吧,全国那么多手机呢,开端口好象不大实际哦Top

22 楼yiming0755()回复于 2006-06-16 17:35:29 得分 0

腾迅公司就在我公司旁边,一层楼全是服务器。Top

23 楼wq_quake(Paladin 象风一样的游侠)回复于 2006-06-17 08:30:31 得分 0

不至于吧,楼主学什么专业的,稍微知道点网络协议的都应该懂啊。服务器端的一个端口就可以接受成千上万个客户端连接。真不想参与这个问题,我还以为讨论完成端口的帖子呢。楼主看看indy吧。理论是实践的需要,没有实践你也永远不会明白理论。Top

24 楼zhgroup(王员外)回复于 2006-06-17 09:04:56 得分 0

QQ的服务器端有若干个服务器,第个服务器维护一定数量的用户列表,这个应该是由一个主登陆服务器来动态配置的。服务器端用一个端口来监听由客户端发来的消息,并对此消息排队,然后依次处理。服务器端只是维护在线用户列表,每当有用户登陆、退出、隐身等,就将其消息发送给该用户的所有在线好友。Top

25 楼l51(山中人)回复于 2006-06-17 10:59:15 得分 0

垃圾帖,基本網絡知識都沒有的人在這裡討論...........  
   
  鬱悶,浪費俺的時間........  
   
  居然還有人說   HTTP   是無連接協議.......  
   
  居然沒有人提過   Socket   ,隻有一位提了一下完成端口,  
   
  精彩的標題裡面包著一堆狗屎,包括這個帖子........  
  Top

26 楼isline(缘清)回复于 2006-06-17 11:11:47 得分 0

udp协议本来就比tcp占用资源少,加上用户分布在不同机器,这样理解就好多了。Top

27 楼AgainWarning()回复于 2006-06-17 11:18:13 得分 0

很明显可以开线程咯。Top

28 楼cppkiller(实事求是)回复于 2006-06-17 11:33:55 得分 0

<<<wq_quake(Paladin   象风一样的游侠):  
  不至于吧,楼主学什么专业的,稍微知道点网络协议的都应该懂啊。服务器端的一个端口就可以接受成千上万个客户端连接。真不想参与这个问题,我还以为讨论完成端口的帖子呢。楼主看看indy吧。理论是实践的需要,没有实践你也永远不会明白理论。  
  >>>  
  学的是考古啊,没办法,考古的没饭吃,到IT业里来了,结果IT业也是闹饥荒啊,只好乞讨了。  
   
  我倒是想过,难道一定要一个端口对一个客户吗?这不是难为死了服务器吗?可是,我不知道一个端口怎么对付多个客户。这就是我的问题的起因。你楼下还有个写繁体的,估计是为港澳和台湾台胞服务的吧?既然高人,就不吝赐教吧,别郁闷了。  
  Top

29 楼mhorse(火狮)回复于 2006-06-17 11:35:57 得分 0

多服务器的动态负载均衡Top

30 楼ZengMuAnSha(曾牧暗鲨)回复于 2006-06-17 11:48:44 得分 0

你们都搞错了,还是去学学TCP/IP吧!!  
   
  一个服务器连接数量跟端口好无关系!  
   
  跟线程有关系,跟机器配置和操作系统最大限度有关系  
   
  比如说WIN2K支持255个线程   在加个线程共享池就能支持到1K个   在加上集群   1k*n*0。8=    
   
  在加上地域布置,1亿都能搞定!!  
   
  QQ   只是连接服务器查找自己的信息和朋友的信息,再就是接受QQ的广告而已,直接P2P朋友Top

31 楼cppkiller(实事求是)回复于 2006-06-17 12:08:41 得分 0

ZengMuAnSha(曾牧暗鲨):  
  你能否给段代码,就是那种winsock连接请求,不要port参数的?服务器和客户端的代码。。。。  
  Top

32 楼Hellohuan(java 好不好???)回复于 2006-06-17 13:02:09 得分 0

1000个服务器算什么       真是的...           面试说最少在1000台linux机器中数据共享解决方案...Top

33 楼ehxz(郝晓宗)回复于 2006-06-17 13:12:04 得分 0

自己下个类似的软件装装玩玩.Top

34 楼bjjj(椰酥)回复于 2006-06-17 17:35:01 得分 0

赞同  
  <<zhgroup(Hotel   California)    
  <<服务器端用一个端口来监听由客户端发来的消息,并对此消息排队  
   
  考虑最简单的一台服务器的情况,服务器监听一个端口,当服务器收到多个用户的连接请求时,根据收到请求的顺序将请求放入队列中,并依次处理  
  Top

35 楼alby(批发各种型号黄瓜)回复于 2006-06-17 17:51:45 得分 0

用一个网络嗅探器看看就清楚了  
  Top

36 楼cppkiller(实事求是)回复于 2006-06-17 18:20:46 得分 0

排队的方法,似乎有一定道理,但延迟就很难受了。设想一下排到第10000位的客户端,等得花儿都谢了还没见反应,那会是什么感受啊?  
  前面笔误,http是个无状态协议,不是无连接协议。Top

37 楼cppkiller(实事求是)回复于 2006-06-17 18:57:00 得分 0

未处理的“System.Net.Sockets.SocketException”类型的异常出现在   system.dll   中。  
   
  其他信息:   通常每个套接字地址   (协议/网络地址/端口)  
  只允许使用一次。  
   
  这是我在做实验时,试图让一个服务器用同一个port连接多个客户端时,debug报告的错误信息。供大家参考吧,希望继续讨论。Top

38 楼c2u()回复于 2006-06-17 20:19:23 得分 0

我估计:  
   
  刚开始是把自己的登录IP、时间发给服务器,  
   
  并从服务器获取好友的IP列表(最近1天登录过的),  
   
  再刷新到历史记录(本地数据库)存的好友中的IP地址中  
   
  然后依据这些IP,用UDP一个接一个地点他们……看是否在线……  
   
  接到响应的就是在线,没有响应的就不在线;  
   
  然后每隔一断时间向服务器发送自己的IP地址(也可能每隔一段时间向好友们发送在线的UDP数据包P2P的),但不获取好友IP列表,(减轻服务器负载)在线好友的更新,通过好友上线时(或者定时)发送的UDP数据包判断。  
   
  如果对方在线,点对点聊天不经过服务器;  
   
     聊天时建立基于TCP连接的SOCKET……  
   
     或视频时建立UDP连接的SOCKET……  
   
     传文件时很可能是建立基于TCP连接的……  
   
  如果对方不在线,聊天记录存入服务器数据库,三层C/S模式。  
   
  我认为从编程和服务器的负载情况来看,上面的方法是最简单的,  
   
  所以QQ是基于TCP/IP协议的,  
   
  不过他肯定自己开发了一套类似FTP协议的聊天协议规则……  
   
  服务端接受请求时应该建立的应该是异步SOCKET……Top

39 楼007cs(syan)回复于 2006-06-17 23:46:18 得分 0

QQ好像是基于UDP的  
  通讯是客户端对客户端的,所以服务器的压力不是想象的那么大。Top

40 楼Juchiyufei(三更半夜我送你回家.总统也许我做不到.今生难得的遇见你,我们就应该在一起.....)回复于 2006-06-18 03:01:05 得分 0

upTop

41 楼dk385(大康)回复于 2006-06-18 03:49:02 得分 0

我估计这样:  
  服务器用一线程监听某个端口(这里称之为监听主线程吧),当收到客户端的连接请求时,新建一线程进行处理用户请求。而这并不影响服务器监听主线程,所以它能同时接受多个客户端登陆连接请求。所以并不需要一个端口对一个客户。  
  Top

42 楼cppkiller(实事求是)回复于 2006-06-18 10:36:17 得分 0

dk385(大康):  
  从前面我帖出来的错误信息就可以知道,一个服务器同一端口对多个客户连接是不行的。无论你用多少线程,只有一个socket,要连多个客户端,就不行。Top

43 楼sbdx(塞北的雪)回复于 2006-06-18 11:19:57 得分 0

QQ服务器是用HTTP协议的。80端口  
  然后QQclient定时获取、更新相关资料。  
   
  看看QQ的协议就知道了Top

44 楼hanfeichunan(病态天使)回复于 2006-06-18 14:02:14 得分 0

Loading   Balance   techTop

45 楼dk385(大康)回复于 2006-06-18 14:10:38 得分 0

从前面我帖出来的错误信息就可以知道,一个服务器同一端口对多个客户连接是不行的。无论你用多少线程,只有一个socket,要连多个客户端,就不行。  
  ===================  
  楼主,你还是先去好好看一下有关通讯方面的资料吧。  
   
  当客户端连接到服务端的监听端口时,服务端可以通过  
  Socket   s=this.listener.AcceptSocket()这样的方式取得当前客户端到服务端的连接Socket,  
  也就是说,对于TCP协议,是允许多个客户端连接到服务端的同一端口的。  
  如:在服务器端开始在端口14500上监听:  
  TcpListener   listener=new   TcpListener(14500);  
  listener.Start();  
   
  下面这段代码可以用服务器监听主线程循环执行。  
  if(listener.Pending())  
  {  
      Socket   s=listener.AcceptSocket();//取得当前客户端到服务端的连接Socket.  
      if(s.Connected)  
  {  
      Thread   th=new   Thread(new   ThreadStart(Run)); //这里用另外一线程去处理连接请求.  
  th.Start();  
  }  
  }  
   
  Top

46 楼EVOLution124(奇迹小子)回复于 2006-06-18 14:15:32 得分 0

晕   http协议端口通常就是80,csdn开这个端口我们就能访问了,这不就是一个端口实现多连接对象嘛。Top

47 楼cppkiller(实事求是)回复于 2006-06-18 14:18:52 得分 0

大康:  
  未处理的“System.Net.Sockets.SocketException”类型的异常出现在   system.dll   中。  
   
  其他信息:   通常每个套接字地址   (协议/网络地址/端口)  
  只允许使用一次。  
   
  ==========  
  以上是debug的信息。从这个信息看,每套接字只允许使用一次。  
  另,没有找到通讯方面的好的资料。还请赐教哪里有好的。Top

48 楼surefire()回复于 2006-06-18 16:50:48 得分 0

大康:  
  未处理的“System.Net.Sockets.SocketException”类型的异常出现在   system.dll   中。  
   
  其他信息:   通常每个套接字地址   (协议/网络地址/端口)  
  只允许使用一次。  
   
  ==========  
  以上是debug的信息。从这个信息看,每套接字只允许使用一次。  
  另,没有找到通讯方面的好的资料。还请赐教哪里有好的。  
   
  ============================  
   
  你这种情况是不是说在同一个线程里每个套结字只允许使用一次啊?  
   
  我觉得QQ服务器维持2000万人同时在线没什么稀奇的,因为这个在线的概念不是我们想的那样2000万用户都登录到服务器了,正如前面的所讲,服务器只是维护一个QQ用户状态列表而已,这对于现在大型的服务器来说没什么难的。QQ客户端通过一定的时间间隔向服务器发送自己的状态(也有可能是服务器通过一定的时间间隔来查询他所维护的QQ列表里所有QQ客户端的在线状态),我们用QQ的时候可以发现,如果我们本地网络正常,而与服务器断开后,本地机器的QQ并没有马上显示离线,而是过一定的时间,可以得知,QQ不是时时刻刻“在线”的!Top

49 楼dk385(大康)回复于 2006-06-18 19:08:45 得分 0

每套接字只允许使用一次,这没错。  
  服务端并不需要为每一个客户端提供一个专门的端口来通讯。  
  通常,服务端通过监听某一端口来接受所有客户端的连接请求.  
  关于通讯方面的资料,看msdn就是。  
  下面是个简单的示例:  
  服务端:  
  using   System;  
  using   System.Drawing;  
  using   System.Collections;  
  using   System.ComponentModel;  
  using   System.Windows.Forms;  
  using   System.Data;  
  using   System.Net.Sockets;  
  using   System.Threading;  
  using   System.IO;  
  namespace   Sockets  
  {  
  ///   <summary>  
  ///   Form1   的摘要说明。  
  ///   </summary>  
  public   class   frmServer   :   System.Windows.Forms.Form  
  {  
  private   System.Windows.Forms.ListBox   lsbox;  
  private   System.Windows.Forms.Button   btnStart;  
  private   System.Windows.Forms.Button   btnStop;  
  private   System.Threading.Thread   ListenerThread;  
  private   System.Threading.Thread   DataThread;  
  private   bool   m_IsStop;  
  private   int   m_Port;  
  private   TcpListener   listener;  
  private   System.Collections.ArrayList   TCPServerSession;  
  private   System.Windows.Forms.Label   lblMsg;  
  ///   <summary>  
  ///   必需的设计器变量。  
  ///   </summary>  
  private   System.ComponentModel.Container   components   =   null;  
   
  public   frmServer()  
  {  
  //  
  //   Windows   窗体设计器支持所必需的  
  //  
  InitializeComponent();  
  m_IsStop=true;  
  m_Port=15000;  
  TCPServerSession=new   ArrayList();  
  //  
  //   TODO:   在   InitializeComponent   调用后添加任何构造函数代码  
  //  
  }  
   
  ///   <summary>  
  ///   清理所有正在使用的资源。  
  ///   </summary>  
  protected   override   void   Dispose(   bool   disposing   )  
  {  
  if(   disposing   )  
  {  
  if   (components   !=   null)    
  {  
  components.Dispose();  
  }  
  }  
  base.Dispose(   disposing   );  
  }  
   
  ///   <summary>  
  ///   应用程序的主入口点。  
  ///   </summary>  
  [STAThread]  
  static   void   Main()    
  {  
  Application.Run(new   frmServer());  
  }  
  #region   Windows   窗体设计器生成的代码  
  ///   <summary>  
  ///   设计器支持所需的方法   -   不要使用代码编辑器修改  
  ///   此方法的内容。  
  ///   </summary>  
  private   void   InitializeComponent()  
  {  
  this.btnStart   =   new   System.Windows.Forms.Button();  
  this.lsbox   =   new   System.Windows.Forms.ListBox();  
  this.btnStop   =   new   System.Windows.Forms.Button();  
  this.lblMsg   =   new   System.Windows.Forms.Label();  
  this.SuspendLayout();  
  //    
  //   btnStart  
  //    
  this.btnStart.Location   =   new   System.Drawing.Point(22,   26);  
  this.btnStart.Name   =   "btnStart";  
  this.btnStart.Size   =   new   System.Drawing.Size(130,   23);  
  this.btnStart.TabIndex   =   0;  
  this.btnStart.Text   =   "Start   Listener";  
  this.btnStart.Click   +=   new   System.EventHandler(this.btnStart_Click);  
  //    
  //   lsbox  
  //    
  this.lsbox.ItemHeight   =   12;  
  this.lsbox.Location   =   new   System.Drawing.Point(22,   64);  
  this.lsbox.Name   =   "lsbox";  
  this.lsbox.Size   =   new   System.Drawing.Size(268,   124);  
  this.lsbox.TabIndex   =   1;  
  //    
  //   btnStop  
  //    
  this.btnStop.Location   =   new   System.Drawing.Point(160,   26);  
  this.btnStop.Name   =   "btnStop";  
  this.btnStop.TabIndex   =   2;  
  this.btnStop.Text   =   "Stop";  
  this.btnStop.Click   +=   new   System.EventHandler(this.btnStop_Click);  
  //    
  //   lblMsg  
  //    
  this.lblMsg.Location   =   new   System.Drawing.Point(36,   194);  
  this.lblMsg.Name   =   "lblMsg";  
  this.lblMsg.Size   =   new   System.Drawing.Size(204,   18);  
  this.lblMsg.TabIndex   =   3;  
  //    
  //   frmServer  
  //    
  this.AutoScaleBaseSize   =   new   System.Drawing.Size(6,   14);  
  this.ClientSize   =   new   System.Drawing.Size(398,   221);  
  this.Controls.Add(this.lblMsg);  
  this.Controls.Add(this.btnStop);  
  this.Controls.Add(this.lsbox);  
  this.Controls.Add(this.btnStart);  
  this.Name   =   "frmServer";  
  this.Text   =   "Server";  
  this.ResumeLayout(false);  
   
  }  
  #endregion  
   
  private   void   btnStart_Click(object   sender,   System.EventArgs   e)  
  {  
  this.m_IsStop=false;  
  this.listener=new   TcpListener(m_Port);  
  this.listener.Start();  
   
  ListenerThread=new   Thread(new   ThreadStart(AccecptRequest));  
  ListenerThread.Start();  
   
  DataThread=new   Thread(new   ThreadStart(ReceiveData));  
  DataThread.Start();  
  }  
   
  private   void   btnStop_Click(object   sender,   System.EventArgs   e)  
  {  
  this.m_IsStop=true;  
  this.listener.Stop();  
  ListenerThread.Abort();  
  DataThread.Abort();  
  }  
  public   void   AccecptRequest()  
  {  
  while(!this.m_IsStop   )  
  {  
  if(listener.Pending())  
  {  
   
  Socket   s=this.listener.AcceptSocket();  
     
  TCPServerSession.Add(s);  
  if(s.Connected)  
  {  
   
  string   ServerEndPoint=s.LocalEndPoint.ToString()+"/r/n";  
  Byte[]   buf=System.Text.Encoding.ASCII.GetBytes(ServerEndPoint.ToCharArray());    
  s.Send(buf);  
   
  string   mpoint=   s.RemoteEndPoint.ToString();  
  this.lsbox.Items.Add(mpoint);  
  this.lblMsg.Text="Current   Session   Count:"+this.TCPServerSession.Count.ToString();  
  }  
  }  
  Thread.Sleep(100);  
  }  
  }  
   
  public   void   ReceiveData()  
  {  
  System.Collections.ArrayList   CloseSocketList=new   ArrayList();  
  while(!this.m_IsStop   )  
  {  
  CloseSocketList.Clear();  
  foreach(Socket   s   in   TCPServerSession)  
  {  
  NetworkStream   ns=new   NetworkStream(s);  
  StreamReader   reader=new   StreamReader(ns);  
  if(ns.DataAvailable)  
  {  
  string   result=reader.ReadLine();  
  if(result.StartsWith("Close"))  
  {  
  CloseSocketList.Add(s);  
  }  
  }  
  }  
  foreach(Socket   s   in   CloseSocketList)  
  {  
  this.lsbox.Items.Remove(s.RemoteEndPoint.ToString());  
  s.Close();  
  TCPServerSession.Remove(s);  
  }  
  Thread.Sleep(100);  
  }  
   
  }  
  }  
  }  
   
  Top

50 楼dk385(大康)回复于 2006-06-18 19:10:03 得分 12

客户端:  
  using   System;  
  using   System.Drawing;  
  using   System.Collections;  
  using   System.ComponentModel;  
  using   System.Windows.Forms;  
  using   System.Net.Sockets;  
  using   System.IO;  
  namespace   Sockets  
  {  
  ///   <summary>  
  ///   frmClient   的摘要说明。  
  ///   </summary>  
  public   class   frmClient   :   System.Windows.Forms.Form  
  {  
  private   System.Windows.Forms.Button   btnConnect;  
  ///   <summary>  
  ///   必需的设计器变量。  
  ///   </summary>  
  private   System.ComponentModel.Container   components   =   null;  
  private   TcpClient   client=null;  
  private   NetworkStream   m_OutStream=null;  
  private   StreamReader   m_InStream=null;  
  private   string   m_Host="192.168.1.25";  
  private   System.Windows.Forms.ListBox   lsbox;  
  private   System.Windows.Forms.ContextMenu   contextMenu1;  
  private   System.Windows.Forms.MenuItem   muClose;  
  private   int   m_Port=15000;  
  private   System.Collections.ArrayList   ClientList;  
  public   frmClient()  
  {  
  //  
  //   Windows   窗体设计器支持所必需的  
  //  
  InitializeComponent();  
   
  ClientList=new   ArrayList();  
  //  
  //   TODO:   在   InitializeComponent   调用后添加任何构造函数代码  
  //  
  }  
   
  ///   <summary>  
  ///   清理所有正在使用的资源。  
  ///   </summary>  
  protected   override   void   Dispose(   bool   disposing   )  
  {  
  if(   disposing   )  
  {  
  if(components   !=   null)  
  {  
  components.Dispose();  
  }  
  }  
  base.Dispose(   disposing   );  
  }  
   
  ///   <summary>  
  ///   应用程序的主入口点。  
  ///   </summary>  
  [STAThread]  
  static   void   Main()    
  {  
  Application.Run(new   frmClient());  
  }  
   
  #region   Windows   窗体设计器生成的代码  
  ///   <summary>  
  ///   设计器支持所需的方法   -   不要使用代码编辑器修改  
  ///   此方法的内容。  
  ///   </summary>  
  private   void   InitializeComponent()  
  {  
  this.btnConnect   =   new   System.Windows.Forms.Button();  
  this.lsbox   =   new   System.Windows.Forms.ListBox();  
  this.contextMenu1   =   new   System.Windows.Forms.ContextMenu();  
  this.muClose   =   new   System.Windows.Forms.MenuItem();  
  this.SuspendLayout();  
  //    
  //   btnConnect  
  //    
  this.btnConnect.Location   =   new   System.Drawing.Point(58,   34);  
  this.btnConnect.Name   =   "btnConnect";  
  this.btnConnect.Size   =   new   System.Drawing.Size(170,   23);  
  this.btnConnect.TabIndex   =   0;  
  this.btnConnect.Text   =   "Connect   To   Server";  
  this.btnConnect.Click   +=   new   System.EventHandler(this.btnConnect_Click);  
  //    
  //   lsbox  
  //    
  this.lsbox.ContextMenu   =   this.contextMenu1;  
  this.lsbox.ItemHeight   =   12;  
  this.lsbox.Location   =   new   System.Drawing.Point(54,   76);  
  this.lsbox.Name   =   "lsbox";  
  this.lsbox.Size   =   new   System.Drawing.Size(250,   148);  
  this.lsbox.TabIndex   =   1;  
  //    
  //   contextMenu1  
  //    
  this.contextMenu1.MenuItems.AddRange(new   System.Windows.Forms.MenuItem[]   {  
    this.muClose});  
  //    
  //   muClose  
  //    
  this.muClose.Index   =   0;  
  this.muClose.Text   =   "Close";  
  this.muClose.Click   +=   new   System.EventHandler(this.muClose_Click);  
  //    
  //   frmClient  
  //    
  this.AutoScaleBaseSize   =   new   System.Drawing.Size(6,   14);  
  this.ClientSize   =   new   System.Drawing.Size(448,   273);  
  this.Controls.Add(this.lsbox);  
  this.Controls.Add(this.btnConnect);  
  this.Name   =   "frmClient";  
  this.Text   =   "frmClient";  
  this.ResumeLayout(false);  
   
  }  
  #endregion  
   
  private   void   btnConnect_Click(object   sender,   System.EventArgs   e)  
  {  
  try  
  {  
  this.client=new   TcpClient(this.m_Host,this.m_Port);  
  this.m_OutStream=this.client.GetStream();  
  this.m_InStream=new   StreamReader(this.m_OutStream);  
  string   result=this.m_InStream.ReadLine();  
  ListViewItem   listItem=new   ListViewItem("Server   Message:"+result);  
  listItem.Tag=client;  
  this.lsbox.Items.Add(listItem);  
     
  }  
  catch(System.Exception   ex)  
  {  
  MessageBox.Show(this,ex.Message);  
  }  
     
  }  
   
  private   void   muClose_Click(object   sender,   System.EventArgs   e)  
  {  
  if(this.lsbox.SelectedIndex!=-1)  
  {  
  try  
  {  
  ListViewItem   listItem=(ListViewItem)this.lsbox.Items[lsbox.SelectedIndex];  
  string   cmd="Close   "+listItem.Text+"/r/n";  
  byte[]   buf=System.Text.Encoding.ASCII.GetBytes(cmd);  
  TcpClient   mClient=(TcpClient)listItem.Tag;  
  this.m_OutStream=mClient.GetStream();  
  this.m_OutStream.Write(buf,0,buf.Length);  
  }  
  catch{}  
  this.lsbox.Items.RemoveAt(lsbox.SelectedIndex);  
  }  
  }  
  }  
  }  
  Top

51 楼zhaojiang(不会飞的鱼)回复于 2006-06-18 19:30:14 得分 0

实际上QQ采用的是UDP协议,而不是TCP协议.  
  UDP是无连接的,   所以任务与服务器的通讯都是断开的,不存在什么连接问题,   这也是为什么你们有时候会看见"刚才的消息XXXX,   没有成功发送...",   这是因为在规定的时间内,发送的UDP数据包没有收到响应信息,登陆的时候,也是通过这种方式,   验证成功后,会返回一个成功的状态信息到客户端,并在服务器记录当前用户在线,   这就是为什么有的人机器宕掉了,没有向服务器发送退出的信息,所以别人看到该用户"在线",   直到服务器在规定的时间内没有收到客户端的响应信息,所以才清除用户状态.此时别的用户才能知道此用户已经下线了.Top

52 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-06-18 19:33:34 得分 0

一个就会说:你去看***吧  
  既然人家楼主来这里发问,就是想让你们这些"大虾"帮忙答个疑,怎么能随便叫人去看***呢.  
  还有那些满口大话的就不要说了,为什么半天下来,你们没讨论出个令人信服的答案?Top

53 楼tianhuo_soft(悬崖边的舞者)回复于 2006-06-18 19:58:22 得分 0

你看看服务器的   80端口   netstat   -n     和N个客户端连接Top

54 楼dk385(大康)回复于 2006-06-18 21:01:42 得分 0

一个就会说:你去看***吧  
  既然人家楼主来这里发问,就是想让你们这些"大虾"帮忙答个疑,怎么能随便叫人去看***呢.  
  还有那些满口大话的就不要说了,为什么半天下来,你们没讨论出个令人信服的答案?  
  =======================================  
  to   femalelover:  
  叫人去先看相关的资料有什么不对?  
  这样至少会对解决问题有帮助吧,大家都可以为解决问题去找资料,凭什么你就不可以??  
  你来这里发问,不就是为了解决问题么?  
  难道你要解决网络问题,大家还叫你去看诗词歌赋不成?  
  至于你把大家对这个问题的看法说成是"满口大话",那么,请问你有什么高论?不防说出来大家学习下。  
  还有,你有提问的权利,大家并没有回答你的义务.所以请不要说“半天下来,你们没讨论出个令人信服的答案"这样话!  
   
  Top

55 楼gccr(古城痴人)回复于 2006-06-18 22:14:43 得分 0

服务器只要监听一个端口就行了.Top

56 楼zhouhongyun(jerry)回复于 2006-06-19 00:55:45 得分 0

QQ说同时在线人数已经突破2000万了  
  ----又不是同时登陆,同时登陆有个十万就差不多了Top

57 楼sandy33()回复于 2006-06-19 07:32:52 得分 0

HAVENT(夜雨流星℡)   (   )   信誉:100    
  ……楼主对通讯了解太少  
   
  服务器只需开1个端口就足够了  
   
  腾讯服务器好像多开了几个端口  
  人家服务器牛,网络宽阔  
   
  最重要的是QQ只负责维护用户列表而已,真正的通讯除了转发的都是P2P连接,除了第一次向服务器获取对方地址外就不会叨扰服务器了(当然,QQ会定期上服务器告知服务器自己还健在,顺便下载好友在线情况)  
   
  zhaojiang(不会飞的鱼)   (   )   信誉:100    
  实际上QQ采用的是UDP协议,而不是TCP协议.  
  UDP是无连接的,   所以任务与服务器的通讯都是断开的,不存在什么连接问题,   这也是为什么你们有时候会看见"刚才的消息XXXX,   没有成功发送...",   这是因为在规定的时间内,发送的UDP数据包没有收到响应信息,登陆的时候,也是通过这种方式,   验证成功后,会返回一个成功的状态信息到客户端,并在服务器记录当前用户在线,   这就是为什么有的人机器宕掉了,没有向服务器发送退出的信息,所以别人看到该用户"在线",   直到服务器在规定的时间内没有收到客户端的响应信息,所以才清除用户状态.此时别的用户才能知道此用户已经下线了.  
   
  我认为此二位讲的有道理,QQ一定不会是实时与服务器相连的,看断线后一会儿QQ才出现下线的提示,可看出。是隔段时间刷新的。这么多人在线腾讯公司的服务器不止1000台吧。  
  Top

58 楼sun_daxigua(大西瓜)回复于 2006-06-19 08:08:09 得分 0

谁说一个端口对付所有的连接不行啊?这么大的通信量,肯定是要做集群的。一个端口的说法是针对一个服务器的。那么多的服务器,还怕承受不了请求啊。腾讯有没有钱就不说了,人家靠这个吃饭的。机群里有负载均衡技术,根本不用担心Top

59 楼yahuadai()回复于 2006-06-19 09:46:51 得分 0

QQ只是登录时候才与服务器作一次请求,其它情况下都是P2P的了  
  Top

60 楼LIJUNCN()回复于 2006-06-19 09:57:14 得分 0

结果是什么样的呀!   还是没搞清楚。。。  
   
  我在继续学习中。。。Top

61 楼ggy56(fuckmoney)回复于 2006-06-19 11:05:27 得分 0

什么乱七八糟的?!Top

62 楼ggy56(fuckmoney)回复于 2006-06-19 11:06:12 得分 0

盲人说象!  
  自己把自己玩了还不知道Top

63 楼benjiam(今晚打老虎)回复于 2006-06-19 13:06:40 得分 0

一般是用AJAX来实现的     这样的红星   真是值得瞻仰啊  
   
   
  ....Top

64 楼xinggg(风之渡)回复于 2006-06-19 13:18:41 得分 0

呵呵,最近流行ajax嘛...  
   
  进来之前还以为在讨论负载平衡实现方法呢..Top

65 楼cxz7531(大花猫)回复于 2006-06-19 13:37:13 得分 0

偶用delphi做过聊天程序,很简单的。  
   
  服务器只开一个监听端口就行了。  
  1   客户端登陆的时候连接该端口,注册登陆信息,登陆成功,服务器将其加入在线用户列表。  
      同时检测该用户有哪些在线好友,把他们的名单传给客户端,客户端将这些人显示在线状  
  态。同时把该用户的ID,发给他的在线好友们的客户端,让他们将该用户显示成在线,  
  2   该用户下线时候,向服务器发出一个注销信息,服务器将其从在线用户中删除,同时通知所有的在线好友客户端,更新状态。  
   
  每隔几分钟,循环检测所有在线用户的客户端是否能连通,不通则判断为掉线,从在线用户中删除。Top

66 楼cxz7531(大花猫)回复于 2006-06-19 13:41:09 得分 0

至于两个客户端之间聊天,是直接通信,无需服务器中转。每个客户端都包含一个Socket   server,和两个Socket   ClientTop

67 楼liangxf0022(小新)回复于 2006-06-19 13:49:20 得分 0

 
  ……楼主对通讯了解太少  
   
  服务器只需开1个端口就足够了  
   
  腾讯服务器好像多开了几个端口  
  人家服务器牛,网络宽阔  
  --------------------------  
  好像这个并不是最好的方案哦,1个端口的容量造就被撑爆了。如果这样的话,Web服务器也不用集群了,一个服务器就OK了Top

68 楼liangxf0022(小新)回复于 2006-06-19 13:54:27 得分 0

未处理的“System.Net.Sockets.SocketException”类型的异常出现在   system.dll   中。  
   
  其他信息:   通常每个套接字地址   (协议/网络地址/端口)  
  只允许使用一次。  
   
  ==========  
   
  你不可以重复绑定端口,比如别人已经绑定了端口了。Top

69 楼liangxf0022(小新)回复于 2006-06-19 13:54:52 得分 0

最好绑定之前用netstat   -a看看你现在所有已经绑定得端口Top

70 楼asonglaw(AsongLaw)回复于 2006-06-19 14:19:19 得分 1

暈倒,沒有常常聽說MS   SQL的端口是1433,HTTP的端口是80嗎;要是一個客戶端一個端口,那客戶端怎麼去找服務器啊?服務器的端口是固定的,客戶端是動態的。Top

71 楼huzhangyou(信仰(http://www.libing.net.cn))回复于 2006-06-19 14:29:30 得分 6

我的blog有一片文章  
  类似于QQ游戏百万人同时在线的服务器架构实现!    
  http://221.8.9.2/youyou/boblog/read.php?41  
   
  还有大型服务器设计的代码  
  一般windows采用IOCP   +   ThreadPool  
  linux采用异步   +   non-Blocking   +   ThreadPool实现  
  当然按照socket的规定  
  最大只能支持2^32并发  
  也就是6w多一点  
  然而实际根本就不可能的  
  一般QQ是大概5000并发但台PC的实现,他们使用服务器集群  
  对于一个QQ游戏100w量,起码需要200台机器的集群Top

72 楼yu308()回复于 2006-06-19 15:06:16 得分 0

markTop

73 楼xinggg(风之渡)回复于 2006-06-19 15:10:31 得分 0

服务端一个端口和多个端口其实还不是一样,如果一个端口要挤爆,多个端口一样要挤爆吧,都是同一个IP的,从网络层而言,都是到同一地址的Top

74 楼benjiam(今晚打老虎)回复于 2006-06-20 09:07:02 得分 0

一般QQ是大概5000并发但台PC的实现  
   
  tcp   都有1w     何况udp  
   
   
  我得到的资料是udp   单机10w     tcp   1wTop

75 楼cppkiller(实事求是)回复于 2006-06-20 11:46:13 得分 0

谢谢大康的代码回复。  
  也谢谢其它网友的回复。起始设分太少了,怎么加分呢?Top

76 楼wq_quake(Paladin 象风一样的游侠)回复于 2006-06-22 20:52:19 得分 1

不好意思,建议楼主看看windows网络游戏编程这本书,上面写了一些理论的东西。应该先从理论做起,我做过私服的服务器端编程,用的是Indy,indy是开发源码的,更好一些的还有dxsock尽管都是pascal代码,不过语法么,和c没有什么太大的差别。先读代码再从简单的例子做起,必须得动手作一下才能明白!!!Top

77 楼cppkiller(实事求是)回复于 2006-06-24 23:47:14 得分 0

暂且结贴把分给了。起始设定分数太少,研究了半天不知道怎么加分,有好些朋友都该得分的。。。Top

78 楼wc_king()回复于 2006-11-05 23:53:02 得分 0

感谢大康的代码回复!  
  对于计算机集群这个概念理解不深,请问各位有什么介绍这个概念的资料?  
  谢谢!  
  受益了!  
  再次拜谢!

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页