最近项目上面需要用到聊天室,在Ajax轮询和WebSocket之间考虑了下,决定还是采用WebSocket来实现这个项目。采用WebSocket实现,那么就必须进行服务器的技术选型,主要考虑的有Java(Netty/Jetty)、node.js(socket.io)、PHP(swoole/workerman)。
但是PHP语言的服务器就全部放弃了,论速度估计是比不上前面两者(没测试,但有人好像测试过,不过PHP的一般情况下貌似的确速度方面不如其他语言,而且workerman貌似不太快,swoole安装还挺麻烦的,相对来讲),最关键的是我感觉PHP的一个非常重要的就是可以在虚拟主机中运行,但是PHP的WebSocket基本上必须得虚拟服务器,那我要他何用!!!
在Java和node.js中进行选择的主要原因是:服务器中已经配好Java环境了,就懒得搞node.js了。
Netty和Jetty对比,好像是Netty更好一些,我从网上看到的,说错了,别打我!!!
好了,那就Netty了,顺便还可以学点新的玩意。以下内容其实大部分来自慕课网的netty websocket课程,主讲人是济癫,其中有个地方好像有BUG,随手改了,另外Netty5被废了,于是我用的包其实是Netty4的。
PS:网络上绝大部分内容应该都是来自慕课网的课程或者是《Netty权威指南》,其实慕课网的这些个应该是来自《Netty权威指南》。
其实没有什么好说的,直接上代码吧!
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
public class NettyConfig {
/*
* 存储每个客户端接入的配置量
*/
public static ChannelGroup cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
这个没有任何改动,都是慕课网原装的!
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
// 接收/处理/响应客户端WebSocket请求的核心业务处理类
public class MwWebSocketHandler extends SimpleChannelInboundHandler
把返回消息时会增加一个时间的地方去掉了,另外Nettt4中的处理 “服务端处理客户端WebSocket请求的核心方法”是channelRead0,不再是 messageReceived了,有几个函数被标记为deprecation了,以上这些就是使用netty4的差异吧。另外有一个BUG是:如果客户端断开请求时,服务器会弹出上面的那个自己定义的二进制异常,包括刷新、浏览器关闭和websocket主动关闭。其实原因在于在判断frame是否为CloseWebSocketFrame后,如果是的话,除了执行代码块后的代码,还会继续往下执行,然后是判断PingWebSocketFrame,再然后判断是否是TextWebScoketFrame,如果不是的话,就是二进制代码,但是上面说了这个frame其实是CloseWebSocketFrame,所以自然会抛出异常。那么解决方案其实很简单,在判断是否为CloseWebSocketFrame时,如果是的话执行完close后,那就return出去!
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
// 初始化连接时的各个组件
public class MwWebSocketChannelHandler extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
// TODO Auto-generated method stub
arg0.pipeline().addLast("http-codec", new HttpServerCodec());
arg0.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
arg0.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
arg0.pipeline().addLast("handler", new MwWebSocketHandler());
}
}
这个没啥说的,继续!
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
// 程序的入口,负责启动应用
public class Main {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new MwWebSocketChannelHandler());
System.out.println("服务端开启,等待客户端连接...");
Channel ch = b.bind(8888).sync().channel();
ch.closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
// TODO: handle finally clause
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
同上
客户端代码我增加了一个关闭按钮,其实关闭、刷新浏览器也会自动关闭websocket的。
WebSocket客户端
OK啦,对于一个简单的聊天室是可以实现基本的功能了,不过这玩意如果上交给领导,那我就该另投简历了,所以之后还会有一些别的东西加入,例如:
1.用户的识别!
2.用户的分组,不可能所有的用户都在一起聊吧!!
3.心跳机制,就算没有什么意外,难免会被防火墙/网关去中断!!
4.前端的美化,现在都9102年了,还用这个界面!!!
5.以及其他功能的实现!
之后有空的时候还会更新的!!!
注意:是有空的时候!!!!!