日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

websocket 代理tcp_netty实现websocket请求实战

發布時間:2025/3/19 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 websocket 代理tcp_netty实现websocket请求实战 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

描述

WebSocket是html5開始瀏覽器和服務端進行全雙工通信的網絡技術。在該協議下,與服務端只需要一次握手,之后建立一條快速通道,開始互相傳輸數據,實際是基于TCP雙向全雙工,比http半雙工提高了很大的性能,常用于網絡在線聊天室等。繼續netty實例的學習,本期內容主要做WebSocket實例的實現和相關協議的驗證。

WebSocket與http比較

httpWebSocket
半雙工,可以雙向傳輸,不能同時傳輸全雙工
消息冗長繁瑣,消息頭,消息體,換行...對代理、防火墻、路由器透明
http輪詢實現推送請求量大,而comet采用長連接無頭部、Cookie等
-ping/pong幀保持鏈路激活
-特點:服務端可以主動傳遞給客戶端,不需要輪詢

代碼示例和運行結果

服務端

服務端比較簡單,是啟動一個端口來處理請求。

主程序

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.junit.Test;

public class NettyWebSocketServer {

public void run(final int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(65535))
.addLast(new ChunkedWriteHandler())
.addLast(new WebSocketServerHandler());
}
});
Channel ch = bootstrap.bind(port).sync().channel();
System.out.println("websocket @" + port);
ch.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void runServer() {
run(23123);
}
}

請求處理handler

核心處理WebSocket請求,重點地方加了注釋

import io.netty.buffer.ByteBufUtil;
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.*;
import io.netty.handler.codec.http.websocketx.*;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
private WebSocketServerHandshaker handshaker;

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
//首次請求之后先進行握手,通過http請求來實現
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
handleWebSocketRequest(ctx, (WebSocketFrame) msg);
}
}

private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
//解碼http失敗返回
if (!request.decoderResult().isSuccess()) {
sendResponse(ctx, request, new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.BAD_REQUEST, ctx.alloc().buffer()));
return;
}

if (!HttpMethod.GET.equals(request.method())) {
sendResponse(ctx, request, new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.FORBIDDEN, ctx.alloc().buffer()));
return;
}

//參數分別是ws地址,子協議,是否擴展,最大frame長度
WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(getWebSocketLocation(request), null, true, 5 * 1024 * 1024);
handshaker = factory.newHandshaker(request);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), request);
}
}

//SSL支持采用wss://
private String getWebSocketLocation(FullHttpRequest request) {
String location = request.headers().get(HttpHeaderNames.HOST) + "/websocket";
return "ws://" + location;
}

private void handleWebSocketRequest(ChannelHandlerContext ctx, WebSocketFrame frame) {
//關閉
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
return;
}

//握手 PING/PONG
if (frame instanceof PingWebSocketFrame) {
ctx.write(new PongWebSocketFrame(frame.content().retain()));
return;
}

//文本接收和發送
if (frame instanceof TextWebSocketFrame) {
String recv = ((TextWebSocketFrame) frame).text();
String text = "now@" + LocalDateTime.now() + "\n recv:" + recv;

//這里加了個循環,每隔一秒服務端主動發送內容給客戶端
for (int i = 0; i < 5; i++) {
String res = i + "-" + text;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ctx.writeAndFlush(new TextWebSocketFrame(res));
}
System.out.println(recv);
return;
}

if (frame instanceof BinaryWebSocketFrame) {
ctx.write(frame.retain());
}
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}

private void sendResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse resp) {
HttpResponseStatus status = resp.status();
if (status != HttpResponseStatus.OK) {
ByteBufUtil.writeUtf8(resp.content(), status.toString());
HttpUtil.setContentLength(req, resp.content().readableBytes());
}
boolean keepAlive = HttpUtil.isKeepAlive(req) && status == HttpResponseStatus.OK;
HttpUtil.setKeepAlive(req, keepAlive);
ChannelFuture future = ctx.write(resp);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}

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

客戶端

客戶端使用了html來實現,可以直接在瀏覽器打開,代碼比較簡單,不做多的介紹。本地使用注意ws地址和服務端的一致。

<html>
<head>
<meta charset="UTF-8">
head>
<body>
<form onsubmit="return false;">
<input type="text" name="message" value="test"/>
<br/>
<input type="button" value="send", onclick="send(this.form.message.value)"/>
<hr/>
resp:
<br/>
<textarea id="responseText" style="width: 200px;height:300px;">textarea>
form>

form>
body>
<script type="text/javascript">var socket;if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;}if (window.WebSocket) {
socket = new WebSocket("ws://127.0.0.1:23123/websocket");
socket.onmessage = function (ev) {var ta = document.getElementById('responseText');
ta.value = '';
ta.value = ev.data;}
socket.onopen = function (ev) {var ta = document.getElementById('responseText');
ta.value = 'start open websocket ...';}
socket.onclose = function (ev) {var ta = document.getElementById('responseText');
ta.value = '';
ta.value = 'close websocket ...';}}else {alert('not support websocket !')}function send(msg) {if (!window.WebSocket) {return;}if (socket.readyState == WebSocket.OPEN) {
socket.send(msg);}else {alert('connect fail !')}}script>
html>

試驗結果

  • 請求抓包說明

    ws請求

    如圖中,首先是TCP三次握手,然后HTTP請求,建立WebSocket連接。之后客戶端請求一次,然后服務端每隔1秒發送內容到客戶端。

    keepAlive


    上圖是空閑時候的keepAlive調用

  • 客戶端調用

    服務端關閉的時候效果

    服務端主動發送的效果

    上圖是服務端主動發送的效果,隨著內容不同數據會變化。

  • 參考資料

    [1]示例原始地址:github
    [2]《Netty權威指南(第二版)》P213

    結語

    以上就是本期的內容,后面有時間會繼續netty相關實例的文章,確實在網絡方面有很多新的收獲。

    總結

    以上是生活随笔為你收集整理的websocket 代理tcp_netty实现websocket请求实战的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。