netty chat
explain:
I found that it takes a lot of energy to write a blog. In order to leave more time for promotion, I just post the code. Learning address: https://netty.io/ And netty https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example Students who want to learn run the official website
My thoughts
The following implementation is achieved: for a person's private message chat with a person, clicking send can only mean that the person designated by you receives the message. The group send function means that all people can receive the message. For details, I will cut the following figure first (three object tests, please copy and paste, direct hard connect)
demonstration
Click to open chat
Demo address:
It doesn't have to expire one day Which big brother server is available
http://112.35.164.210:18089/
Chat server
Register directly as the bean object of springboot, package and run directly
public class JiayangApplication { public static void main(String[] args) { SpringApplication.run(JiayangApplication.class, args); } @Bean public void chat() throws Exception{ EventLoopGroup mainGroup = new NioEventLoopGroup(); EventLoopGroup subGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(mainGroup,subGroup) .channel(NioServerSocketChannel.class) .childHandler(new WSServerInitialzer());//Initializer ChannelFuture future = serverBootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); }finally { mainGroup.shutdownGracefully(); subGroup.shutdownGracefully(); } } }
Chat initialization configuration
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 WSServerInitialzer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); // pipeline.addLast(new HttpServerCodec()); // pipeline.addLast(new StringDecoder()); // pipeline.addLast(new StringEncoder()); // //webcocket is based on http protocol, so http codec is required // pipeline.addLast(new HttpServerCodec()); // //Support for writing big data stream // pipeline.addLast(new ChunkedWriteHandler()); / / supports big data flow // //Aggregate httpmessage into fullhttprequest or fullhttpresponse // pipeline.addLast(new HttpObjectAggregator(1024*64)); // //==========================The above is used to support http protocol support // //The protocol processed by websocket server, which is used to specify the route for client connection access: / ws // //This handler will help you deal with some heavy and complicated things // //handshaking(close,ping,pong)ping +pong = heartbeat // //For websocket, frames are used for transmission. Different data types correspond to different frames // pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); // //Custom handler // pipeline.addLast(new ChatHandler()); pipeline.addLast("http-decoder", new HttpServerCodec()); // Add the ObjectAggregator decoder to convert multiple messages into a single FullHttpRequest or FullHttpResponse pipeline.addLast("http-aggregator", new HttpObjectAggregator(65536)); // The main function of chunked is to support the code stream (large file transfer) sent asynchronously, but not to use too much memory to prevent java memory overflow pipeline.addLast(new ChunkedWriteHandler()); // Add custom handler pipeline.addLast( new ChatHandler()); // hanlder joining webSocket pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); } }
Client processing message
import com.alibaba.fastjson.JSON; 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.FullHttpRequest; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.concurrent.GlobalEventExecutor; import lombok.Data; import org.apache.commons.lang3.StringUtils; import java.net.URLDecoder; import java.util.concurrent.ConcurrentHashMap; /** * handler for handling messages * TextWebSocketFrame:In netty, it is suitable for the object that deals with text for websocket. frame is the carrier of message */ public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { //For logging and managing all clients private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); // The thread safe Map of the concurrent package is used to store the MyWebSocket object corresponding to each client. (replace with database later) private static ConcurrentHashMap<String, Channel> webSocketMap = new ConcurrentHashMap<>(); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // //Get client message String content = msg.text(); Msgs msgs = JSON.parseObject(content, Msgs.class); if(msgs.getType()==2){ clients.writeAndFlush(new TextWebSocketFrame(msgs.getFromUserId()+"(Mass hair):"+ msgs.getMsg())); }else{ String toUserId = msgs.getToUserId(); Channel channel = webSocketMap.get(toUserId); if (channel == null || !channel.isActive()) { // ctx.writeAndFlush(new TextWebSocketFrame("you say:+ msgs.getMsg())); ctx.writeAndFlush(new TextWebSocketFrame(msgs.getToUserId() + " Not online "+ "\n")); } else { // ctx.writeAndFlush(new TextWebSocketFrame("you say:+ msgs.getMsg())); channel.writeAndFlush(new TextWebSocketFrame(msgs.getFromUserId() + " : " + msgs.getMsg())); } } // System.out.println("received data:" + content); // //The following method is consistent with the above one // clients.writeAndFlush(new TextWebSocketFrame("[server in:]"+ // LocalDateTime.now() + "message received, message is:" + content)); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (null != msg && msg instanceof FullHttpRequest) { //Convert to http request FullHttpRequest request = (FullHttpRequest) msg; //Get the request address String uri = request.uri(); System.out.println(uri); //Judge whether it is a websocket request. If it is a parameter passed by us (my token) String origin = request.headers().get("Origin"); if (null == origin) { ctx.close(); } else { String userId = StringUtils.substringAfter(uri, "/ws/"); userId = URLDecoder.decode(userId, "UTF-8"); webSocketMap.put(userId, ctx.channel()); //Reset request address request.setUri("/ws"); clients.writeAndFlush(new TextWebSocketFrame(userId+" It's online----------")); clients.add(ctx.channel()); } } //Then create the request super.channelRead(ctx, msg); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channel...active"); super.channelActive(ctx); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channel...remove"); super.channelUnregistered(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channel...Inactive"); super.channelInactive(ctx); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("channel...Read complete"); super.channelReadComplete(ctx); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { System.out.println("channel...User event trigger"); super.userEventTriggered(ctx, evt); } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { System.out.println("channel...Changeable"); super.channelWritabilityChanged(ctx); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("Assistant class adding"); super.handlerAdded(ctx); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("Helper class removal"); super.handlerRemoved(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("Exception caught"); super.exceptionCaught(ctx, cause); } } @Data class Msgs { private String fromUserId; private String toUserId; private String msg; private int type; }
Front end code
The front-end code of slag and the effect of mobile phone are OK
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>My little black room</title> <script src="js/jquery-2.1.1.min.js"></script> </head> <body> <div style="background-color: #C9394A;text-align: center;"> <div id="tt" style="background-color: #C9394A;text-align: center;font-weight:800;font-size:3.75em;padding-top: 25px;align-items: center; vertical-align: middle; font-family: Georgia;">Little black house</div> <div style="background-color: #C9394A;text-align: center;"> <label>My nickname:</label> <input id="nm" placeholder="Please enter your name" onkeyup="event.keyCode==13?sure():''"/> <input type="button" value="determine" onclick="sure()" id="an"/> </div> <div style="background-color: #C9394A;text-align: center;"> <label>Chat with:</label> <input id="nm1" placeholder="Please enter chat object" onkeyup="event.keyCode==13?sure1():''"/> <input type="button" value="determine" onclick="sure1()" id="an1"/> </div> <div style="background-color: #C9394A;text-align: center;"> <input id="bu" type="button" value="Open chat" style="width: 19.01rem;" onclick="click1()"/> </div> <div style="background-color: #C9394A;text-align: center;"> <label >send message:</label> <input type="text" id="msgContent" placeholder="Please input what you want to send" onkeyup="event.keyCode==13?CHAT.chat():''"/> <input id="in" type="button" value="send out" onclick="CHAT.chat()" /> <input type="button" value="Mass hair" onclick="CHAT.chat1()" /> </div> <div style="background-color: #C9394A;"> <label>receive messages:</label> <div id="reveiveMsg"></div> </div> </div> <script type="application/javascript"> function keyupt(e){ console.log(111) var evt = window.event || e; if (evt.keyCode == 13){ alert('11') i //Carriage return event } } var msg = { fromUserId :"", toUserId : "", msg : "", type : 1 }; function click1(){ if(this.msg.fromUserId==""){ alert("Nickname cannot be empty") return } if(this.msg.toUserId == ""){ alert("Chat object cannot be empty") return } $("#bu").hide(); var that = this window.CHAT={ socket: null, init: function(){ if(window.WebSocket){ var m = document.getElementById("nm"); CHAT.socket = new WebSocket("ws://47.107.235.154:8080/ws/"+m.value); CHAT.socket.onopen = function(){ console.log("Connection established successfully...") }, CHAT.socket.onclose = function(){ CHAT.socket.close() console.log("Connection closed...") }, CHAT.socket.onerror = function(){ console.log("Connection error...") }, CHAT.socket.onmessage = function(e){ console.log("Message received..."+e.data); var reveiveMsg = document.getElementById("reveiveMsg"); var html = reveiveMsg.innerHTML; reveiveMsg.innerHTML = "<span style='background-color: green;display:block;text-align:block;margin-right:310px;margin-left:310px;float:left;'>"+e.data+"</span>"+"<br/>"+html } }else{ alert("Browser does not support websocket agreement...") } }, chat: function(){ var msg = document.getElementById("msgContent"); that.msg.msg = msg.value if(msg.value==""){ alert('Please enter content') return } reveiveMsg.innerHTML = "<span style='background-color: skyblue;display:block;text-align:block;margin-right:310px;margin-left:310px;float:right;'>You say?: "+msg.value+"</span>"+ "<br/>"+reveiveMsg.innerHTML that.msg.type = 1 console.log(JSON.stringify(that.msg)) CHAT.socket.send(JSON.stringify(that.msg)); document.getElementById("msgContent").value = ""; }, chat1: function(){ var msg = document.getElementById("msgContent"); that.msg.msg = msg.value if(msg.value==""){ alert('Please enter content') return } that.msg.type = 2 console.log(JSON.stringify(that.msg)) CHAT.socket.send(JSON.stringify(that.msg)); document.getElementById("msgContent").value = ""; }, } CHAT.init(); } function sure(){ var msg = document.getElementById("nm"); $('#nm').prop("disabled", msg.disabled?false:true) $('#an').prop("value",msg.disabled?"modify":"determine") if(msg.disabled){ this.msg.fromUserId = msg.value } } function sure1(){ var msg = document.getElementById("nm1"); $('#nm1').prop("disabled", msg.disabled?false:true) $('#an1').prop("value",msg.disabled?"modify":"determine") if(msg.disabled){ this.msg.toUserId = msg.value }else{ CHAT.socket.close() $("#bu").show(); this.msg.toUserId = "" document.getElementById("nm1").value = "" } } </script> <style> div{ border:solid 2px #C9394A; } </style> </body> </html>
epilogue
It takes a lot of time to write a blog with your heart, but not too much time. It's a bit slow to update things. People who have time to write a blog, the elderly are either big guys or big gods, the young people who blog every day, or people who have too much time to use, or who have dedication and sacrifice their own time