All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aggrepoint.utils.ws.WebSocketServer Maven / Gradle / Ivy

The newest version!
package com.aggrepoint.utils.ws;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import javax.net.ssl.SSLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.aggrepoint.utils.TriConsumer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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.FullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.CharsetUtil;

public class WebSocketServer {
	private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

	/**
	 * 启动运行WebSocketServer
	 * 
	 * @param certChain
	 *            SSL证书所在路径
	 * @param privateKey
	 *            SSL证书私钥所在路径
	 * @param port
	 *            监听端口
	 * @param webSocketPath
	 *            websocket请求路径
	 * @param httpHandler
	 *            处理HTTP请求
	 * @param wsConnected
	 *            WS连接建立成功回调。返回的对象会被传递给frameReceived和wsDisconnect
	 * @param frameReceived
	 *            接收到WS帧
	 * @param wsDisconnect
	 *            WS断开连接
	 * @return 返回的Runable对象,如果被执行,会停止服务器
	 * @throws SSLException
	 * @throws FileNotFoundException
	 */
	public static  Runnable start(String certChain, String privateKey, int port, String webSocketPath,
			BiConsumer httpHandler, BiFunction wsConnected,
			TriConsumer frameReceived, Consumer wsDisconnect)
			throws SSLException, FileNotFoundException {
		SslContext sslCtx;
		if (!StringUtils.isEmpty(certChain) && !StringUtils.isEmpty(privateKey)) {
			// 指定了证书路径,启用SSL
			sslCtx = SslContextBuilder.forServer(new FileInputStream(certChain), new FileInputStream(privateKey))
					.build();
		} else {
			sslCtx = null;
		}

		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		Runnable stop = () -> {
			if (bossGroup != null)
				try {
					bossGroup.shutdownGracefully();
				} catch (Exception e) {
				}
			if (workerGroup != null)
				try {
					workerGroup.shutdownGracefully();
				} catch (Exception e) {
				}
		};

		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer() {
						@Override
						public void initChannel(SocketChannel ch) throws Exception {
							ChannelPipeline pipeline = ch.pipeline();
							if (sslCtx != null)
								pipeline.addLast(sslCtx.newHandler(ch.alloc()));
							pipeline.addLast(new HttpServerCodec()).addLast(new HttpObjectAggregator(65536))
									.addLast(new WebSocketServerHandler(webSocketPath, httpHandler, wsConnected,
											frameReceived, wsDisconnect));
						}
					});

			Channel ch = b.bind(port).sync().channel();

			ch.closeFuture().addListener(new ChannelFutureListener() {
				public void operationComplete(ChannelFuture future) {
					stop.run();
				}
			});
		} catch (Exception e) {
			logger.error("Error starting", e);
			stop.run();
		}

		return stop;
	}

	/**
	 * 发送HTTP响应
	 * 
	 * @param ctx
	 * @param req
	 * @param res
	 */
	public static void sendHttpResponse(Channel channel, FullHttpRequest req, FullHttpResponse res) {
		// Generate an error page if response getStatus code is not OK (200).
		if (res.status().code() != 200) {
			ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
			HttpUtil.setContentLength(res, res.content().readableBytes());
		}

		// Send the response and close the connection if necessary.
		ChannelFuture f = channel.writeAndFlush(res);
		if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}

	/**
	 * 将BinaryWebSocketFrame转换为byte数组
	 * 
	 * @param bframe
	 * @return
	 */
	public byte[] toBytes(BinaryWebSocketFrame bframe) {
		if (!bframe.isFinalFragment())
			throw new UnsupportedOperationException("Multiple fragments not supported!");

		byte[] bytes = new byte[bframe.content().readableBytes()];
		bframe.content().readBytes(bytes);
		return bytes;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy