前段时间公司要求做聊天室,在网上看到那篇经典的基于推技术的聊天室。于是开始动手。先说下我的思路,再说我碰到的一些问题,希望得到大虾们的帮助。
我的思路:
客户端就不说了,只有浏览器,不过要支持http1.1协议。
服务器端:
1.数据库:因为这个聊天室跟我们的一个网站以及论坛有关系,所以我用到了数据库,其实数据库也非常简单,就只有一个聊天室表以及一个聊天室类型表,会员表我直接拿论坛的表来用。
2.主要的类:对于这个聊天室,我构造如下几个主要的类,聊天室类(ChatRoom),聊天者类(Chater),发言内容类(Content),监听线程类(ListenThread,主要监听各个聊天室的端口),socket解析线程类(SocketParseThread),发送线程类(SendThread),socket列表类(SocketList,通过chatroom构造),发言列表类(ContentList,通过chatroom构造)。
3.流程,系统首先从数据库得到聊天室信息,构造聊天室类,并且放入聊天室列表。在构造聊天室的时候,启动ListenThread线程;然后当有用户进入的时候,得到一个socket,启动SocketParseThread线程,这是一个主要的聊天控制类,在这个线程里面,解析用户的请求,比方说可能是登陆,发送聊天内容,离开,屏蔽某个人等等。举个例子,假如用户登录聊天室,此时会构造Chater类并放入ChaterList中,同时返回一个框架页面给客户端,并且关闭socket,比方说返回<frame name="**",src="/pubjoin?roomid=**&chaterid=**"..>,假如我们的框架有四个页面(显示公共聊天内容,个人聊天内容,在线列表,发言区),浏览器收到这个框架页面,同时会有四个socket进来,此时初始这四个页面,其中前三个socket会一直连接,保存在SocketList中,SocketParseThread在运行的时候,根据用户的不同命令会构造Content类并放入ContentList中,这是会启动SendThread线程。SendThread主要从ContentList中取出发言内容,并且遍历SocketList,把发言内容发给Socket。下面说说我遇到的问题
1,通过上面的流程,我们可以看到系统同时可能有很多线程在运行,也会有很多socket连接被保持,服务器能承受吗?
2,我如何判断保存在SocketList中的socket是否仍然保持连接,例如用户关闭浏览器,我怎么得到这个消息?
3,在程序中每一个socket进来都会有很多判断,如果碰到异常我该如何处理,我打算继承一个Exception类,定义自己的错误类型,目前还没想好。
4,关于客户端的问题,大家可以看到,我在服务器端没有做任何页面,客户所得到的页面都是通过socket直接写的,这就为客户端的一些操作带来了困难,例如客户端如何分屏?如何刷新在线列表?如何刷新聊天内容?整个聊天室服务程序,完全可以不用web服务器来发布。我曾经想过直接在服务器端监听80端口,然后启一个线程生成聊天室首页,但是考虑到服务器可能还需要发布别的网站,所以聊天室首页以及管理端(主要是管理聊天室的一些属性,跟数据库相关,对聊天没有影响)我还是通过web服务器发布。这些都是次要的,上面列出的问题,恳请大虾们给出解答!
我的思路:
客户端就不说了,只有浏览器,不过要支持http1.1协议。
服务器端:
1.数据库:因为这个聊天室跟我们的一个网站以及论坛有关系,所以我用到了数据库,其实数据库也非常简单,就只有一个聊天室表以及一个聊天室类型表,会员表我直接拿论坛的表来用。
2.主要的类:对于这个聊天室,我构造如下几个主要的类,聊天室类(ChatRoom),聊天者类(Chater),发言内容类(Content),监听线程类(ListenThread,主要监听各个聊天室的端口),socket解析线程类(SocketParseThread),发送线程类(SendThread),socket列表类(SocketList,通过chatroom构造),发言列表类(ContentList,通过chatroom构造)。
3.流程,系统首先从数据库得到聊天室信息,构造聊天室类,并且放入聊天室列表。在构造聊天室的时候,启动ListenThread线程;然后当有用户进入的时候,得到一个socket,启动SocketParseThread线程,这是一个主要的聊天控制类,在这个线程里面,解析用户的请求,比方说可能是登陆,发送聊天内容,离开,屏蔽某个人等等。举个例子,假如用户登录聊天室,此时会构造Chater类并放入ChaterList中,同时返回一个框架页面给客户端,并且关闭socket,比方说返回<frame name="**",src="/pubjoin?roomid=**&chaterid=**"..>,假如我们的框架有四个页面(显示公共聊天内容,个人聊天内容,在线列表,发言区),浏览器收到这个框架页面,同时会有四个socket进来,此时初始这四个页面,其中前三个socket会一直连接,保存在SocketList中,SocketParseThread在运行的时候,根据用户的不同命令会构造Content类并放入ContentList中,这是会启动SendThread线程。SendThread主要从ContentList中取出发言内容,并且遍历SocketList,把发言内容发给Socket。下面说说我遇到的问题
1,通过上面的流程,我们可以看到系统同时可能有很多线程在运行,也会有很多socket连接被保持,服务器能承受吗?
2,我如何判断保存在SocketList中的socket是否仍然保持连接,例如用户关闭浏览器,我怎么得到这个消息?
3,在程序中每一个socket进来都会有很多判断,如果碰到异常我该如何处理,我打算继承一个Exception类,定义自己的错误类型,目前还没想好。
4,关于客户端的问题,大家可以看到,我在服务器端没有做任何页面,客户所得到的页面都是通过socket直接写的,这就为客户端的一些操作带来了困难,例如客户端如何分屏?如何刷新在线列表?如何刷新聊天内容?整个聊天室服务程序,完全可以不用web服务器来发布。我曾经想过直接在服务器端监听80端口,然后启一个线程生成聊天室首页,但是考虑到服务器可能还需要发布别的网站,所以聊天室首页以及管理端(主要是管理聊天室的一些属性,跟数据库相关,对聊天没有影响)我还是通过web服务器发布。这些都是次要的,上面列出的问题,恳请大虾们给出解答!
star_str:在监听线程里面,如果有socket进来,我会启动SocketParseThread线程,尽管监听线程是一直在运行,但是对SocketParseThread没有影响啊
"我也碰到过你说的socket不关闭,浏览器不能显示页面的问题,不过我已经解决了"你是怎么解决的?还有这个问题的原因是什么?
谢谢。
2, 严格讲、浏览器关闭事件无法准确得到。
4, 例如客户端如何分屏?如何刷新在线列表?如何刷新聊天内容?——你做的是BS聊天室、必然需要大量javascript来操控客户端页面的!我作过CS聊天室、不过是很久以前了、
在网上找一个聊天室DOWN它的javascript应该可以找到如何让客户端浏览器自动定时刷新(如2秒刷新一次在线列表)。另外BS结构决定了BS聊天室的稳定性比较差、也比较脆弱。
对于客户端的操作,大部分我都解决了。javascript代码很少很少,一个比较困难的问题是在线列表的刷新。如果让页面定时刷新来获取在线列表,的确是很简单,但是定时刷新屏幕会闪烁,我不想那样做。对于用户能够操作的,我希望是由用户来触发,比方说用户刷屏,分屏,这些动作是随意的,用户可选可不选,
但是对于在线列表,尽管用户也可以刷新,但是他不知道何时该刷新,如果定时刷新,在线列表在某时总是会有偏差的,而在线用户的变化,服务器总是可以马上得到,所以这项工作我想交给服务器来做。我的问题是服务器如何触发用户的在线列表更新。
对于我的这个程序来说,用户的在线列表页面跟服务器总是保持了socket连接,尽管是这样,由于客户端仅仅是浏览器,所以在线列表那个页面仅仅是在跟服务器建立socket连接的时候请求了一次服务器,以后的工作都是服务器给它写数据。服务器可以给页面写javascript,我想知道的是有没有javascript代码不需要触发,也就是说,现在,服务器知道了在线列表多了或者少了人,他给每个用户的在线列表页面发一段javascript代码,这段代码实现页面的刷新,这样就实现了在线列表的更新。
不知道有没有这样的代码?
至于你说的公共信息,私聊信息放的地方,我的程序没有session。对于每个聊天室,我都有一个唯一的ContentList实例根他对应,所有的聊天信息都是放在这里面。ContentList里面存放的是Content,Content的属性包括(fromchater--Chater对象,tochater--Chater对象,chattime-String,issecret-boolean,contents-String),这个类已经能够完全说明这个聊天信息该发给谁。所以只需要一个发送线程就够了,无所谓把聊天信息分开放。