2023年3月3日17:15:41
parent
8bfc462af7
commit
0f12ae23df
27
pom.xml
27
pom.xml
|
|
@ -559,33 +559,6 @@
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
||||||
<!-- <plugin>-->
|
|
||||||
<!-- <groupId>org.apache.maven.plugins</groupId>-->
|
|
||||||
<!-- <artifactId>maven-assembly-plugin</artifactId>-->
|
|
||||||
<!-- <version>3.3.0</version>-->
|
|
||||||
<!-- <configuration>-->
|
|
||||||
<!-- <archive>-->
|
|
||||||
<!-- <manifest>-->
|
|
||||||
<!-- <!– <mainClass>入口类,main 方法所在位置</mainClass> –>-->
|
|
||||||
<!-- <mainClass>com.zhangmeng.tools.JavaFxToolsApplication</mainClass>-->
|
|
||||||
<!-- </manifest>-->
|
|
||||||
<!-- </archive>-->
|
|
||||||
<!-- <descriptorRefs>-->
|
|
||||||
<!-- <!– jar 携带依赖 –>-->
|
|
||||||
<!-- <descriptorRef>jar-with-dependencies</descriptorRef>-->
|
|
||||||
<!-- </descriptorRefs>-->
|
|
||||||
<!-- </configuration>-->
|
|
||||||
<!-- <executions>-->
|
|
||||||
<!-- <execution>-->
|
|
||||||
<!-- <phase>package</phase>-->
|
|
||||||
<!-- <goals>-->
|
|
||||||
<!-- <goal>single</goal>-->
|
|
||||||
<!-- </goals>-->
|
|
||||||
<!-- </execution>-->
|
|
||||||
<!-- </executions>-->
|
|
||||||
<!-- </plugin>-->
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ import io.netty.channel.*;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
import io.netty.handler.codec.http.*;
|
||||||
import io.netty.handler.codec.http.HttpClientCodec;
|
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
|
||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
|
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
|
||||||
|
|
@ -29,8 +26,7 @@ import java.net.URISyntaxException;
|
||||||
public class NettyClient {
|
public class NettyClient {
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
Ws("ws"),
|
Ws("ws"), Wss("wss");
|
||||||
Wss("wss");
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
@ -52,14 +48,13 @@ public class NettyClient {
|
||||||
|
|
||||||
private String params;
|
private String params;
|
||||||
|
|
||||||
private Channel channel;
|
private Channel channel = null;
|
||||||
|
|
||||||
private final EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
|
private EventLoopGroup workerLoopGroup = null;
|
||||||
|
private ChannelFuture future = null;
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
workerLoopGroup = new NioEventLoopGroup();
|
||||||
WebSocketClientHandshaker handshaker = this.getWebSocketClientHandshaker();
|
|
||||||
var businessHandler = new WebsocketClientHandler(handshaker);
|
|
||||||
Bootstrap bootstrap = new Bootstrap();
|
Bootstrap bootstrap = new Bootstrap();
|
||||||
bootstrap.group(workerLoopGroup);
|
bootstrap.group(workerLoopGroup);
|
||||||
bootstrap.channel(NioSocketChannel.class);
|
bootstrap.channel(NioSocketChannel.class);
|
||||||
|
|
@ -75,11 +70,27 @@ public class NettyClient {
|
||||||
// 方便大文件传输,不过实质上都是短的文本数据
|
// 方便大文件传输,不过实质上都是短的文本数据
|
||||||
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
|
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
|
||||||
// 定义业务处理器
|
// 定义业务处理器
|
||||||
ch.pipeline().addLast("businessHandler", businessHandler);
|
ch.pipeline().addLast("businessHandler", new WebsocketClientHandler());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ChannelFuture future = bootstrap.connect();
|
|
||||||
|
try {
|
||||||
|
URI websocketURI = new URI(String.format("ws://%s:%d/websocket?%s", serverIp, serverPort,params));
|
||||||
|
log.info(String.format("ws://%s:%d/", serverIp, serverPort));
|
||||||
|
HttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||||
|
//进行握手
|
||||||
|
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(websocketURI, WebSocketVersion.V13, (String)null, true,httpHeaders);
|
||||||
|
channel = bootstrap.connect(websocketURI.getHost(), websocketURI.getPort()).sync().channel();
|
||||||
|
WebsocketClientHandler handler = (WebsocketClientHandler)channel.pipeline().get("businessHandler");
|
||||||
|
handler.setHandshaker(handshaker);
|
||||||
|
// 通过它构造握手响应消息返回给客户端,
|
||||||
|
// 同时将WebSocket相关的编码和解码类动态添加到ChannelPipeline中,用于WebSocket消息的编解码,
|
||||||
|
// 添加WebSocketEncoder和WebSocketDecoder之后,服务端就可以自动对WebSocket消息进行编解码了
|
||||||
|
handshaker.handshake(channel);
|
||||||
|
//阻塞等待是否握手成功
|
||||||
|
future = handler.handshakeFuture().sync();
|
||||||
|
|
||||||
future.addListener((ChannelFuture futureListener) -> {
|
future.addListener((ChannelFuture futureListener) -> {
|
||||||
if (futureListener.isSuccess()) {
|
if (futureListener.isSuccess()) {
|
||||||
log.info("客户端连接成功");
|
log.info("客户端连接成功");
|
||||||
|
|
@ -87,46 +98,12 @@ public class NettyClient {
|
||||||
log.info("客户端连接失败");
|
log.info("客户端连接失败");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
|
||||||
future.sync();
|
|
||||||
} catch (Exception exception) {
|
|
||||||
log.info(exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.channel = future.channel();
|
} catch (Exception e) {
|
||||||
handshaker.handshake(channel);
|
e.printStackTrace();
|
||||||
try {
|
|
||||||
businessHandler.sync();
|
|
||||||
} catch (InterruptedException exception) {
|
|
||||||
log.info(exception.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateWebsocketUrl(String ip, int port, String relativePath,String params) {
|
|
||||||
// return String.format("ws://%s:%d/%s?%s", ip, port, relativePath,params);
|
|
||||||
// return String.format("ws://%s:%d?%s", ip, port,params);
|
|
||||||
// return String.format("ws://%s:%d", ip, port);
|
|
||||||
return String.format("ws://%s:%d/%s", ip, port, relativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String WEBSOCKET_PREFIX_PATH = "websocket";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 进行三报文握手中的第一握手,由客户端发起
|
|
||||||
*
|
|
||||||
* @since 2021-12-2
|
|
||||||
*/
|
|
||||||
private WebSocketClientHandshaker getWebSocketClientHandshaker() {
|
|
||||||
URI websocketUri = null;
|
|
||||||
try {
|
|
||||||
// websocketUri = new URI(this.generateWebsocketUrl(this.serverIp, this.serverPort, WEBSOCKET_PREFIX_PATH,this.params));
|
|
||||||
websocketUri = new URI(this.generateWebsocketUrl(this.serverIp, this.serverPort, WEBSOCKET_PREFIX_PATH,null));
|
|
||||||
log.info(websocketUri.toString());
|
|
||||||
} catch (URISyntaxException exception) {
|
|
||||||
exception.printStackTrace(); // FIXME:日志
|
|
||||||
}
|
|
||||||
return WebSocketClientHandshakerFactory.newHandshaker(websocketUri, WebSocketVersion.V13, null, false, new DefaultHttpHeaders());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(String msg) {
|
public void send(String msg) {
|
||||||
channel.writeAndFlush(new TextWebSocketFrame(msg));
|
channel.writeAndFlush(new TextWebSocketFrame(msg));
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,81 @@
|
||||||
package com.zhangmeng.tools.netty;
|
package com.zhangmeng.tools.netty;
|
||||||
|
|
||||||
import com.zhangmeng.tools.controller.NettyClientController;
|
import com.zhangmeng.tools.controller.NettyClientController;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.*;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.handler.codec.http.*;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
|
||||||
import io.netty.handler.codec.http.websocketx.*;
|
import io.netty.handler.codec.http.websocketx.*;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author : 芊芊墨客
|
* @author : 芊芊墨客
|
||||||
* @version : 1.0
|
* @version : 1.0
|
||||||
* @date : 2023-02-27 09:20
|
* @date : 2023-02-27 09:20
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class WebsocketClientHandler extends ChannelInboundHandlerAdapter {
|
public class WebsocketClientHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
|
||||||
|
|
||||||
private WebSocketClientHandshaker handshaker;
|
WebSocketClientHandshaker handshaker;
|
||||||
|
ChannelPromise handshakeFuture;
|
||||||
|
|
||||||
private ChannelPromise channelPromise;
|
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||||
|
this.handshakeFuture = ctx.newPromise();
|
||||||
|
}
|
||||||
|
public WebSocketClientHandshaker getHandshaker() {
|
||||||
|
return handshaker;
|
||||||
|
}
|
||||||
|
|
||||||
public WebsocketClientHandler(WebSocketClientHandshaker handshaker) {
|
public void setHandshaker(WebSocketClientHandshaker handshaker) {
|
||||||
this.handshaker = handshaker;
|
this.handshaker = handshaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ChannelPromise getHandshakeFuture() {
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object obj) {
|
return handshakeFuture;
|
||||||
if (!this.handshaker.isHandshakeComplete()) { // 如果三报文握手的流程还没有走完
|
}
|
||||||
// 如果现在进行的是三报文握手中的第三握手
|
|
||||||
finishHandshake(ctx, (FullHttpResponse) obj);
|
|
||||||
} else if (obj instanceof FullHttpResponse response) {
|
|
||||||
var msg = String.format("Unexpected FullHttpResponse, status=%s, content=%s", response.status(), response.content().toString(CharsetUtil.UTF_8));
|
|
||||||
System.out.println(msg); // FIXME:日志
|
|
||||||
} else if (obj instanceof WebSocketFrame) {
|
|
||||||
// TODO:如果需要服务器反馈信息,可在此添加业务
|
|
||||||
handleWebSocketResponse(ctx, (WebSocketFrame) obj);
|
|
||||||
} // 此分支不应该发生
|
|
||||||
|
|
||||||
|
public void setHandshakeFuture(ChannelPromise handshakeFuture) {
|
||||||
|
this.handshakeFuture = handshakeFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelFuture handshakeFuture() {
|
||||||
|
return this.handshakeFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
|
||||||
ctx.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
|
||||||
this.channelPromise = ctx.newPromise();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChannelFuture sync() throws InterruptedException {
|
|
||||||
return this.channelPromise.sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finishHandshake(ChannelHandlerContext ctx, FullHttpResponse response) {
|
|
||||||
try {
|
|
||||||
this.handshaker.finishHandshake(ctx.channel(), response);
|
|
||||||
//设置成功
|
|
||||||
this.channelPromise.setSuccess();
|
|
||||||
} catch (WebSocketHandshakeException exception) {
|
|
||||||
FullHttpResponse rsp = response;
|
|
||||||
String errorMsg = String.format("WebSocket Client failed to connect, status=%s, reason=%s", rsp.status(), rsp.content().toString(CharsetUtil.UTF_8));
|
|
||||||
this.channelPromise.setFailure(new Exception(errorMsg)); // TODO:日志
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleWebSocketResponse(ChannelHandlerContext ctx, WebSocketFrame frame) {
|
|
||||||
|
|
||||||
// 关闭请求
|
// 关闭请求
|
||||||
if (frame instanceof CloseWebSocketFrame) {
|
if (frame instanceof CloseWebSocketFrame) {
|
||||||
|
if (handshaker == null) {
|
||||||
|
sendErrorMessage(ctx, "不存在的客户端连接!");
|
||||||
|
} else {
|
||||||
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
|
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping请求
|
// ping请求
|
||||||
if (frame instanceof PingWebSocketFrame) {
|
if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
|
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String text = ((TextWebSocketFrame) frame).text();
|
// 只支持文本格式,不支持二进制消息
|
||||||
log.info("text:{}", text);
|
if (!(frame instanceof TextWebSocketFrame)) {
|
||||||
NettyClientController.add_message(text);
|
sendErrorMessage(ctx, "仅支持文本(Text)格式,不支持二进制消息");
|
||||||
|
}
|
||||||
|
String request = ((TextWebSocketFrame) frame).text();
|
||||||
|
log.info(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendErrorMessage(ChannelHandlerContext ctx, String errorMsg) {
|
||||||
|
String responseJson = new ResponseJson()
|
||||||
|
.error(errorMsg)
|
||||||
|
.toString();
|
||||||
|
ctx.channel().writeAndFlush(new TextWebSocketFrame(responseJson));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,17 +45,17 @@ public class JsoupUtil {
|
||||||
.post();
|
.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
// public static void main(String[] args) {
|
||||||
try {
|
// try {
|
||||||
Document document = sendGet("https://so.gushiwen.cn/shiwenv_45c396367f59.aspx", 5000);
|
// Document document = sendGet("https://so.gushiwen.cn/shiwenv_45c396367f59.aspx", 5000);
|
||||||
Element body = document.body();
|
// Element body = document.body();
|
||||||
Element sonsyuanwen = body.getElementById("sonsyuanwen");
|
// Element sonsyuanwen = body.getElementById("sonsyuanwen");
|
||||||
String title = sonsyuanwen.getElementsByTag("h1").get(0).text();
|
// String title = sonsyuanwen.getElementsByTag("h1").get(0).text();
|
||||||
String user_name = sonsyuanwen.getElementsByTag("p").get(0).getElementsByTag("a").get(0).text();
|
// String user_name = sonsyuanwen.getElementsByTag("p").get(0).getElementsByTag("a").get(0).text();
|
||||||
String date_name = sonsyuanwen.getElementsByTag("p").get(0).getElementsByTag("a").get(1).text();
|
// String date_name = sonsyuanwen.getElementsByTag("p").get(0).getElementsByTag("a").get(1).text();
|
||||||
String content = sonsyuanwen.getElementsByClass("cont").get(0).getElementsByClass("contson").text();
|
// String content = sonsyuanwen.getElementsByClass("cont").get(0).getElementsByClass("contson").text();
|
||||||
} catch (IOException e) {
|
// } catch (IOException e) {
|
||||||
e.printStackTrace();
|
// e.printStackTrace();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue