1.Netty搭建WS服务端
@Component
@Slf4j
public class NettyServer {
@Autowired
private ServerHandler serverHandler;
public static ServerSocketChannel serverSocketChannel;
public void start(int port) throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
ChannelPipeline pipeline = sc.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(1024 * 62));
pipeline.addLast(new WebSocketServerProtocolHandler("/netty/connect"));
pipeline.addLast( // 自定义的处理器
serverHandler);
}
});
ChannelFuture future;
try {
log.info("netty服务器在[{}]端口启动监听", port);
future = bootstrap.bind(port).sync();
if (future.isSuccess()) {
serverSocketChannel = (ServerSocketChannel) future.channel();
log.info("netty服务开启成功");
} else {
log.info("netty服务开启失败");
}
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
@Component
@ChannelHandler.Sharable
@Slf4j
public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
String clientIp = ipSocket.getAddress().getHostAddress();
String channleId = ctx.channel().id().toString();
log.info("有新的netty客户端连接:id is {},ip is {}", channleId, clientIp);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (ChannelMapServer.getChannelHashMap() != null && ChannelMapServer.getChannelHashMap().size() > 0) {
Iterator<Map.Entry<String, Channel>> iterator = ChannelMapServer.getChannelHashMap().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Channel> next = iterator.next();
String key = next.getKey();
Channel channel = next.getValue();
if (channel.id().toString().equals(ctx.channel().id().toString())) {
log.info("netty客户端与服务端连接关闭...appKey is {}", key);
ChannelMapServer.removeChannelByName(key);
}
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
log.error("异常信息: {}", cause.getMessage());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) {
String json = textWebSocketFrame.text();
String msg = null;
if (StringUtils.isNotEmpty(json)) {
log.info("netty服务器收到客户端消息 {}", json);
if (json.contains(GloabalConstants.HEART_BEAT)) {
NettyHeartBo nettyHeartBo = JSONUtil.toBean(json, NettyHeartBo.class);
Channel channelByName = ChannelMapServer.getChannelByName(nettyHeartBo.getAppKey());
msg = channelByName == null ? BusinessErrorEnum.HEART_FAIL.getMsgEn() : BusinessErrorEnum.HEART_SUCC.getMsgEn();
} else {
NettyConfigBo nettyConfigBo = JSONUtil.toBean(json, NettyConfigBo.class);
boolean flag = checkAppKey(nettyConfigBo);
if (flag) {
ChannelMapServer.addChannel(IdUtil.randomUUID(), ctx.channel());
}
msg = flag ? BusinessErrorEnum.CONNECT_SUCC.getMsgEn() : BusinessErrorEnum.CONNECT_FAIL.getMsgEn();
}
}
ctx.channel().writeAndFlush(new TextWebSocketFrame(msg));
}
private boolean checkAppKey(NettyConfigBo nettyConfigBo) {
return true;
}
}
public class ChannelMapServer {
public static ConcurrentHashMap<String, Channel> channelHashMap=null;
public static ConcurrentHashMap<String, Channel> getChannelHashMap() {
return channelHashMap;
}
public static Channel getChannelByName(String name){
if(channelHashMap==null||channelHashMap.isEmpty()){
return null;
}
return channelHashMap.get(name);
}
public static boolean pushNewsToAllClient(String obj){
if(channelHashMap==null||channelHashMap.isEmpty()){
return false;
}
for(String name: channelHashMap.keySet()) {
Channel channel = channelHashMap.get(name);
channel.writeAndFlush(new TextWebSocketFrame(obj));
}
return true;
}
public static void addChannel(String name,Channel channel){
if(channelHashMap==null){
channelHashMap=new ConcurrentHashMap<>(128);
}
channelHashMap.put(name,channel);
}
public static void removeChannelByName(String name){
if(channelHashMap.containsKey(name)){
channelHashMap.remove(name);
}
}
}
2.WS客户端
public class main {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
try {
BaseWebsocketClient myClient = new BaseWebsocketClient(new URI("ws://127.0.0.1:8000/netty/connect"));
myClient.connect();
while (!myClient.getReadyState().equals(ReadyState.OPEN)) {
System.out.println("连接中....");
Thread.sleep(10);
}
myClient.send("");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}
@Slf4j
public class BaseWebsocketClient extends WebSocketClient {
public BaseWebsocketClient(URI serverUri) {
super(serverUri);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
log.info(">>>>>>>>>>>websocket open");
}
@Override
public void onMessage(String s) {
System.out.println("线程:【" + Thread.currentThread().getName() + "】 收到消息:" + s);
}
@Override
public void onClose(int i, String s, boolean b) {
log.info(">>>>>>>>>>>websocket close");
}
@Override
public void onError(Exception e) {
log.error(">>>>>>>>>websocket error {}", e);
}
}