手把手教你实现一个网页聊天室

现在写文章都喜欢“手把手”,有一种老爸抱着三岁的儿子在路边撒尿的感觉,所以这篇的题目我也“手把手”一回,费话不说,开始撒尿:

使用到的技术

  • websocket
  • springmvc
  • maven
  • fastjson

这里使用到的主要技术是websocket,后端服务用java来写,其中springmvcmaven只是用于写java时的框架和构建工具,fastjson用于json数据格式的转换。

什么是websocket?

websocket是html5提供的一种在一个(TCP)接口进行全双工通信的技术,所谓全双工通信,简单点说就是通信的双方都可以同时向对方发送数据,类似于打电话时两方可以同时说话。

在没有websocket之前,实现聊天室这种实时的消息响应需求,一般会用长连接轮询的方式,在特定的的时间间隔(如每1秒),由客户端对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端,这种方式有很明显的缺点,客户端需要不断的向服务器发出请求,然而HTTP请求的header是非常长的,而我们需要的有用数据可能只是一个很小的值,这样会占用很多的带宽。而用websocket,浏览器和服务器只需要做一个握手的动作,两者之间就形成了一条快速通道,直接就可以互相传送数据了。

支持websocket的浏览器和服务器

websocket是html5的特性,浏览器至少要支持html5,服务器我这边用tomcat7.0.54(其它较高版本我这边不起作用!?),另外java版本要7以上:

浏览器支持 版本
Chrome 4+
Firefox 4+
Internet Explorer 10+
Opera 10+
Safari 5+
服务器支持 版本
tomcat 7.0.27+
jetty 7.0.1+
Nginx 1.3.13+
resin 4+

开始代码

websocket浏览器实现

浏览器通过javascript向服务器发起websocket请求,获取websocket连接后,浏览器和服务器就可以通过TCP连接直接交换数据了。

首先需要创建一个websocket对象:

var socket = new WebSocket('ws://localhost:8080/chatsocket');

这里的参数指定后台websocket服务的连接地址,不同于http://,websocket请求地址以ws://开头,接下来是主机的地址+端口号+后台服务名,这里的chatsocket是后面java后端所起的服务名字。其实第一次的握手连接,套接字还是以HTTP连接开始的,在HTTP握手之后才升级为TCP套接字,任意一端就都可以发送数据了。如果页面用得是JSP模板,地址和端口可以用JSP的pageContext获取,而不要直接写死:

  • 获取地址:${pageContext.request.serverName}
  • 获取端口:${pageContext.request.serverPort}
  • 获取上下文路径:${pageContext.request.contextPath}

websocket对象的方法

创建websocket对象后,它有一些相关属性、事件和方法,具体可以查看W3C的websocket API

比较重要的事件和方法主要有以下几个:

//连接成功建立的回调方法
socket.onopen = function (event) { console.log("连接服务器成功!"); };
//点击发送消息的方法,这里以json格式传送数据
socket.send(JSON.stringify({
    content: "这里是消息的内容"
    name:"发送人"
}));
//接收到消息的回调方法,一般将返回的数据append到消息页面
socket.onmessage = function (event) { 
    var message = JSON.parse(event.data);
    //dosomething...
    //发送人:message.name ,发送时间: message.date,发送内容:message.content
};

websocket服务器实现

第一步:加入信赖

java要使用websocket要加入相关信赖:

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>

注意:

  • tomcat的lib目录下有一个包websocket-api.jar,用这个包就可以了,所以部署的时候将上述的javax.websocket-api.jar删除再重新启动,否则两则重复,启动会报错。
  • spring-websocket-4.0.1.RELEASE.jar这个包的作用只是需要在websocket后端注入service所用的,否则删除这个包不影响websocket服务运行。
  • 具体spring对websocket的支持见这里

第二步:开启后端服务

新建一个java类,在这个类上加上ServerEndpoint注解,它就变成websocket服务了:

@ServerEndpoint(value = "/chatsocket",configurator = SpringConfigurator.class)
public class HudongEndpoint{

}

这里定义了两个参数:

  • value:服务的名字,前面js创建websocket时的url的服务名就是这个
  • configurator:如果要用spring注入servie,就要加上这个参数

第三步:接收消息的方法

和html5的websocket一样,后端也有相关操作方法,加上相关的注解就可以,比如:

  • @OnOpen:连接建立成功时调用的方法
  • @OnMessage:收到客户端消息后调用的方法
  • @OnClose:连接关闭调用的方法
  • @OnError:发生错误时调用的方法

这里主要说明下@OnMessage方法:

@OnMessage
public void getMessage(String message, Session session) {
    JSONObject jsonObject= JSONObject.parseObject(message);

    jsonObject.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    for (Session openSession : session.getOpenSessions()) {
        jsonObject.put("isSelf", openSession.equals(session));
        openSession.getAsyncRemote().sendText(jsonObject.toString());
        //openSession.getBasicRemote().sendText(message);
    }
}

这个方法有两个参数:

  • message:前端发送过来的消息,json格式
  • session:这个参数可选,为与某个客户端的连接会话,需要通过它给客户端发送数据。如果要将聊天数据保存到数据库中,可以在这个方法中执行相关操作

总结

整个websocket发送消息和接收消息的过程,和http请求在使用上其实没多大区别,后台的一个ServerEndpoint类似于 springmvc的一个controller,相关的业务操作在对应的方法里就是了。这里只是简单的文本聊天,如果要加上表情和图片,可以使用富文本编辑器(Ueditor,kindeditor等),当然还可以加上计算实时在线人数,上线下线通知等功能。