当前位置: 首页 > news >正文

网页制作与网站建设期末考试永久免费自助建站软件

网页制作与网站建设期末考试,永久免费自助建站软件,试用体验网站,国外网站域名备案目录1.目的2.Netty是什么2.1.Netty和Tomcat的区别2.2.Netty为什么流行2.2.1并发高2.2.2.传输快2.2.3.封装好3.基于Netty实现WebSocket聊天室3.1创建simple_webchat项目3.2编写代码4.结果1.目的 学习和了解Netty的应用场景和使用方式 2.Netty是什么 Netty 是一个利用 Java 的…

目录

  • 1.目的
  • 2.Netty是什么
    • 2.1.Netty和Tomcat的区别
    • 2.2.Netty为什么流行
      • 2.2.1并发高
      • 2.2.2.传输快
      • 2.2.3.封装好
  • 3.基于Netty实现WebSocket聊天室
    • 3.1创建simple_webchat项目
    • 3.2编写代码
  • 4.结果

1.目的

  • 学习和了解Netty的应用场景和使用方式

2.Netty是什么

  • Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。

2.1.Netty和Tomcat的区别

  • Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的。Netty能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能。

2.2.Netty为什么流行

2.2.1并发高

  • 对比于BIO(Blocking I/O,阻塞IO),Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架。当一个连接建立之后,NIO有两个步骤要做(接收完客户端发过来的全部数据、处理完请求业务之后返回response给客户端),NIO和BIO的区别主要是在第一步。在BIO中,等待客户端发数据这个过程是阻塞的,一个线程只能处理一个请求而最大线程数是有限的,BIO不能支持高并发。
  • 而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接收这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是不阻塞的。

2.2.2.传输快

  • 依赖NIO的一个特性——零拷贝。Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点。针对这种情况,当Netty需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。

2.2.3.封装好

3.基于Netty实现WebSocket聊天室

3.1创建simple_webchat项目

在这里插入图片描述
在这里插入图片描述

3.2编写代码

  • pom.xml
<dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.23</version></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.6.Final</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.7</version></dependency>
</dependencies>
  • Server
public interface Server {void start();void shutdown();
}
  • BaseServer
public abstract class BaseServer implements Server{protected Logger logger = LoggerFactory.getLogger(getClass());protected String host = "localhost";protected int port = 8099;/*** 1. NioEventLoopGroup是用来处理I/O操作的多线程事件循环器,* 在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。* 第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,* 用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上*/protected DefaultEventLoopGroup defLoopGroup;protected NioEventLoopGroup bossGroup;protected NioEventLoopGroup workGroup;protected NioServerSocketChannel ssch;protected ChannelFuture cf;/*** 2.ServerBootstrap是一个启动 NIO 服务的辅助启动类。* 你可以在这个服务中直接使用 Channel*/protected ServerBootstrap b;public void init(){defLoopGroup = new DefaultEventLoopGroup(8, new ThreadFactory() {private AtomicInteger index = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "DEFAULTEVENTLOOPGROUP_" + index.incrementAndGet());}});bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {private AtomicInteger index = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "BOSS_" + index.incrementAndGet());}});workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 10, new ThreadFactory() {private AtomicInteger index = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "WORK_" + index.incrementAndGet());}});b = new ServerBootstrap();}@Overridepublic void shutdown() {if (defLoopGroup != null) {defLoopGroup.shutdownGracefully();}bossGroup.shutdownGracefully();workGroup.shutdownGracefully();}
}
  • UserInfo
public class UserInfo {private static AtomicInteger uidGener = new AtomicInteger(1000);private boolean isAuth = false; // 是否认证private long time = 0;  // 登录时间private int userId;     // UIDprivate String nick;    // 昵称private String addr;    // 地址private Channel channel;// 通道//其他get和set方法直接生成就行public void setUserId() {this.userId = uidGener.incrementAndGet();}
  • MessageHandler
/*** 1.SimpleChannelInboundHandler实现了ChannelInboundHandler接口,*       提供了许多事件处理的接口方法,然后你可以覆盖这些方法*/
public class MessageHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class);@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame)throws Exception {UserInfo userInfo = UserInfoManager.getUserInfo(ctx.channel());if (userInfo != null && userInfo.isAuth()) {JSONObject json = JSONObject.parseObject(frame.text());// 广播返回用户发送的消息文本UserInfoManager.broadcastMess(userInfo.getUserId(), userInfo.getNick(), json.getString("mess"));}}@Overridepublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {UserInfoManager.removeChannel(ctx.channel());UserInfoManager.broadCastInfo(ChatCode.SYS_USER_COUNT,UserInfoManager.getAuthUserCount());super.channelUnregistered(ctx);}
}
  • UserAuthHandler
/*** 1.SimpleChannelInboundHandler实现了ChannelInboundHandler接口,*      提供了许多事件处理的接口方法,然后你可以覆盖这些方法*/
public class UserAuthHandler extends SimpleChannelInboundHandler<Object> {private static final Logger logger = LoggerFactory.getLogger(UserAuthHandler.class);private WebSocketServerHandshaker handshaker;@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof FullHttpRequest) {handleHttpRequest(ctx, (FullHttpRequest) msg);} else if (msg instanceof WebSocketFrame) {handleWebSocket(ctx, (WebSocketFrame) msg);}}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent evnet = (IdleStateEvent) evt;// 判断Channel是否读空闲, 读空闲时移除Channelif (evnet.state().equals(IdleState.READER_IDLE)) {final String remoteAddress = NettyUtil.parseChannelRemoteAddr(ctx.channel());logger.warn("NETTY SERVER PIPELINE: IDLE exception [{}]", remoteAddress);UserInfoManager.removeChannel(ctx.channel());UserInfoManager.broadCastInfo(ChatCode.SYS_USER_COUNT,UserInfoManager.getAuthUserCount());}}ctx.fireUserEventTriggered(evt);}private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {if (!request.decoderResult().isSuccess() || !"websocket".equals(request.headers().get("Upgrade"))) {logger.warn("protobuf don't support websocket");ctx.channel().close();return;}WebSocketServerHandshakerFactory handshakerFactory = new WebSocketServerHandshakerFactory(Constants.WEBSOCKET_URL, null, true);handshaker = handshakerFactory.newHandshaker(request);if (handshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {// 动态加入websocket的编解码处理handshaker.handshake(ctx.channel(), request);UserInfo userInfo = new UserInfo();userInfo.setAddr(NettyUtil.parseChannelRemoteAddr(ctx.channel()));// 存储已经连接的ChannelUserInfoManager.addChannel(ctx.channel());}}private void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame frame) {// 判断是否关闭链路命令if (frame instanceof CloseWebSocketFrame) {handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());UserInfoManager.removeChannel(ctx.channel());return;}// 判断是否Ping消息if (frame instanceof PingWebSocketFrame) {logger.info("ping message:{}", frame.content().retain());ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));return;}// 判断是否Pong消息if (frame instanceof PongWebSocketFrame) {logger.info("pong message:{}", frame.content().retain());ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));return;}// 本程序目前只支持文本消息if (!(frame instanceof TextWebSocketFrame)) {throw new UnsupportedOperationException(frame.getClass().getName() + " frame type not supported");}String message = ((TextWebSocketFrame) frame).text();JSONObject json = JSONObject.parseObject(message);int code = json.getInteger("code");Channel channel = ctx.channel();switch (code) {case ChatCode.PING_CODE:case ChatCode.PONG_CODE:UserInfoManager.updateUserTime(channel);logger.info("receive pong message, address: {}", NettyUtil.parseChannelRemoteAddr(channel));return;case ChatCode.AUTH_CODE:boolean isSuccess = UserInfoManager.saveUser(channel, json.getString("nick"));UserInfoManager.sendInfo(channel,ChatCode.SYS_AUTH_STATE,isSuccess);if (isSuccess) {UserInfoManager.broadCastInfo(ChatCode.SYS_USER_COUNT,UserInfoManager.getAuthUserCount());}return;case ChatCode.MESS_CODE: //普通的消息留给MessageHandler处理break;default:logger.warn("The code [{}] can't be auth!!!", code);return;}//后续消息交给MessageHandler处理ctx.fireChannelRead(frame.retain());}
}
  • UserInfoManager
/*** Channel的管理器*/
public class UserInfoManager {private static final Logger logger = LoggerFactory.getLogger(UserInfoManager.class);private static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);private static ConcurrentMap<Channel, UserInfo> userInfos = new ConcurrentHashMap<>();private static AtomicInteger userCount = new AtomicInteger(0);public static void addChannel(Channel channel) {String remoteAddr = NettyUtil.parseChannelRemoteAddr(channel);if (!channel.isActive()) {logger.error("channel is not active, address: {}", remoteAddr);}UserInfo userInfo = new UserInfo();userInfo.setAddr(remoteAddr);userInfo.setChannel(channel);userInfo.setTime(System.currentTimeMillis());userInfos.put(channel, userInfo);}public static boolean saveUser(Channel channel, String nick) {UserInfo userInfo = userInfos.get(channel);if (userInfo == null) {return false;}if (!channel.isActive()) {logger.error("channel is not active, address: {}, nick: {}", userInfo.getAddr(), nick);return false;}// 增加一个认证用户userCount.incrementAndGet();userInfo.setNick(nick);userInfo.setAuth(true);userInfo.setUserId();userInfo.setTime(System.currentTimeMillis());return true;}/*** 从缓存中移除Channel,并且关闭Channel* @param channel*/public static void removeChannel(Channel channel) {try {logger.warn("channel will be remove, address is :{}", NettyUtil.parseChannelRemoteAddr(channel));rwLock.writeLock().lock();channel.close();UserInfo userInfo = userInfos.get(channel);if (userInfo != null) {UserInfo tmp = userInfos.remove(channel);if (tmp != null && tmp.isAuth()) {// 减去一个认证用户userCount.decrementAndGet();}}} finally {rwLock.writeLock().unlock();}}/*** 广播普通消息** @param message*/public static void broadcastMess(int uid, String nick, String message) {if (!BlankUtil.isBlank(message)) {try {rwLock.readLock().lock();Set<Channel> keySet = userInfos.keySet();for (Channel ch : keySet) {UserInfo userInfo = userInfos.get(ch);if (userInfo == null || !userInfo.isAuth()) continue;ch.writeAndFlush(new TextWebSocketFrame(ChatProto.buildMessProto(uid, nick, message)));}} finally {rwLock.readLock().unlock();}}}/*** 广播系统消息*/public static void broadCastInfo(int code, Object mess) {try {rwLock.readLock().lock();Set<Channel> keySet = userInfos.keySet();for (Channel ch : keySet) {UserInfo userInfo = userInfos.get(ch);if (userInfo == null || !userInfo.isAuth()) continue;ch.writeAndFlush(new TextWebSocketFrame(ChatProto.buildSystProto(code, mess)));}} finally {rwLock.readLock().unlock();}}public static void broadCastPing() {try {rwLock.readLock().lock();logger.info("broadCastPing userCount: {}", userCount.intValue());Set<Channel> keySet = userInfos.keySet();for (Channel ch : keySet) {UserInfo userInfo = userInfos.get(ch);if (userInfo == null || !userInfo.isAuth()) continue;ch.writeAndFlush(new TextWebSocketFrame(ChatProto.buildPingProto()));}} finally {rwLock.readLock().unlock();}}/*** 发送系统消息* @param code* @param mess*/public static void sendInfo(Channel channel, int code, Object mess) {channel.writeAndFlush(new TextWebSocketFrame(ChatProto.buildSystProto(code, mess)));}public static void sendPong(Channel channel) {channel.writeAndFlush(new TextWebSocketFrame(ChatProto.buildPongProto()));}/*** 扫描并关闭失效的Channel*/public static void scanNotActiveChannel() {Set<Channel> keySet = userInfos.keySet();for (Channel ch : keySet) {UserInfo userInfo = userInfos.get(ch);if (userInfo == null) continue;if (!ch.isOpen() || !ch.isActive() || (!userInfo.isAuth() &&(System.currentTimeMillis() - userInfo.getTime()) > 10000)) {removeChannel(ch);}}}public static UserInfo getUserInfo(Channel channel) {return userInfos.get(channel);}public static ConcurrentMap<Channel, UserInfo> getUserInfos() {return userInfos;}public static int getAuthUserCount() {return userCount.get();}public static void updateUserTime(Channel channel) {UserInfo userInfo = getUserInfo(channel);if (userInfo != null) {userInfo.setTime(System.currentTimeMillis());}}
}
  • ChatCode
public class ChatCode {public static final int PING_CODE = 10015;public static final int PONG_CODE = 10016;public static final int AUTH_CODE = 10000;public static final int MESS_CODE = 10086;/*** 系统消息类型*/public static final int SYS_USER_COUNT = 20001; // 在线用户数public static final int SYS_AUTH_STATE = 20002; // 认证结果public static final int SYS_OTHER_INFO = 20003; // 系统消息
}
  • ChatProto
public class ChatProto {public static final int PING_PROTO = 1 << 8 | 220; //ping消息public static final int PONG_PROTO = 2 << 8 | 220; //pong消息public static final int SYST_PROTO = 3 << 8 | 220; //系统消息public static final int EROR_PROTO = 4 << 8 | 220; //错误消息public static final int AUTH_PROTO = 5 << 8 | 220; //认证消息public static final int MESS_PROTO = 6 << 8 | 220; //普通消息private int version = 1;private int uri;private String body;private Map<String,Object> extend = new HashMap<>();public ChatProto(int head, String body) {this.uri = head;this.body = body;}public static String buildPingProto() {return buildProto(PING_PROTO, null);}public static String buildPongProto() {return buildProto(PONG_PROTO, null);}public static String buildSystProto(int code, Object mess) {ChatProto chatProto = new ChatProto(SYST_PROTO, null);chatProto.extend.put("code", code);chatProto.extend.put("mess", mess);return JSONObject.toJSONString(chatProto);}public static String buildAuthProto(boolean isSuccess) {ChatProto chatProto = new ChatProto(AUTH_PROTO, null);chatProto.extend.put("isSuccess", isSuccess);return JSONObject.toJSONString(chatProto);}public static String buildErorProto(int code,String mess) {ChatProto chatProto = new ChatProto(EROR_PROTO, null);chatProto.extend.put("code", code);chatProto.extend.put("mess", mess);return JSONObject.toJSONString(chatProto);}public static String buildMessProto(int uid,String nick, String mess) {ChatProto chatProto = new ChatProto(MESS_PROTO, mess);chatProto.extend.put("uid", uid);chatProto.extend.put("nick", nick);chatProto.extend.put("time", DateTimeUtil.getCurrentTime());return JSONObject.toJSONString(chatProto);}public static String buildProto(int head, String body) {ChatProto chatProto = new ChatProto(head, body);return JSONObject.toJSONString(chatProto);}public int getUri() {return uri;}public void setUri(int uri) {this.uri = uri;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}public int getVersion() {return version;}public void setVersion(int version) {this.version = version;}public Map<String, Object> getExtend() {return extend;}public void setExtend(Map<String, Object> extend) {this.extend = extend;}
}
  • SimpleWebChatServer
public class SimpleWebChatServer extends BaseServer {private ScheduledExecutorService executorService;public SimpleWebChatServer(int port) {this.port = port;// 创建一个定长线程池executorService = Executors.newScheduledThreadPool(2);}@Overridepublic void start() {b.group(bossGroup, workGroup);// 设置初始化的主从"线程池"b.channel(NioServerSocketChannel.class);//3.接收进来的连接,由于是服务端,故而是NioServerSocketChannelb.option(ChannelOption.SO_KEEPALIVE, true);//4提供给NioServerSocketChannel用来接收进来的连接b.option(ChannelOption.TCP_NODELAY, true);b.option(ChannelOption.SO_BACKLOG, 1024);//5设置Channel实现的配置参数b.localAddress(new InetSocketAddress(port));b.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {//6帮助使用者配置一个新的 Channelch.pipeline().addLast(defLoopGroup,new HttpServerCodec(),   //请求解码器new HttpObjectAggregator(65536),//将多个消息转换成单一的消息对象new ChunkedWriteHandler(),  //支持异步发送大的码流,一般用于发送文件流new IdleStateHandler(60, 0, 0), //检测链路是否读空闲new UserAuthHandler(), //处理握手和认证new MessageHandler()    //处理消息的发送);}});try {// 绑定端口,开始接收进来的连接cf = b.bind().sync();InetSocketAddress addr = (InetSocketAddress) cf.channel().localAddress();logger.info("WebSocketServer start success, port is:{}", addr.getPort());// 定时扫描所有的Channel,关闭失效的ChannelexecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {logger.info("scanNotActiveChannel --------");UserInfoManager.scanNotActiveChannel();}}, 3, 60, TimeUnit.SECONDS);// 定时向所有客户端发送Ping消息executorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {UserInfoManager.broadCastPing();}}, 3, 50, TimeUnit.SECONDS);} catch (InterruptedException e) {logger.error("WebSocketServer start fail,", e);}}@Overridepublic void shutdown() {if (executorService != null) {executorService.shutdown();}super.shutdown();}
}
  • BlankUtil
public class BlankUtil {/*** 判断字符串是否为空*/public static boolean isBlank(final String str) {return (str == null) || (str.trim().length() <= 0);}/*** 判断字符是否为空* @param cha* @return*/public static boolean isBlank(final Character cha) {return (cha == null) || cha.equals(' ');}/*** 判断对象是否为空*/public static boolean isBlank(final Object obj) {return (obj == null);}/*** 判断数组是否为空* @param objs* @return*/public static boolean isBlank(final Object[] objs) {return (objs == null) || (objs.length <= 0);}/*** 判断Collectionj是否为空* @param obj* @return*/public static boolean isBlank(final Collection<?> obj) {return (obj == null) || (obj.size() <= 0);}/*** 判断Set是否为空* @param obj* @return*/public static boolean isBlank(final Set<?> obj) {return (obj == null) || (obj.size() <= 0);}public static boolean isBlank(Integer i) {return i == null || i < 1;}/*** 判断Serializable是否为空* @param obj* @return*/public static boolean isBlank(final Serializable obj) {return obj == null;}/*** 判断Map是否为空* @param obj* @return*/public static boolean isBlank(final Map<?, ?> obj) {return (obj == null) || (obj.size() <= 0);}
}
  • Constants
public class Constants {public static String DEFAULT_HOST = "localhost";public static int DEFAULT_PORT = 9688;public static String WEBSOCKET_URL = "ws://localhost:8099/websocket";
}
  • DateTimeUtil
public class DateTimeUtil {private static final Logger logger = LoggerFactory.getLogger(DateTimeUtil.class);private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss";/*** 获取当天的字符串* @return*/public static String getTodayStr(){SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");return sdf.format(new Date());}public static String getTodayStr2(){SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd");return sdf.format(new Date());}public static String getCurrentTime(){return getCurrentTime(DEFAULT_TIME_PATTERN);}/*** 获取当前时间的字符串* @return*/public static String getCurrentDateTime(){return getCurrentTime(DEFAULT_DATE_PATTERN);}/*** 获取当前时间的字符串* @param format 字符串格式,如:yy-MM-dd HH:mm:ss* @return*/public static String getCurrentTime(String format){SimpleDateFormat sdf = new SimpleDateFormat(format);Timestamp timestamp = new Timestamp(System.currentTimeMillis());return sdf.format(timestamp);}/*** 获取当前的月份* @return*/public static String getCurrentMonth(){SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");return sdf.format(new Date());}/*** 比较两个时间,如果返回大于0,time1大于time2,如果返回-1,time1小于time2,返回0则相等* @param time1* @param time2* @return* @throws ParseException*/public static int compareTime(String time1,String time2) throws ParseException{SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_PATTERN);Date date1 = sdf.parse(time1);Date date2 = sdf.parse(time2);long result = date1.getTime() - date2.getTime();if(result > 0){return 1;}else if(result==0){return 0;}else{return -1;}}/*** 转换字符串成日期对象* @param dateStr 日期字符串* @param format 格式,如:yy-MM-dd HH:mm:ss* @return*/public static Date convertStrToDate(String dateStr,String format){if(!BlankUtil.isBlank(dateStr)&&!BlankUtil.isBlank(format)){try{SimpleDateFormat sdf = new SimpleDateFormat(format);return sdf.parse(dateStr);}catch (Exception e) {logger.warn("convertDate fail, date is "+ dateStr, e);}}return null;}/*** 把字符串日期转换成另一种格式* @param dateStr 字符串日期* @param format 转换日期格式* @param otherFormat 转换日期格式* @return*/public static String convertDate(String dateStr,String format,String otherFormat){try{Date date = convertStrToDate(dateStr, format);SimpleDateFormat sdf = new SimpleDateFormat(otherFormat);return sdf.format(date);}catch (Exception e) {logger.warn("convertDate fail, date is "+ dateStr, e);}return null;}/*** 把字符串日期转换成另一种格式* @param dateStr 字符串日期* @param format 转换格式* @return*/public static String convertDate(String dateStr,String format){return convertDate(dateStr, DEFAULT_DATE_PATTERN,format);}
  • NettyUtil
public class NettyUtil {/*** 获取Channel的远程IP地址* @param channel* @return*/public static String parseChannelRemoteAddr(final Channel channel) {if (null == channel) {return "";}SocketAddress remote = channel.remoteAddress();final String addr = remote != null ? remote.toString() : "";if (addr.length() > 0) {int index = addr.lastIndexOf("/");if (index >= 0) {return addr.substring(index + 1);}return addr;}return "";}
}
  • SimpleWebChatApplication
public class SimpleWebChatApplication {private static final Logger logger = LoggerFactory.getLogger(SimpleWebChatApplication.class);public static void main(String[] args) {final SimpleWebChatServer server = new SimpleWebChatServer(Constants.DEFAULT_PORT);server.init();server.start();//注册进程钩子,在JVM进程关闭前释放资源Runtime.getRuntime().addShutdownHook(new Thread(){@Overridepublic void run(){server.shutdown();logger.warn(">>>>>>>>>> jvm shutdown");System.exit(0);}});}
}
  • resources下载地址。提取码:ymk2

4.结果

在这里插入图片描述

http://www.lbrq.cn/news/2399203.html

相关文章:

  • 做网站需要注册公司同城引流用什么软件
  • 域名注册网站系统seo技术交流
  • 杨浦建设交通管理委员会网站百度seo关键词怎么做
  • flashfxp怎么做网站必应搜索国际版
  • 网站建设程序做哪些百度官方网页版
  • 网站做支付借口多少钱免费优化网站
  • 建筑建设网站企业关键词优化推荐
  • 电商网站建设应用苏州seo服务
  • 什么网站可以做字体效果好金华关键词优化平台
  • 企业网站建设推广方案怎么写企业网站推广的形式有
  • 呼伦贝尔做网站公司线上线下推广方案
  • 个人做外贸商城网站企业网站的推广阶段
  • 阿里妈妈网站怎么做搜索引擎优化关键词
  • 烟台网站制作专业上海全网推广
  • 做刷赞网站能赚钱吗焦作seo公司
  • 免费做视频网站宁波seo的公司联系方式
  • 网站上内容列表怎么做东莞网络推广培训
  • 制作网页要钱seo站长之家
  • 内存数据库 网站开发网络营销方式方法
  • 怎么修改网站后台路径惠州网站关键词排名
  • 怎么做盗号网站手机腾讯效果推广
  • 建立网站做家教辅导国外网页模板
  • 遵义市建设局网站官网搜索引擎优化的基本手段
  • 台州网站建设费用网络平台的推广方法
  • 企业网站建设的层次怎么用模板做网站
  • 建立微信商城网站互动营销案例都有哪些
  • xp系统做网站服务器2022年近期重大新闻事件
  • 深圳做网站的公司 cheungdom网页设计框架
  • 爱做的小说网站南京seo推广公司
  • 怎么做网站卡盟seo用什么工具
  • OPC UA, CAN, PROFINET, SOCKET, MODBUS, HTTP, S7七种物联网常用协议解释
  • Kubernetes中为ELK组件配置持久化存储
  • 综合实验--eNSP实验
  • 让Logo/文字“自己画自己”!✨
  • 一个基于阿里云的C端Java服务的整体项目架构
  • ChatGPT Agent深度解析:告别单纯问答,一个指令搞定复杂任务?