2015年06月11日

来源:互联网 发布:linux 创建文件系统 编辑:程序博客网 时间:2024/05/16 00:45
WebSocket 协议开发
WebSocket将网络套接字引入到了客户端和服务端,浏览器是服务器之间可以通过套接字建立持久的连接,双方随时都可以互发数据给对方,而不是之前由客户端控制的已请求一应答模式。
1、WebSocket的特点:
a)、单一的TCP连接,采用全双工模式通信;
b)、对代理,防火墙和路由器透明;
c)、无头部信息,Cookie和省份验证;
d)、无安全开销;
e)、通过“ping、pong”帧保持链路激活
f)、服务器可以主动传递消息给客户端。
2、WebSocket建立连接
2015年06月11日 - 小宝9192 - 小宝9192的博客
在建立WebSocket连接时,需要通过客户端发出握手请求,请求消息示例图如下:
2015年06月11日 - 小宝9192 - 小宝9192的博客
为了建立WebSocket连接,客户端浏览器首先要向服务器发出一个HTTP请求,这个请求和通常的HTTP请求不同,包含一些附加头信息,其中附加头信息“Upgrade:WebSocket”表明这是一个申请协议升级的HTTP请求,服务器端解析这些附加头信息,然后生成应答信息返回给客户端,客户端和服务端的WebSocket连接就建立起来了,双发都可以通过这个连接通道给对方发送信息,知道服务的一段被关闭。
服务端返回给客户端的应答信息:
2015年06月11日 - 小宝9192 - 小宝9192的博客
 
 
3、WebSocket生命周期
握手成功后,服务端和客服端就可以通过“messages”的方式进行通信了,一个消息由一个或者多个帧组成,帧都有自己的类型,属于同一个消息的多个帧具有相同类型的数据,广义上讲,数据类型可以是文本数据(UTF-8[RFC3629]文字)、二进制数据和控制帧(协议级信令,如信号)。
2015年06月11日 - 小宝9192 - 小宝9192的博客
  
WebSocket服务开发:
public class WebSocketServer {
    public void run(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
   ServerBootstrap b = new ServerBootstrap();
   b.group(bossGroup, workerGroup)
   .channel(NioServerSocketChannel.class)
   .childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch)
throws Exception {
   ChannelPipeline pipeline = ch.pipeline();
   //将请求和应答消息编码和或者解码为HTTP消息
   pipeline.addLast("http-codec",new HttpServerCodec());
   //将HTTP消息的多个部分组合成一条完整的HTTP消息
   pipeline.addLast("aggregator",new HttpObjectAggregator(65536));
   //来向客户端发送HTML5文件,他主要用于支持浏览器和服务器进行WebSocket通信
   pipeline.addLast("http-chunked",new ChunkedWriteHandler());
   //增加WebSocket服务端handler
   pipeline.addLast("handler",new WebSocketServerHandler());
}
   });

   Channel ch = b.bind(port).sync().channel();
   System.out.println("Web socket server started at port " + port + '.');
   System.out.println("Open your browser and navigate to http://localhost:" + port + '/');

   ch.closeFuture().sync();
} finally {
   bossGroup.shutdownGracefully();
   workerGroup.shutdownGracefully();
}
    }

    public static void main(String[] args) throws Exception {
int port = 8080;
if (args.length > 0) {
   try {
port = Integer.parseInt(args[0]);
   } catch (NumberFormatException e) {
e.printStackTrace();
   }
}
new WebSocketServer().run(port);
    }
}

//handler
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger logger = Logger
.getLogger(WebSocketServerHandler.class.getName());

//创建握手处理类WebSocketServerHandshaker
private WebSocketServerHandshaker handshaker;

@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("messageReceived");
// 传统的HTTP接入
//虽然是websocket,但在建立websocket连接前,先进行http握手,所以,这时也要处理http请求
        //在http握手完成后,才是websocket下的通信
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
}
// WebSocket接入
else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
}
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelReadComplete---"+ctx.channel().id()+"-------"+ctx.handler());
ctx.flush();
}

private void handleHttpRequest(ChannelHandlerContext ctx,
FullHttpRequest req) throws Exception {
//System.out.println(req);
System.out.println("handleHttpRequest");
// 如果HTTP解码失败,返回HHTP异常
if (!req.decoderResult().isSuccess()
|| (!"websocket".equals(req.headers().get("Upgrade")))) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
BAD_REQUEST));
return;
}

// 构造握手响应返回,本机测试
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
"ws://localhost:8080/websocket", null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx
.channel());
} else {
//向客户端发送websocket握手,完成握手
            //客户端收到的状态是101 sitching protocol
handshaker.handshake(ctx.channel(), req);
}
}
//websocket通信
private void handleWebSocketFrame(ChannelHandlerContext ctx,
WebSocketFrame frame) {
System.out.println("handleWebSocketFrame");
// 判断是否是关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(),(CloseWebSocketFrame) frame.retain());
return;
}
// 判断是否是Ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
// 本例程仅支持文本消息,不支持二进制消息
if (!(frame instanceof TextWebSocketFrame)) {
throw new UnsupportedOperationException(String.format(
"%s frame types not supported", frame.getClass().getName()));
}
//这个地方 可以根据需求,加上一些业务逻辑,业务逻辑就只在这里添加的
// 返回应答消息
String request = ((TextWebSocketFrame) frame).text();
if (logger.isLoggable(Level.FINE)) {
logger.fine(String.format("%s received %s", ctx.channel(), request));
}
ctx.channel().write(
new TextWebSocketFrame(request
+ " , 欢迎使用Netty WebSocket服务,这是我的第一个推送,现在时刻:"
+ new java.util.Date().toString()));
}

private static void sendHttpResponse(ChannelHandlerContext ctx,
FullHttpRequest req, FullHttpResponse res) {
System.out.println("sendHttpResponse");
// 返回应答给客户端
if (res.status().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(),
CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
setContentLength(res, res.content().readableBytes());
}

// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.out.println("exceptionCaught");
cause.printStackTrace();
ctx.close();
}
}


WebSocket客户端开发:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
Netty WebSocket 时间服务器
</head>
<br>
<body>
<br>
<script type="text/javascript">
var socket;
if (!window.WebSocket) 
{
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/websocket");
socket.onmessage = function(event) {
//console.log(event);
var ta = document.getElementById('responseText');
ta.value="";
ta.value = event.origin+"-----------------"+event.data
};
socket.onopen = function(event) {
var ta = document.getElementById('responseText');
ta.value = "打开WebSocket服务正常,浏览器支持WebSocket!";
};
socket.onclose = function(event) {
var ta = document.getElementById('responseText');
ta.value = "";
ta.value = "WebSocket 关闭!"; 
};
}
else
{
alert("抱歉,您的浏览器不支持WebSocket协议!");
}

function send(message) {
if (!window.WebSocket) { return; }
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
}
else
{
 alert("WebSocket连接没有建立成功!");
}
}
</script>
<form onsubmit="return false;">
<input type="text" name="message" value="Netty最佳实践"/>
<br><br>
<input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/>
<input type="button" value="发送WebSocket请求消息" onclick="send('我是小帅')"/>
<hr color="blue"/>
<h3>服务端返回的应答消息</h3>
<textarea id="responseText" style="width:500px;height:300px;"></textarea>
</form>
</body>
</html>

在客户端运行的结果如下:
2015年06月11日 - 小宝9192 - 小宝9192的博客
这个就代表运行成功。
 

0 0
原创粉丝点击