运用场景:与机器设备进行通讯或者其他场景;
pom文件就不上传了,直接上代码,网上都可以找的到 主要是 SpringBoot 和 Netty 的依赖
1.配置类
@Component
@ConfigurationProperties(prefix = "ws")
public class WebSocketConfig {
private int port;
private String host;
private boolean ssl;
private String cert;
private String key;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public boolean getSsl() {
return ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getCert() {
return cert;
}
public void setCert(String cert) {
this.cert = cert;
}
}
2.properties 配置文件
ws.ssl=false
ws.port=8082
ws.host=localhost
3.Action
public class Action {
private Object target;
private Method method;
/**
* @param target
* @param method
*/
public Action(Object target, Method method) {
this.target = target;
this.method = method;
}
public Object call(Object... args) throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
@Override
public String toString() {
return "ActionMethod{" +
"target=" + target +
", method=" + method +
'}';
}
}
4.定义Command注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Command {
int value();
}
5.路由类
public class Router {
private static Map routers = new ConcurrentHashMap();
public static void add(int command, Action action) {
if (routers.containsKey(command)) {
// log.warn("Router: duplicate command " + command);
}
routers.put(command, action);
}
public static void dispatcher(Integer command, ChannelHandlerContext ctx, Object msg) throws Exception {
Action action = routers.get(command);
if (action == null) {
// throw new Exception("Action Not Found: " + command);
// log.warn("Router not found: " + command);
return;
}
action.call(ctx, msg);
}
}
6.Handler扫描器
@Component
public class Scaner implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class extends Object> clazz = bean.getClass();
clazz.getAnnotation(Command.class);
Command command = clazz.getAnnotation(Command.class);
if (command != null) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals("handler")) {
Router.add(command.value(), new Action(bean, method));
}
}
}
return bean;
}
}
7.Netty服务
@Component
public class Server {
@Autowired
WebSocket webSocket;
/**
* 创建bootstrap
*/
private ServerBootstrap serverBootstrap;
/**
* BOSS
*/
private EventLoopGroup bossGroup;
/**
* Worker
*/
private EventLoopGroup workerGroup;
/**
* NETT服务器配置类
*/
@Resource
private WebSocketConfig webSocketConfig;
/**
* 关闭服务器方法
*/
@PreDestroy
public void close() {
// 优雅退出
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
/**
* 开启及服务线程
*/
public void start() throws Exception {
int port = webSocketConfig.getPort();
serverBootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_LINGER, 0)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
//设置事件处理
serverBootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline
.addLast(new IdleStateHandler(50, 50, 50, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(65536))
.addLast(new ChunkedWriteHandler())
//设置websocket路径
.addLast(new WebSocketServerProtocolHandler("/sweeper", null, true))
.addLast(webSocket);
}
});
ChannelFuture f = serverBootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
8.websocket核心类
@Service()
@Scope("prototype")
@ChannelHandler.Sharable
public class WebSocket extends SimpleChannelInboundHandler
9.定义Handler接口
public interface Handler {
public void handler(ChannelHandlerContext ctx, Protocol msg);
}
10.帧
@Command(Protocol.CMD_PING)
public class Ping implements Handler {
public void handler(ChannelHandlerContext ctx, Protocol msg) {
ctx.writeAndFlush(new TextWebSocketFrame(Protocol.PONG));
}
}
11.定制协议
public class Protocol {
public static final int CMD_PING = 0;
public static final int CMD_PONG = 1;
public static final int CMD_TEST = 123;
public static final String PING = "[" + CMD_PING + "]";
public static final String PONG = "[" + CMD_PONG + "]";
public static final int RESPONSE_OK = 0;
public static final int RESPONSE_ERROR = 1;
private Integer cmd;
private String msg;
private JSONArray json;
public Protocol() {
}
public Protocol(String msg) {
this.json = JSON.parseArray(msg);
this.cmd = json.getInteger(0);
this.msg = msg;
}
public Integer getCmd() {
return cmd;
}
public String getMsg() {
return msg;
}
public Object getBody() {
Object body = null;
return body;
}
public static class Response {
private JSONArray jsonArray = new JSONArray();
public Response(int cmd) {
jsonArray.add(0, cmd);
jsonArray.add(1, Protocol.RESPONSE_OK);
}
public Response(int cmd, int error) {
jsonArray.add(0, cmd);
jsonArray.add(1, error);
}
public String toJSONString() {
return jsonArray.toJSONString();
}
}
}
12.启动类
@SpringBootApplication
@EnableAutoConfiguration
@MapperScan("com.xxx.dao")
public class Application implements CommandLineRunner{
Logger Log = LoggerFactory.getLogger(Application.class);
@Autowired
Server server;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
Log.info("-------------------开启websocket----------------------");
server.start();
}
}
开始测试:
websocket在线测试工具
http://www.blue-zero.com/WebSocket/
测试:
@Command(Protocol.CMD_TEST)
public class Test implements Handler{
@Override
public void handler(ChannelHandlerContext ctx, Protocol msg) {
// TODO Auto-generated method stub
ctx.writeAndFlush(new TextWebSocketFrame("[生病][生病][生病][生病]"));
}
}
这里必须要实现Handler接口重写handler方法
测试效果