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;
}
}
效果