netty整合使用webscoket

上一篇我们简单说了一下netty的基本操作,其实在netty底层,提供了大量的支持服务端和客户端进行通讯的组件,比如像http协议,支持webscoket的整合等,可以很方便的进行使用,当然,我们也可以单纯的使用websocket进行客户端和服务端的通信,可根据业务场景和自己项目的情况进行选择,下面来实现一个通过页面发送消息到netty服务端,再有服务端推送消息给客户端的功能,

整个部分分为2步,我们先进行服务端代码编写,根据上一篇我们使用的经验,服务端启动主要包括三个类,1、初始化连接配置 2、自定义childHandler 3、自定义业务逻辑处理

1、pom依赖文件:

        <dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>5.0.0.Alpha2</version>
			<!-- <version>4.1.24.Final</version> -->
		</dependency>

2、netty初始化启动配置线程组信息:

package com.congge.sort.netty.day2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * 服务端
 * @author asus
 *
 */
public class WssServer {
	
	public static void main(String[] args) {
		
		EventLoopGroup mainGroup = new NioEventLoopGroup();
		EventLoopGroup subGroup = new NioEventLoopGroup();
		
		try {
			ServerBootstrap server = new ServerBootstrap();
			server.group(mainGroup, subGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(new WssServerInitialzer());	//添加自定义初始化处理器
			ChannelFuture future = server.bind(8087).sync();
			future.channel().closeFuture().sync();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			mainGroup.shutdownGracefully();
			subGroup.shutdownGracefully();
		}
	}
	
}	

3、WssServerInitialzer ,这个类主要配置相应的处理业务的前置信息,比如像和客户端通信,指定相应的通信协议,编码解码信息,自定义相关的handler,其实也可以放在一个代码块儿里面,netty这里使用的类似java设计模式中的责任链模式,可以将所有的自定义或者相关的handler串联起来,对应的就是 : pipeline.addLast ,可以为pipeline添加多个handler

package com.congge.sort.netty.day2;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;

public class WssServerInitialzer extends ChannelInitializer<SocketChannel>{

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
	
		ChannelPipeline pipeline = ch.pipeline();
		
		//websocket基于http协议,所以需要http编解码器
		pipeline.addLast(new HttpServerCodec());
		//添加对于读写大数据流的支持
		pipeline.addLast(new ChunkedWriteHandler());
		//对httpMessage进行聚合
		pipeline.addLast(new HttpObjectAggregator(1024*64));
		
		// ================= 上述是用于支持http协议的 ==============
		
		//websocket 服务器处理的协议,用于给指定的客户端进行连接访问的路由地址
		//比如处理一些握手动作(ping,pong)
		pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
		
		//自定义handler
		pipeline.addLast(new ChatHandler());
		
		
	}

}

4、下面是我们自定义的handler,这个里面要做的就是处理客户端发送过来的数据,解析数据,并将相应的服务端数据或者业务消息推送给客户端,同时这个类里面,我们可以通过调用netty提供的相应的API,及时捕获到客户端和服务端的连接信息,即所谓的生命周期,可以参照代码和调试的时候看出来,

package com.congge.sort.netty.day2;

import java.time.LocalDateTime;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * 聊天的ehandler
 * TextWebSocketFrame  用于为websockt处理文本的对象
 * @author asus
 *
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{

	//用于记录和管理所有客户端的channel
	private static ChannelGroup clients = 
			new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	
	@Override
	protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) 
			throws Exception {
		//客户端传递过来的消息
		String content = msg.text();
		System.out.println("接收到了客户端的消息是:" + content);
		
		//将客户端发送过来的消息刷到所有的channel中
		for(Channel channel : clients){
			//channel.writeAndFlush(msg);	
			channel.writeAndFlush(
					new TextWebSocketFrame("[服务器接收到了客户端的消息:]" + LocalDateTime.now()+",消息为:" + content));
		}
		
//		clients.writeAndFlush(
//				new TextWebSocketFrame("[服务器接收到了客户端的消息:]" + LocalDateTime.now()+",消息为:" + content));
		
	}
	
	//客户端创建的时候触发,当客户端连接上服务端之后,就可以获取该channel,然后放到channelGroup中进行统一管理	
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		clients.add(ctx.channel());
	}
	
	//客户端销毁的时候触发,
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		//当handlerRemoved 被触发时候,channelGroup会自动移除对应的channel
		//clients.remove(ctx.channel());
		System.out.println("客户端断开,当前被移除的channel的短ID是:" +ctx.channel().id().asShortText());
	}
	

}

5、接下来是客户端代码编写,为了模拟的效果更真实,这里简单写了一个html页面,

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
	</head>
	<body>
		
		<div>发送消息</div>
		<input type="text" id="msgContent"/>
		<input type="button" value="点我发送" onclick="CHAT.chat()"/>
		
		<div>接收消息:</div>
		
		<div id="receiveMsg" style="background-color: burlywood;"></div>
		
		<script type="application/javascript">
			
			window.CHAT = {
				socket:null,
				init:function(){
					if(window.WebSocket){
						CHAT.socket = new WebSocket("ws://192.168.111.1:8087/ws");
						
						CHAT.socket.onopen = function(){
							console.log("连接创建成功...");
						};
						CHAT.socket.onclose = function(){
							console.log("连接关闭...");
						}
						CHAT.socket.onerror = function(){
							console.log("连接发生错误...");
						}
						CHAT.socket.onmessage = function(e){
							console.log("接收消息:" + e.data);
							var receiveMsg = document.getElementById("receiveMsg");
							var html = receiveMsg.innerHTML;
							receiveMsg.innerHTML = html + "<br />"+e.data;
						}
					}else{
						alert("浏览器不支持webscoket协议。。。")
					}
				},
				chat:function(){
					var msg = document.getElementById("msgContent");
					CHAT.socket.send(msg.value);
				}
			}
			CHAT.init();
		</script>
		
	</body>
</html>

页面效果如图:
在这里插入图片描述

我们重点关注一下里面的JS,其实就是创建了一个wensocket的对象,然后可以将文本框输入内容发送出去,

6、启动服务端,可以看到控制台输出了内容,表示连接服务端成功,然后我们再发送一条消息给服务端,
在这里插入图片描述
服务端控制台收到了客户端的消息:
在这里插入图片描述

同时服务端将消息推回给了客户端:
在这里插入图片描述

这时我们断开服务端的连接,客户端捕捉到了连接断开的消息,当然,
在这里插入图片描述

当然,如果我们关闭浏览器,服务端也可以看到捕捉到这个客户端连接断开的信息,利用netty的这种生命周期相关的API可以在此做一些比如聊天室好友上下线的提示等…
在这里插入图片描述
到此,netty使用websocket基本结束,感谢观看!

### Spring Boot 整合 NettyWebSocket 实现方案 #### 1. 添加依赖项 要在Spring Boot项目中集成NettyWebSocket,首先需要在`pom.xml`文件中添加必要的依赖项。这通常包括Spring WebFlux、Netty以及WebSocket支持的相关库。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>io.projectreactor.netty</groupId> <artifactId>reactor-netty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 这些依赖项提供了构建基于反应式的Web应用程序所需的基础结构和支持[^4]。 #### 2. 创建WebSocket配置类 创建一个Java配置类来定义WebSocket服务器的行为。通过继承`WebSocketServerEndpointConfigurer`并重写相应的方法可以自定义握手处理器和其他设置。 ```java @Configuration public class WebSocketConfig implements WebSocketServerCustomizer { @Override public void customize(ReactiveWebSocketService service) { // 自定义WebSocket服务行为 } } ``` 此部分代码用于初始化WebSocket的服务端逻辑,并允许开发者根据需求调整其工作方式[^2]。 #### 3. 编写处理程序 编写具体的WebSocket事件处理程序,负责监听来自客户端的消息并与之交互。这里展示了一个简单的例子: ```java @Component @ServerEndpoint("/ws/{userId}") public class MyWebSocketHandler extends TextWebSocketHandler { private final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class); @Autowired private PushMsgService pushMsgService; @OnOpen public void handleConnection(ServerHttpRequest request, ServerHttpResponse response, WebSocketSession session, EndpointConfig config) throws IOException { String userId = (String)session.getAttributes().get("USER_ID"); this.pushMsgService.addUser(userId, session); logger.info("New connection from user {}", userId); } @OnMessage public void handleMessage(TextMessage message, WebSocketSession session) throws IOException { // 处理接收到的信息 } @OnError public void handleError(Throwable error){ // 错误处理机制 } @OnClose public void handleClose(CloseReason reason, WebSocketSession session)throws Exception{ // 断开连接后的清理操作 } } ``` 上述代码片段展示了如何利用注解驱动的方式捕获不同类型的WebSocket生命周期事件,并执行相应的业务逻辑[^5]。 #### 4. 构建消息推送服务 为了能够向已建立的WebSocket会话发送通知或更新数据给前端页面,还需要开发一套有效的消息广播系统。下面给出了一种可能的设计模式——即封装好的`PushMsgService`接口及其默认实现: ```java @Service public class SimplePushMsgServiceImpl implements PushMsgService { private Map<String, List<WebSocketSession>> usersSessionsMap = new ConcurrentHashMap<>(); @Override public synchronized void addUser(String userId, WebSocketSession session) { if (!usersSessionsMap.containsKey(userId)) { usersSessionsMap.putIfAbsent(userId, Collections.synchronizedList(new ArrayList<>())); } usersSessionsMap.get(userId).add(session); } @Override public void pushMsgToOne(String userId, String msg) { Optional.ofNullable(usersSessionsMap.get(userId)) .ifPresent(sessions -> sessions.forEach(session -> sendToSession(msg, session))); } @Override public void pushMsgToAll(String msg) { usersSessionsMap.values() .stream() .flatMap(Collection::stream) .forEach(session -> sendToSession(msg, session)); } private void sendToSession(String msg, WebSocketSession session) { try { if (session.isOpen()) { session.sendMessage(new TextMessage(msg)); } } catch (IOException e) { log.error("Failed to send message", e); } } } ``` 这段代码实现了基本的一对一及一对多的消息分发能力,确保任何时刻都能及时有效地传递信息到目标用户那里。 #### 5. 测试与调试 最后,在完成以上步骤之后就可以启动应用并通过工具如Postman或者专门设计用来测试WebSocket的应用来进行验证了。确保一切正常运行后再考虑部署上线等问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小码农叔叔

谢谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值