Netty 整合 SpringMVC

1.导入 maven 依赖

  <properties>

    ......

    <!-- spring -->
    <spring.version>5.1.1.RELEASE</spring.version>
    <!-- jackson-json -->
    <jackson.version>2.9.4</jackson.version>
    <!-- log4j -->
    <slf4j.version>1.7.18</slf4j.version>
    <log4j.version>1.2.17</log4j.version>
  </properties>

  <dependencies>
    <!-- spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- Jackson -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!-- AOP -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.6</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.6</version>
    </dependency>

    <!-- 日志相关 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

    <!-- servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <!-- netty -->
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.44.Final</version>
    </dependency>
  </dependencies>

 

 

2.创建 spring.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 自动扫描的包名 -->
    <context:component-scan base-package="com.wode" />

    <!-- 开启AOP代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!--开启注解处理器 -->
    <context:annotation-config />

</beans>

 

 

3.创建 spring-mvc.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    <!-- 开启SpringMVC注解模式 -->
    <mvc:annotation-driven />

    <!-- 扫描web相关的bean -->
    <context:component-scan base-package="com.wode.controller" />

    <!-- 静态资源默认servlet配置 -->
    <mvc:default-servlet-handler/>

</beans>

 

 

4.创建 Spring 管理器

public class SpringManager {
    //单例
    private static SpringManager instance = new SpringManager();

    private ApplicationContext ctx;
    private XmlWebApplicationContext mvcContext;
    private DispatcherServlet dispatcherServlet;

    private SpringManager() {
        ctx = new ClassPathXmlApplicationContext("spring.xml");
        mvcContext = new XmlWebApplicationContext();
        mvcContext.setConfigLocation("classpath:spring-mvc.xml");
        mvcContext.setParent(ctx);
        MockServletConfig servletConfig = new MockServletConfig(mvcContext.getServletContext(), "dispatcherServlet");
        dispatcherServlet = new DispatcherServlet(mvcContext);
        try {
            dispatcherServlet.init(servletConfig);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SpringManager getInstance(){
        return instance;
    }

    public ApplicationContext getSpringContext(){
        return ctx;
    }

    public XmlWebApplicationContext getMvcContext(){
        return mvcContext;
    }

    public DispatcherServlet getDispatcherServlet(){
        return dispatcherServlet;
    }
}

 

 

5.创建 Netty 启动类

public class NettyServer {
    //单例
    private static NettyServer instance = new NettyServer();
    private NettyServer() {}
    private static NettyServer getInstance(){
        return instance;
    }

    public void start(int port) throws Exception {
        //负责接收客户端的连接的线程。线程数设置为1即可,netty处理链接事件默认为单线程,过度设置反而浪费cpu资源
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //负责处理数据传输的工作线程。线程数默认为CPU核心数乘以2
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            //在ServerChannelInitializer中初始化ChannelPipeline责任链,并添加到serverBootstrap中
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel channel) {
                    //添加HTTP编解码
                    channel.pipeline().addLast("decoder", new HttpRequestDecoder());
                    channel.pipeline().addLast("encoder", new HttpResponseEncoder());
                    //消息聚合器,将消息聚合成FullHttpRequest
                    channel.pipeline().addLast("aggregator", new HttpObjectAggregator(1024*1024*5));
                    //支持大文件传输
                    channel.pipeline().addLast("chunked", new ChunkedWriteHandler());
                    //自定义Handler
                    channel.pipeline().addLast("dispatchHandler", new DispatchHandler());
                    channel.pipeline().addLast("httpHandler", new HttpHandler());
                    channel.pipeline().addLast("webSocketHandler", new WebSocketHandler());
                }
            });
            //标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            //Netty4使用对象池,重用缓冲区
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            //是否启用心跳保活机制
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            //禁止使用Nagle算法,便于小数据即时传输
            bootstrap.childOption(ChannelOption.TCP_NODELAY, true);

            //绑定端口后,开启监听
            ChannelFuture future = bootstrap.bind(port).sync();
            future.addListener(f -> {
                if (f.isSuccess()) {
                    System.out.println("服务启动成功");
                } else {
                    System.out.println("服务启动失败");
                }
            });
            //等待服务监听端口关闭
            future.channel().closeFuture().sync();
        } finally {
            //释放资源
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }


    public static void main(String[] args) {
        try {
            SpringManager.getInstance();
            NettyServer.getInstance().start(8080);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

 

6.创建请求分发处理器

public class DispatchHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            //判断是否为websocket握手请求
            if(isWebSocketHandShake(request)) {
                ctx.fireChannelRead(new WebSocketRequestVo(request));
                //Http请求
            }else{
                ctx.fireChannelRead(new HttpRequestVo(request));
            }
            //websocket请求
        } else if (msg instanceof WebSocketFrame) {
            WebSocketFrame frame = (WebSocketFrame) msg;
            ctx.fireChannelRead(new WebSocketRequestVo(frame));
        }
    }

    //判断是否为websocket握手请求
    private boolean isWebSocketHandShake(FullHttpRequest request){
        //1、判断是否为get 2、判断Upgrade头是否包含websocket 3、Connection头是否包含upgrade
        return request.method().equals(HttpMethod.GET)
                && request.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true)
                && request.headers().contains(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true);
    }

}

 

 

7.Http部分

  a.创建 Http 请求 VO 类

public class HttpRequestVo {

    private FullHttpRequest request;

    public HttpRequestVo(FullHttpRequest request) {
        this.request = request;
    }

    public FullHttpRequest getRequest() {
        return request;
    }

    public void setRequest(FullHttpRequest request) {
        this.request = request;
    }
}

 

  b.创建 Http 请求处理器

public class HttpHandler extends SimpleChannelInboundHandler<HttpRequestVo> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpRequestVo requestVo) throws Exception {
        FullHttpRequest nettyRequest = requestVo.getRequest();
        boolean isKeepAlive = HttpUtil.isKeepAlive(nettyRequest);
        MockHttpServletRequest servletRequest = RequestTransUtil.transRequest2Spring(nettyRequest);
        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
        try {
            SpringManager.getInstance().getDispatcherServlet().service(servletRequest, servletResponse);
            FullHttpResponse nettyResponse = RequestTransUtil.transResponse2Netty(servletResponse);
            ResponseUtil.sendHttpResponse(ctx, nettyResponse, isKeepAlive);
        } catch (Exception e) {
            ResponseUtil.sendHttpResponse(ctx, ResponseUtil.get500Response(), false);
        }
    }

}

 

 

8.WebSocket部分

  a.创建 WebSocket 请求 VO 类

public class WebSocketRequestVo {

    //握手请求
    private FullHttpRequest request;
    //websocket请求
    private WebSocketFrame frame;

    public WebSocketRequestVo(FullHttpRequest request) {
        this.request = request;
    }

    public WebSocketRequestVo(WebSocketFrame frame) {
        this.frame = frame;
    }

    public FullHttpRequest getRequest() {
        return request;
    }

    public void setRequest(FullHttpRequest request) {
        this.request = request;
    }

    public WebSocketFrame getFrame() {
        return frame;
    }

    public void setFrame(WebSocketFrame frame) {
        this.frame = frame;
    }
}

 

  b.创建 WebSocket 请求处理器

public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketRequestVo> {

    //Channel属性名称:握手处理器
    private static final AttributeKey<WebSocketServerHandshaker> HAND_SHAKE_ATTR = AttributeKey.valueOf("HAND_SHAKE");

    public static final String WEBSOCKET_ID_ATTR = "wsId";
    public static final String WEBSOCKET_STATE_ATTR = "wsState";
    public static final int TYPE_OPEN = 1;
    public static final int TYPE_CLOSE = 2;
    public static final int TYPE_MSG = 3;

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String id = ctx.channel().id().asShortText();
        WsClinetVo client = WsClientManager.getInstance().getClient(id);
        MockHttpServletRequest servletRequest = RequestTransUtil.transFrame2Spring(null, client.getUrl());
        servletRequest.setAttribute(WEBSOCKET_ID_ATTR, id);
        servletRequest.setAttribute(WEBSOCKET_STATE_ATTR, TYPE_CLOSE);
        SpringManager.getInstance().getDispatcherServlet().service(servletRequest, new MockHttpServletResponse());
        WsClientManager.getInstance().removeClient(id);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketRequestVo requestVo) throws Exception {
        //处理握手
        if(requestVo.getRequest() != null){
            this.handleShake(ctx, requestVo.getRequest());
        }
        //处理websocket数据
        if(requestVo.getFrame() != null){
            this.handleFrame(ctx, requestVo.getFrame());
        }
    }

    //处理握手
    private void handleShake(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        String id = ctx.channel().id().asShortText();
        MockHttpServletRequest servletRequest = RequestTransUtil.transRequest2Spring(request);
        servletRequest.setAttribute(WEBSOCKET_ID_ATTR, id);
        servletRequest.setAttribute(WEBSOCKET_STATE_ATTR, TYPE_OPEN);
        SpringManager.getInstance().getDispatcherServlet().service(servletRequest, new MockHttpServletResponse());
        WsClientManager.getInstance().putClient(id, servletRequest.getRequestURI(), ctx.channel());
        // 握手操作
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(null, null, false);
        WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(request);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), request);
            //绑定属性到channel
            ctx.channel().attr(HAND_SHAKE_ATTR).set(handshaker);
        }
    }

    //处理websocket数据
    private void handleFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // 判断是否关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            WebSocketServerHandshaker handshaker = ctx.channel().attr(HAND_SHAKE_ATTR).get();
            if(handshaker == null){
                ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
                return;
            }
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        // 判断是否ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 暂仅支持文本消息,不支持二进制消息
        if (! (frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException("暂不支持该消息类型:" + frame.getClass().getName());
        }

        // 处理消息
        String id = ctx.channel().id().asShortText();
        WsClinetVo client = WsClientManager.getInstance().getClient(id);
        MockHttpServletRequest servletRequest = RequestTransUtil.transFrame2Spring(frame, client.getUrl());
        servletRequest.setAttribute(WEBSOCKET_ID_ATTR, id);
        servletRequest.setAttribute(WEBSOCKET_STATE_ATTR, TYPE_MSG);
        SpringManager.getInstance().getDispatcherServlet().service(servletRequest, new MockHttpServletResponse());
    }

}

 

  c.创建客户端信息 VO 类

public class WsClinetVo {

    private String wsId;
    private String url;
    private Channel channel;

    public WsClinetVo() {}

    public WsClinetVo(String wsId, String url, Channel channel) {
        this.wsId = wsId;
        this.url = url;
        this.channel = channel;
    }

    public String getWsId() {
        return wsId;
    }

    public void setWsId(String wsId) {
        this.wsId = wsId;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }
}

 

  d.创建客户端信息管理类

public class WsClientManager {

    //单例
    private static WsClientManager instance = new WsClientManager();
    private WsClientManager(){}
    public static WsClientManager getInstance(){
        return instance;
    }

    //socketID与用户信息的对应关系
    private Map<String, WsClinetVo> clientMap = new ConcurrentHashMap<>();

    //添加用户信息
    public void putClient(String id, String url, Channel channel){
        this.clientMap.put(id, new WsClinetVo(id, url, channel));
    }

    //获取用户信息
    public WsClinetVo getClient(String id){
        return this.clientMap.get(id);
    }

    //删除用户信息
    public void removeClient(String id){
        this.clientMap.remove(id);
    }

    //发送消息
    public void sendMsg(String id, String msg){
        TextWebSocketFrame frame = new TextWebSocketFrame(msg);
        WsClinetVo client = clientMap.get(id);
        if(client == null || client.getChannel() == null){
            return;
        }
        client.getChannel().writeAndFlush(frame);
    }

}

 

 

9.创建测试 Controller

@RestController
public class TestController {

    @RequestMapping("/add")
    public int add(int p1, int p2){
        return p1 + p2;
    }

    @RequestMapping("/ws")
    public void socket(HttpServletRequest request){
        String wsId = (String) request.getAttribute(WebSocketHandler.WEBSOCKET_ID_ATTR);
        int wsState = (int) request.getAttribute(WebSocketHandler.WEBSOCKET_STATE_ATTR);
        if(wsState == WebSocketHandler.TYPE_OPEN){
            System.out.println("[" + wsId + "]正在连接,参数:[userId]" + request.getParameter("userId"));
            return;
        }else if(wsState == WebSocketHandler.TYPE_CLOSE){
            System.out.println("[" + wsId + "]已断开");
            return;
        }

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
            StringBuffer buffer = new StringBuffer();
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            System.out.println("[" + wsId + "]接收消息:" + buffer.toString());
            //发送消息
            WsClientManager.getInstance().sendMsg(wsId, "Hello World");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

 

10.创建工具类

  a.创建请求响应转换工具类

public class RequestTransUtil {

    //Netty转Spring请求
    public static MockHttpServletRequest transRequest2Spring(FullHttpRequest nettyRequest){
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(nettyRequest.uri()).build();
        ServletContext servletContext = SpringManager.getInstance().getDispatcherServlet().getServletConfig().getServletContext();

        MockHttpServletRequest servletRequest = new MockHttpServletRequest(servletContext);
        servletRequest.setRequestURI(uriComponents.getPath());
        servletRequest.setPathInfo(uriComponents.getPath());
        servletRequest.setMethod(nettyRequest.method().name());

        if (uriComponents.getScheme() != null) {
            servletRequest.setScheme(uriComponents.getScheme());
        }
        if (uriComponents.getHost() != null) {
            servletRequest.setServerName(uriComponents.getHost());
        }
        if (uriComponents.getPort() != -1) {
            servletRequest.setServerPort(uriComponents.getPort());
        }

        for (String name : nettyRequest.headers().names()) {
            servletRequest.addHeader(name, nettyRequest.headers().get(name));
        }

        ByteBuf content = nettyRequest.content();
        content.readerIndex(0);
        byte[] data = new byte[content.readableBytes()];
        content.readBytes(data);
        servletRequest.setContent(data);

        if (uriComponents.getQuery() != null) {
            String query = UriUtils.decode(uriComponents.getQuery(), "UTF-8");
            servletRequest.setQueryString(query);
        }

        Map<String, String> paramMap = ParamUtil.getRequestParams(nettyRequest);
        if(! CollectionUtils.isEmpty(paramMap)){
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                servletRequest.addParameter(entry.getKey(), entry.getValue());
            }
        }
        return servletRequest;
    }

    //WebSocket转Spring请求
    public static MockHttpServletRequest transFrame2Spring(WebSocketFrame frame, String url){
        ServletContext servletContext = SpringManager.getInstance().getDispatcherServlet().getServletConfig().getServletContext();
        MockHttpServletRequest servletRequest = new MockHttpServletRequest(servletContext);
        servletRequest.setRequestURI(url);
        servletRequest.setPathInfo(url);
        servletRequest.setMethod(HttpMethod.POST.name());
        servletRequest.setContentType(HttpHeaderValues.TEXT_PLAIN.toString());

        if(frame != null){
            ByteBuf content = frame.content();
            content.readerIndex(0);
            byte[] data = new byte[content.readableBytes()];
            content.readBytes(data);
            servletRequest.setContent(data);
        }
        return servletRequest;
    }


    //Spring转Netty响应
    public static FullHttpResponse transResponse2Netty(MockHttpServletResponse servletResponse){
        HttpResponseStatus status = HttpResponseStatus.valueOf(servletResponse.getStatus());
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.wrappedBuffer(servletResponse.getContentAsByteArray()));

        for (String name : servletResponse.getHeaderNames()) {
            for (Object value : servletResponse.getHeaderValues(name)) {
                response.headers().add(name, value);
            }
        }
        return response;
    }

}

 

  b.创建请求参数工具类

public class ParamUtil {

    /**
     * 获取请求参数
     */
    public static Map<String, String> getRequestParams(HttpRequest request){
        Map<String, String>requestParams=new HashMap<>();
        // 处理get请求
        if (request.method() == HttpMethod.GET) {
            QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
            Map<String, List<String>> params = decoder.parameters();
            for(Map.Entry<String, List<String>> entry : params.entrySet()){
                requestParams.put(entry.getKey(), entry.getValue().get(0));
            }
        }
        // 处理POST请求
        if (request.method() == HttpMethod.POST) {
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
            List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();
            for(InterfaceHttpData data : postData){
                if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                    MemoryAttribute attribute = (MemoryAttribute) data;
                    requestParams.put(attribute.getName(), attribute.getValue());
                }
            }
        }
        return requestParams;
    }

}

 

  c.创建响应工具类

public class ResponseUtil {

    /**
     * 获取400响应
     */
    public static FullHttpResponse get400Response(){
        return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
    }

    /**
     * 获取200响应
     */
    public static FullHttpResponse get200Response(String content){
        return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(content.getBytes()));
    }

    /**
     * 获取500响应
     */
    public static FullHttpResponse get500Response(){
        return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer("服务器异常".getBytes()));
    }

    /**
     * 发送HTTP响应
     */
    public static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
        // 返回应答给客户端
        if (response.status().code() != 200) {
            ByteBufUtil.writeUtf8(response.content(), response.status().toString());
        }

        //添加header描述length,避免客户端接收不到数据
        if(StringUtils.isEmpty(response.headers().get(HttpHeaderNames.CONTENT_TYPE))){
            response.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN);
        }
        if(StringUtils.isEmpty(response.headers().get(HttpHeaderNames.CONTENT_LENGTH))){
            response.headers().add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        }

        //解决跨域的问题
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN,"*");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS,"*");//允许headers自定义
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS,"GET, POST, PUT,DELETE");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");

        // 如果是非Keep-Alive,关闭连接
        if (! HttpUtil.isKeepAlive(request) || response.status().code() != 200) {
            response.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
            ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }else{
            response.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
            ctx.channel().writeAndFlush(response);
        }
    }

}

 

11.测试

  a.WebSocket测试:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
    <style type="text/css">
        h1{
            text-align:center;
        }
        .row{
            margin:10px 0px;
        }
        .col{
            display: inline-block;
            margin:0px 5px;
        }
        .msg-container{
            width: 40%;
            height: 500px;
        }
        .msg-div{
            width: 80%;
            margin:0 0 0 10%;
            height: 300px;
            border:solid 1px;
            overflow: scroll;
        }
        .msg-input-wrapper{
            margin: 0 0 0 10%;
        }
    </style>
</head>
<body>
<div>
    <div class="row">
        <div class="col msg-container">
            <div class="row">
                <h1>消息窗口1</h1>
            </div>
            <div class="row">
                <div class="col msg-div"></div>
            </div>
            <div class="row">
                <div class="col msg-input-wrapper">
                    <input class="msg-input" type="text"/>
                </div>
                <div class="col">
                    <button id="send-btn-1">发送</button>
                </div>
            </div>
        </div>

        <div class="col msg-container">
            <div class="row">
                <h1>消息窗口2</h1>
            </div>
            <div class="row">
                <div class="col msg-div"></div>
            </div>
            <div class="row">
                <div class="col msg-input-wrapper">
                    <input class="msg-input" type="text"/>
                </div>
                <div class="col">
                    <button id="send-btn-2">发送</button>
                </div>
            </div>
        </div>
    </div>

</div>
</body>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
    let userIdArray = ["张三", "李四"];
    let wsArray = new Array(2);

    $(function(){
        initWebSocketFunc(userIdArray[0], 0);
        initWebSocketFunc(userIdArray[1], 1);
        $("#send-btn-1").on("click", {num: 0}, sendMsgFunc);
        $("#send-btn-2").on("click",{num: 1}, sendMsgFunc);
    });


    let initWebSocketFunc = function(userId, num){
        // 初始化一个 WebSocket 对象
        let ws = new WebSocket("ws://localhost:8080/ws?userId=" + userId);

        // 建立 web socket 连接成功触发事件
        ws.onopen = function () {
            console.log("正在建立连接...");
        };

        // 接收服务端数据时触发事件
        ws.onmessage = function (evt) {
            let msg = evt.data;
            $(".msg-div:eq(" + num + ")").append("接收:" + msg + "<br/>");
            // $($(".msg-div")[num]).append("接收:" + msg + "<br/>");
        };

        // 断开 web socket 连接成功触发事件
        ws.onclose = function () {
            console.log("连接已关闭...");
        };

        wsArray[num] = ws;
    };

    let sendMsgFunc = function(e){
        let num = e.data.num;
        let msg = $(".msg-input:eq(" + num + ")").val();
        // let msg = $($(".msg-input")[num]).val();
        wsArray[num].send(msg);
        $(".msg-div:eq(" + num + ")").append("发送:" + msg + "<br/>");
    }
</script>
</html>

 

  b.Http测试:访问 http://localhost:8080/add?p1=2&p2=3 测试

 

posted @ 2020-03-16 11:34  晨M风  阅读(2628)  评论(0编辑  收藏  举报