1.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gzz</groupId>
<artifactId>Websocket-Netty</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
<version>4.1.87.Final</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.server
package com.gzz;
import java.util.Timer;
import java.util.TimerTask;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServer {
public static void main(String[] args) throws Exception {
SocketHandler socketHandler = new SocketHandler();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
socketHandler.send("Hello world!");
}
}, 3000, 3000); // 3秒后每3秒执行一次
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("http-codec", new HttpServerCodec());// 设置解码器
pipeline.addLast("aggregator", new HttpObjectAggregator(65536));// 聚合器,使用websocket会用到
pipeline.addLast("http-chunked", new ChunkedWriteHandler());// 用于大数据的分区传输
pipeline.addLast("handler", socketHandler);// 自定义的业务handler
pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));// 接入点
}
});
Channel channel = bootstrap.bind(8081).sync().channel();
log.info("webSocket服务器启动成功:" + channel);
channel.closeFuture().sync();
}
}
//1 netty直接websocket
//2 统一消息体
//3 统一接入点
//4 前端用一个共用连接 消息类型
3.SocketHandler
package com.gzz;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Sharable
public class SocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static final ConcurrentMap<String, Channel> session = new ConcurrentHashMap<>();
private static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
log.info("收到消息:" + msg.text());
ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:" + LocalDateTime.now().format(format) + msg.text()));
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
session.put(parseIp(ctx.channel().remoteAddress().toString()), ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
session.remove(parseIp(ctx.channel().remoteAddress().toString()));
}
/**
* 指定客户端消息
*/
public void send(String message, String ip) {
session.get(ip).writeAndFlush(new TextWebSocketFrame("服务器时间:" + LocalDateTime.now().format(format) + message));
}
/**
* 广播消息
*/
public void send(String message) {
session.values().forEach(ch -> ch.writeAndFlush(new TextWebSocketFrame("服务器时间:" + LocalDateTime.now().format(format) + message)));
}
private String parseIp(String ip) {
return ip.indexOf("0:0:0:0:0:0:0:1") > 0 ? "127.0.0.1" : ip.replaceAll("(/)(.*)(:)(.*)", "$2");
}
}
4.index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>直接用浏览器打开文件即可</title>
</head>
<body>
<textarea rows="10" cols="50" id="sendMsg" >{"id":1,"name":"高振中","gender":"男"}</textarea>
<button onclick=send()>发送</button>
<textarea rows="10" cols="70" id="responseText" ></textarea>
<button onclick="responseText.value=''">清空</button>
</body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8081/websocket");
socket.onmessage = function(event) {
responseText.value += event.data + "\r\n";
};
socket.onopen = function(event) {
responseText.value = "打开WebSoket 服务正常,浏览器支持WebSoket!" + "\r\n";
};
socket.onclose = function(event) {
responseText.value = "";
responseText.value = "WebSocket 关闭" + "\r\n";
};
} else {
alert("您的浏览器不支持WebSocket协议!");
}
function send() {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(sendMsg.value);
} else {
alert("WebSocket 连接没有建立成功!");
}
}
</script>
</html>

1675232322246.png
网友评论