WebSocket
WebSocket協議是基于TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工通信————允許服務器主動發送信息給客戶端。
WebSocket解決了那些問題
1,http 是無狀態,基于請求/響應模型,導致了不識別用戶,每次請求都是一個新的用戶,
這就產生了cookie,session 記錄用戶的狀態的技術。2,http1.1 進行網絡請求可以重用既有的連接, WebSocket只需進行連接一次。
3,向客戶端推送數據 http輪詢:http的輪詢實現是客戶端每個一段時間就進行網絡請求,
如果服務器端沒有數據返回也就表示這次請求是無效的,多次這樣的網絡請求會影響網絡帶寬,WebSocket是基于tcp全雙工通信,只要連接通道建立,服務器可以向客戶端推送數據。4,http 請求包括header 和 要返回的結果返回給客戶端,往往header大于內容的容量,
websoket 瀏覽器和服務器端會建立長連接,雙方式對等的,只要發送數據本省,不在需要任何header的信息 。
netty 服務器端
- 1,MyServer.java
public class MyServer {
public static void main(String ...arg) throws Exception{
// 負責接收客戶端連接
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
// 負責處理連接
NioEventLoopGroup wokerGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,wokerGroup)
.handler(new LoggingHandler(LogLevel.INFO))
.channel(NioServerSocketChannel.class)
.childHandler(new WebSocketChannelInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
wokerGroup.shutdownGracefully();
}
}
}
- 2,WebSocketChannelInitializer.java
public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(8192));
//websocket 請求路徑
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(new TextWebSocketFrameHandler());
}
}
- 3,TextWebSocketFrameHandler.java
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
System.out.println("服務器收到:"+msg.text());
ctx.channel().writeAndFlush(new TextWebSocketFrame("服務器時間:"+ LocalDateTime.now()));
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//每個channel 都有一個唯一的id
System.out.println("handlerAdded: "+ctx.channel().id().asLongText());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerRemoved: "+ctx.channel().id().asLongText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
WebSocket js 使用
- 1,創建對象
var ws = new WebSocket(url,name);
- 2,發送文本消息
ws.send(msg);
- 3,接收消息
ws.onmessage = (function(){...})();
- 4,錯誤處理
ws.onerror = (function(){...})();
- 5,關閉連接
ws.close();
客戶端端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websoket 客戶端</title>
</head>
<body>
<form onsubmit="return false">
<textarea name="message" style="width:400px;height: 200px"></textarea>
<input type="button" value="發送數據" onclick="send(this.form.message.value)">
<h3>服務器輸出:</h3>
<textarea id="responseText" style="width: 400px; height: 300px"></textarea>
<input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空內容">
</form>
</body>
<script type="text/javascript">
var socket;
if (window.WebSocket) {
socket = new WebSocket("ws://127.0.0.1:9999/ws")
var ta = document.getElementById("responseText");
socket.onmessage = function (event) {
ta.value = ta.value + "\n" + event.data;
}
socket.onopen = function (event) {
ta.value = "連接開啟"
}
socket.onclose = function (event) {
ta.value = ta.value + "\n" + "連接關閉";
}
} else {
alert("瀏覽器不支持websocket")
}
function send(message) {
if(!window.WebSocket) {
return;
}
if(socket.readyState == WebSocket.OPEN){
socket.send(message)
}else{
alert("連接尚未成功")
}
}
</script>
</html>