Netty笔记9----实现一个简单webSocket聊天室

Netty笔记9----实现一个简单webSocket聊天室

  • 启动类

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

public class WebsocketChatServer {

    private int port;

    public WebsocketChatServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .childHandler(new WebsocketChatServerInitializer())
             .option(ChannelOption.SO_BACKLOG, 128)
             .childOption(ChannelOption.SO_KEEPALIVE, true);
            
          System.out.println("WebsocketChatServer 启动了" + port);
          
            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(port).sync();

            // 等待服务器  socket 关闭 。
            // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
            f.channel().closeFuture().sync();

        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            
          System.out.println("WebsocketChatServer 关闭了");
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new WebsocketChatServer(port).run();

    }
}
  • 初始化类

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 WebsocketChatServerInitializer extends
      ChannelInitializer<SocketChannel> {    //1

   @Override
    public void initChannel(SocketChannel ch) throws Exception {//2
      ChannelPipeline pipeline = ch.pipeline();

      //将请求和应答消息编码或者解码为HTTP消息,
        pipeline.addLast(new HttpServerCodec());
        //将http消息的多个部分组合成一条完整的http消息
      pipeline.addLast(new HttpObjectAggregator(64*1024));
      //向客户端发送H5文件,主要用于支持浏览器和服务端进行websocket通信
      pipeline.addLast(new ChunkedWriteHandler());
      pipeline.addLast(new HttpRequestHandler("/ws"));
      pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
      pipeline.addLast(new TextWebSocketFrameHandler());

    }
}
  • http请求处理类

    import io.netty.channel.*;
    import io.netty.handler.codec.http.*;
    import io.netty.handler.codec.http2.Http2Headers;
    import io.netty.handler.ssl.SslHandler;
    import io.netty.handler.stream.ChunkedNioFile;
    import static io.netty.handler.codec.http.HttpHeaders.Names.*;
    
    import java.io.File;
    import java.io.RandomAccessFile;
    import java.net.URISyntaxException;
    import java.net.URL;
    
    public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1
        private final String wsUri;
        private static final File INDEX;
    
        static {
            URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation();
            try {
                String path = location.toURI() + "WebsocketChatClient.html";
                path = !path.contains("file:") ? path : path.substring(5);
                INDEX = new File(path);
            } catch (URISyntaxException e) {
                throw new IllegalStateException("Unable to locate WebsocketChatClient.html", e);
            }
        }
    
        public HttpRequestHandler(String wsUri) {
            this.wsUri = wsUri;
        }
    
        @Override
        public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
            if (wsUri.equalsIgnoreCase(request.uri())) {
                ctx.fireChannelRead(request.retain());
            } else {
                if (HttpUtil.is100ContinueExpected(request)) {
                    send100Continue(ctx);
                }
    
                RandomAccessFile file = new RandomAccessFile(INDEX, "r");
    
                HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
                response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
                boolean keepAlive = HttpUtil.isKeepAlive(request);
    
                if (keepAlive) {
                    response.headers().set(CONTENT_LENGTH, file.length());
                    response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
                }
                ctx.write(response);
    
                if (ctx.pipeline().get(SslHandler.class) == null) {
                    ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
                } else {
                    ctx.write(new ChunkedNioFile(file.getChannel()));
                }
                ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
                if (!keepAlive) {
                    future.addListener(ChannelFutureListener.CLOSE);
                }
                
                file.close();
            }
        }
    
        private static void send100Continue(ChannelHandlerContext ctx) {
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
            ctx.writeAndFlush(response);
        }
    
       @Override
       public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
             throws Exception {
           Channel incoming = ctx.channel();
          System.out.println("Client:"+incoming.remoteAddress()+"异常");
            // 当出现异常就关闭连接
            cause.printStackTrace();
            ctx.close();
       }
    }

     

  • webSocket处理类

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;

public class TextWebSocketFrameHandler extends
      SimpleChannelInboundHandler<TextWebSocketFrame> {
   
   public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
   
   @Override
   protected void channelRead0(ChannelHandlerContext ctx,
         TextWebSocketFrame msg) throws Exception { // (1)
      Channel incoming = ctx.channel();
      for (Channel channel : channels) {
            if (channel != incoming){
                channel.writeAndFlush(new TextWebSocketFrame("[" + incoming.remoteAddress() + "]" + msg.text()));
            } else {
               channel.writeAndFlush(new TextWebSocketFrame("[you]" + msg.text() ));
            }
        }
   }
   
   @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {  // (2)
        Channel incoming = ctx.channel();
        
        // 向通道里广播消息
        channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入"));
        
        channels.add(incoming);
      System.out.println("Client:"+incoming.remoteAddress() +"加入");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
        Channel incoming = ctx.channel();

      // 向通道里广播消息
        channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开"));
        
      System.out.println("Client:"+incoming.remoteAddress() +"离开");

      // 通道Channel关闭以后会自动从ChannelGroup移除,所以不需要channels.remove(ctx.channel()).
    }
       
   @Override
   public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
        Channel incoming = ctx.channel();
      System.out.println("Client:"+incoming.remoteAddress()+"在线");
   }
   
   @Override
   public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
        Channel incoming = ctx.channel();
      System.out.println("Client:"+incoming.remoteAddress()+"掉线");
   }
   
   @Override
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)    // (7)
         throws Exception {
       Channel incoming = ctx.channel();
      System.out.println("Client:"+incoming.remoteAddress()+"异常");
        // 当出现异常就关闭连接
        cause.printStackTrace();
        ctx.close();
   }

}
  • HTML文件

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
</head>
<body>
   <script type="text/javascript">
      var socket;
      if (!window.WebSocket) {
         window.WebSocket = window.MozWebSocket;
      }
      if (window.WebSocket) {
         socket = new WebSocket("ws://localhost:8080/ws");
         socket.onmessage = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = ta.value + '\n' + event.data
         };
         socket.onopen = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = "连接开启!";
         };
         socket.onclose = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = ta.value + "连接被关闭";
         };
      } else {
         alert("你的浏览器不支持 WebSocket!");
      }

      function send(message) {
         if (!window.WebSocket) {
            return;
         }
         if (socket.readyState == WebSocket.OPEN) {
            socket.send(message);
         } else {
            alert("连接没有开启.");
         }
      }
   </script>
   <form onsubmit="return false;">
      <h3>WebSocket 聊天室:</h3>
      <textarea id="responseText" style="width: 500px; height: 300px;"></textarea>
      <br> 
      <input type="text" name="message"  style="width: 300px" value="Are you ok?">
      <input type="button" value="发送消息" onclick="send(this.form.message.value)">
      <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录">
   </form>
   <br> 
   <br> 
</body>
</html>

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值