Implement simple chat function 1 to 1 based on netty (private message / group sending)

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

Keywords: Netty socket codec JSON

Added by toppac on Sun, 07 Jun 2020 07:35:12 +0300