Netty + Web聊天室

1 篇文章 0 订阅

web

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
</head>
<body>
<script type="text/javascript">
    var socket;
    var uid;

    if (window.WebSocket){
        console.log("ok")
    } else {
        alert("浏览器不支持")
    }
    
    function bindSocket(){
    	socket = new WebSocket("ws://127.0.0.1:8080/ws")

        socket.onmessage = function (ev) {
            var ta = document.getElementById("resultText")
            var d = JSON.parse(ev.data)
            if(d.type == 2){
            	ta.value = ta.value + "\n" + d.content
            } else {
	            ta.value = ta.value + "\n" + ev.data
            }
        }
        socket.onopen = function (ev) {
        	document.getElementById('msgText').style.display = 'inline'
        	document.getElementById('bind').style.display = 'none'
            var ta = document.getElementById("resultText")
            ta.value = "连接成功"
            if(uid != null){
	        	document.getElementById('uid').innerHTML = uid
	            socket.send(JSON.stringify({"type":1, "content":uid}))
            }
        }
        socket.onclose = function (ev) {
            var ta = document.getElementById("resultText")
            ta.value = ta.value + "\n" + "连接关闭"
        }
    }

    function send(type, message) {
        if (!window.WebSocket) {
            return
        } else {
        	if(socket == null){
	        	bindSocket()
	        	uid = message
        	}
        }
        
        console.log(socket.readyState)
        if (socket.readyState == WebSocket.OPEN && type == 2){
        	console.log(type, message)
            socket.send(JSON.stringify({"type":type, "content":message}))
        }

    }

</script>

<form onsubmit="return false;">
	<div id="bind">
		<input type="text" name="uid"></input>
	    <input type="button" value="绑定" onclick="send(1,this.form.uid.value);"><br/>
	</div>
    <div id="msgText" style="display: none;">
    	<h3 id="uid"></h3>
	    <textarea name="message" style="width: 400px;height: 30px"></textarea>
	    <input type="button" value="发送消息" onclick="send(2, this.form.message.value);">
	    <h3>聊天框</h3>
	    <textarea id="resultText" style="width: 400px;height: 100px"></textarea>
	    <input type="button" onclick="javascript:document.getElementById('resultText').value=''" value="清空内容">
    </div>

</form>
</body>
</html>

后端

server

import java.util.List;
import java.util.Map;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
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.FullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.Data;

public class WSWebNettyServer {
	public static final int port = 8080;

	public static void main(String[] args) throws Exception {
		new WSWebNettyServer().start();
	}

	public void start() throws Exception {
		ConsoleUtil.println("start");

		EventLoopGroup pGroup = new NioEventLoopGroup();
		EventLoopGroup cGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap bootstrap = new ServerBootstrap();
			WSConsoleServerHandler handler = new WSConsoleServerHandler();
			bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
				.group(pGroup, cGroup)
				.channel(NioServerSocketChannel.class)
				.childOption(ChannelOption.SO_KEEPALIVE, true)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline()
							.addLast(new HttpServerCodec())
				        	.addLast(new ChunkedWriteHandler())
				        	.addLast(new HttpObjectAggregator(8192))
							.addLast(handler)
							.addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10))
							;
					}
					
				})
			;
			
			ChannelFuture f = bootstrap.bind(port).sync();
			f.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pGroup.shutdownGracefully();
			cGroup.shutdownGracefully();
		}
		
		ConsoleUtil.println("end");
	}

}



@Sharable
class WSConsoleServerHandler extends ChannelInboundHandlerAdapter {
	
	private static Map<String, ChannelId> channelMap = Maps.newHashMap();
	private static Map<String, String> keyMap = Maps.newHashMap();
	private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		channelGroup.add(ctx.channel());
		ConsoleUtil.println("up " + ctx.channel().remoteAddress());
		super.channelActive(ctx);
	}
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		channelGroup.remove(ctx.channel());
		ConsoleUtil.println("down " + ctx.channel().remoteAddress());
		super.channelInactive(ctx);
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
		ConsoleUtil.println("error " + ctx.channel().remoteAddress());
		super.exceptionCaught(ctx, cause);
	}
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (null != msg && msg instanceof FullHttpRequest) {
			ConsoleUtil.println("receive request : " + msg.getClass());
		} else if (null != msg && msg instanceof TextWebSocketFrame) {
			TextWebSocketFrame frame = (TextWebSocketFrame) msg;
			ConsoleUtil.println("receive web : " + frame.text());
			WsWebJsonData data = JsonUtil.decode(frame.text(), WsWebJsonData.class);
			if(WsWebJsonData.USERID_TYPE.equals(data.getType())){
				channelMap.put(data.getContent().toString(), ctx.channel().id());
				keyMap.put(ctx.channel().id().asLongText(), data.getContent().toString());
			} else if(WsWebJsonData.MESSAGE_TYPE.equals(data.getType())) {
				data.setContent(keyMap.get(ctx.channel().id().asLongText()) + ": " + data.getContent());
				pushMessage(ctx, data);
			}
		} else {
			ConsoleUtil.println("receive : " + msg.getClass());
		}
		super.channelRead(ctx, msg);
	}

	public Channel getChannel(String channelKey) {
		if(channelMap.get(channelKey) != null) {
			return channelGroup.find(channelMap.get(channelKey));
		}
		return null;
	}
	
	public void pushMessage(ChannelHandlerContext ctx, Object message) {
		List<String> rmKeys = Lists.newArrayList();
		for(String key : channelMap.keySet()) {
			Channel channel = getChannel(key);
			if(channel != null && channel.isActive()) {
				channel.writeAndFlush(new TextWebSocketFrame(JsonUtil.encode(message)));
			} else {
				rmKeys.add(key);
			}
		}
		
		for(String key : rmKeys) {
			channelMap.remove(key);
		}
		
	}

}

@Data
class WsWebJsonData {
	
	public static final Integer USERID_TYPE = 1;
	public static final Integer MESSAGE_TYPE = 2;
	
	private Integer type;
	private Object content;
	public WsWebJsonData(Integer type, Object content) {
		super();
		this.type = type;
		this.content = content;
	}
	public WsWebJsonData(){}
	
}

工具类

import java.text.SimpleDateFormat;
import java.util.Date;

public class ConsoleUtil {
	
	private static String format = "yyyy-MM-dd HH:mm:ss.SSS";
	
	public static void println(Object obj) {
		System.out.println(convertToString(System.currentTimeMillis()) + " : " + obj);
	}
	
	
	private static String convertToString(Long date) {
        SimpleDateFormat formater = new SimpleDateFormat(format);
        try {
            return formater.format(new Date(date));
        } catch (Exception e) {
            return null;
        }
    }


	public static void println(String format, Object ... objs) {
		System.out.println(convertToString(System.currentTimeMillis()) + " : " + String.format(format, objs));
	}
}


import com.alibaba.fastjson.JSON;

public class JsonUtil {
	
	public static <T> T decode(String json, Class<T> clazz){
		return JSON.parseObject(json, clazz);
	}
	
	public static String encode(Object obj) {
		String json = JSON.toJSONString(obj);
		return json;
	}

}

效果

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值