学习Netty
1
pom.xml
|
|
@ -338,6 +338,7 @@
|
|||
<module>ruoyi-api</module>
|
||||
<module>ruoyi-common</module>
|
||||
<module>xjs-business</module>
|
||||
<module>xjs-study</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
<module>xjs-business-webmagic</module>
|
||||
<module>xjs-business-blog</module>
|
||||
<module>xjs-business-srb</module>
|
||||
<module>xjs-learn</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>netty-project</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>Netty聊天室网页版</name>
|
||||
|
||||
<groupId>com.xjs</groupId>
|
||||
<artifactId>netty-springboot</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--整合web模块-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--整合模板引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.lagou;
|
||||
|
||||
import com.lagou.netty.NettyWebSocketServer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class NettySpringbootApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private NettyWebSocketServer webSocketServer;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NettySpringbootApplication.class, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
//启动Netty服务端
|
||||
new Thread(webSocketServer).start();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.lagou.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "netty")
|
||||
@Data
|
||||
public class NettyConfig {
|
||||
|
||||
//netty监听的端口
|
||||
private int port ;
|
||||
|
||||
//websocket访问路径
|
||||
private String path;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.lagou.controller;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class ChatController {
|
||||
|
||||
@RequestMapping("/")
|
||||
public String chat() {
|
||||
return "chat";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.lagou.netty;
|
||||
|
||||
import com.lagou.config.NettyConfig;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
/**
|
||||
* Netty服务器
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
@Component
|
||||
public class NettyWebSocketServer implements Runnable {
|
||||
|
||||
@Autowired
|
||||
private NettyConfig nettyProperties;
|
||||
|
||||
@Autowired
|
||||
private WebSocketChannelInit webSocketChannelInit;
|
||||
|
||||
private EventLoopGroup boosGroup = new NioEventLoopGroup(1);
|
||||
|
||||
private EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
|
||||
/**
|
||||
* 资源关闭---在容器销毁时关闭
|
||||
*/
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
boosGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
//1、创建服务端启动助手
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
|
||||
//2、设置线程组
|
||||
bootstrap.group(boosGroup, workerGroup);
|
||||
|
||||
//3、设置参数
|
||||
bootstrap.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.DEBUG))
|
||||
.childHandler(webSocketChannelInit);
|
||||
|
||||
//4、启动
|
||||
ChannelFuture future = bootstrap.bind(nettyProperties.getPort()).sync();
|
||||
|
||||
System.out.println("---Netty服务端启动成功---");
|
||||
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
boosGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.lagou.netty;
|
||||
|
||||
import com.lagou.config.NettyConfig;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
@Component
|
||||
public class WebSocketChannelInit extends ChannelInitializer {
|
||||
|
||||
@Autowired
|
||||
private NettyConfig nettyConfig;
|
||||
|
||||
@Autowired
|
||||
private WebSocketHandler webSocketHandler;
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
//对http协议支持
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
|
||||
//对大数据流支持
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
|
||||
//post请求分三部分、request line / request header / message body
|
||||
pipeline.addLast(new HttpObjectAggregator(8000));
|
||||
|
||||
//将http协议升级为ws协议 websocket支持
|
||||
pipeline.addLast(new WebSocketServerProtocolHandler(nettyConfig.getPath()));
|
||||
|
||||
pipeline.addLast(webSocketHandler);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.lagou.netty;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义处理类
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
@Component
|
||||
@ChannelHandler.Sharable
|
||||
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
||||
|
||||
|
||||
private static List<Channel> channelList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 读就绪事件
|
||||
* @param ctx
|
||||
* @param msg websocket数据是帧的形式处理
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
|
||||
|
||||
System.out.println("msg="+msg.text());
|
||||
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
for (Channel channel1 : channelList) {
|
||||
//排除自身通道
|
||||
if (channel != channel1) {
|
||||
channel1.writeAndFlush(new TextWebSocketFrame(msg.text() ));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道未就绪 --channel下线
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
//当有客户端断开连接的时候,就移出对应的通道
|
||||
channelList.remove(channel);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道就绪事件
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
//当有新的客户端连接的时候,将通道放入集合
|
||||
channelList.add(channel);
|
||||
System.out.println("[Server]:"+channel.remoteAddress().toString().substring(1)+"在线");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理事件
|
||||
* @param ctx
|
||||
* @param cause
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
Channel channel = ctx.channel();
|
||||
System.out.println("[Server]:"+channel.remoteAddress().toString().substring(1)+"异常");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
server:
|
||||
port: 8070
|
||||
|
||||
netty:
|
||||
port: 8071
|
||||
path: /chat
|
||||
|
||||
resources:
|
||||
static-locations:
|
||||
- classpath:/static/
|
||||
spring:
|
||||
thymeleaf:
|
||||
cache: false
|
||||
checktemplatelocation: true
|
||||
enabled: true
|
||||
encoding: UTF-8
|
||||
mode: HTML5
|
||||
prefix: classpath:/templates/
|
||||
suffix: .html
|
||||
|
|
@ -0,0 +1,454 @@
|
|||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input {
|
||||
background: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border: none;
|
||||
}
|
||||
|
||||
body,
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #323232;
|
||||
font: 14px/1.4em 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif;
|
||||
background: #f5f5f5 url('img/bg.jpg') no-repeat center;
|
||||
background-size: cover;
|
||||
font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#chat {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#chat .sidebar {
|
||||
float: left;
|
||||
color: #f4f4f4;
|
||||
background-color: #2e3238;
|
||||
}
|
||||
|
||||
.m-card {
|
||||
padding: 9pt;
|
||||
border-bottom: 1px solid #24272c;
|
||||
height: 65px;
|
||||
}
|
||||
.m-card header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.m-card .avatar {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.m-card .avatar,
|
||||
.m-card .name {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.m-card .name {
|
||||
display: inline-block;
|
||||
margin: 0 0 0 15px;
|
||||
font-size: 1pc;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.m-list {
|
||||
height: 535px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.m-list li {
|
||||
padding: 9pt 15px;
|
||||
border-bottom: 1px solid #292c33;
|
||||
cursor: pointer;
|
||||
-webkit-transition: background-color .1s;
|
||||
transition: background-color .1s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.m-list .avatar {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.m-list .avatar,
|
||||
.m-list .name {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.m-list .name {
|
||||
display: inline-block;
|
||||
margin: 0 0 0 15px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.m-list li.active {
|
||||
background-color: hsla(0, 0%, 100%, .1);
|
||||
}
|
||||
|
||||
#chat .main {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #eee;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#chat .main,
|
||||
#chat .sidebar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#chat .m-message {
|
||||
height: calc(100% - 10pc);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.m-message {
|
||||
padding: 10px 15px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.m-message .pager_container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.m-message .mask {
|
||||
position: absolute;
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.m-message .self {
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.m-message .self .avatar {
|
||||
float: right;
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
.m-message .self .text {
|
||||
background-color: rgb(155, 255, 101);
|
||||
}
|
||||
|
||||
.m-message li {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.m-message .time {
|
||||
margin: 7px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.m-message .time > span {
|
||||
display: inline-block;
|
||||
padding: 0 18px;
|
||||
font-size: 9pt;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
background-color: #dcdcdc;
|
||||
}
|
||||
|
||||
.m-message .avatar {
|
||||
float: left;
|
||||
margin: 10px 10px 0 0;
|
||||
border-radius: 3px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.m-message .text {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 5px 10px;
|
||||
min-height: 30px;
|
||||
line-height: 2;
|
||||
font-size: 9pt;
|
||||
text-align: left;
|
||||
word-break: break-all;
|
||||
background-color: #fafafa;
|
||||
border-radius: 4px;
|
||||
max-width: 500px
|
||||
}
|
||||
|
||||
.m-message .text:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 100%;
|
||||
border: 6px solid transparent;
|
||||
border-right-color: #fafafa;
|
||||
}
|
||||
|
||||
.m-message .user_name {
|
||||
height: 15px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#chat .m-text {
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 160px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.m-message .self .text:before {
|
||||
right: inherit;
|
||||
left: 100%;
|
||||
border-right-color: transparent;
|
||||
border-left-color: rgb(155, 255, 101);
|
||||
}
|
||||
|
||||
.m-text .handler {
|
||||
padding-left: 20px;
|
||||
height: 30px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.m-text .handler img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.m-text textarea {
|
||||
padding: 10px 10px 20px 10px;
|
||||
height: calc(100% - 70px);
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: 0;
|
||||
font-family: Micrsofot Yahei;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.m-text .pager_btn {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.m-text button {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.m-text .arrow_box {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
padding: 0 5px;
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
right: 15px;
|
||||
border: 1px solid #666;
|
||||
border-radius: 3px;
|
||||
box-sizing: content-box;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.m-text .arrow_box .arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 10px solid #666;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -10px;
|
||||
}
|
||||
|
||||
|
||||
.receive_list {
|
||||
height: 180px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#pager_result > div {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#pager_result .top img {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#already_receive_top {
|
||||
border-bottom: 1px solid rgb(233, 230, 230);
|
||||
line-height: 40px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.receive_list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.receive_list li img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
vertical-align: center;
|
||||
margin-right: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.receive_list li div {
|
||||
flex: 1;
|
||||
border-bottom: 1px solid rgb(233, 230, 230);
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#receive_time {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.receive_list li div p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.text_right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.flex_between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 320px) and (max-width: 640px) {
|
||||
#chat {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 320px;
|
||||
}
|
||||
.m-card .name,.m-list .name {
|
||||
margin: 0 0 0 5px!important;
|
||||
|
||||
}
|
||||
#chat .main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#chat .sidebar {
|
||||
width: 100px;
|
||||
}
|
||||
.m-card {
|
||||
padding: 5px;
|
||||
height: 40px;
|
||||
}
|
||||
.m-card .avatar {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
#user_list li .avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.pager_result #pager_result, .m-pager #pager {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -150px;
|
||||
margin-top: -250px;
|
||||
}
|
||||
.get_pager #get_pager {
|
||||
margin-left: -100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 640px)and (max-width: 992px) {
|
||||
#chat {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#chat .main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#chat .sidebar {
|
||||
width: 150px;
|
||||
}
|
||||
.pager_result #pager_result, .m-pager #pager {
|
||||
margin-top: -250px;
|
||||
}
|
||||
.get_pager #get_pager {
|
||||
margin-left: -50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 992px) {
|
||||
#chat {
|
||||
margin: 20px auto;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
#chat .main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#chat .sidebar {
|
||||
width: 200px;
|
||||
}
|
||||
.pager_result #pager_result, .m-pager #pager {
|
||||
margin-top: -300px;
|
||||
}
|
||||
.get_pager #get_pager {
|
||||
margin-left: -50px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609313472456" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4256" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M625.24295044 507.72109985l-330.26715088 330.20782471a45.140625 45.140625 0 1 0 63.94372559 63.88357544l362.21017456-362.08905029a44.96017456 44.96017456 0 0 0 0-64.06484986L358.91952515 113.62887573a45.140625 45.140625 0 1 0-63.88357544 63.88439942l330.20782471 330.26797485z" fill="" p-id="4257"></path></svg>
|
||||
|
After Width: | Height: | Size: 688 B |
|
After Width: | Height: | Size: 41 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609320928124" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7496" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 1016C233.6 1016 8 790.4 8 512S233.6 8 512 8s504 225.6 504 504-225.6 504-504 504z m0-960C260.8 56 56 260.8 56 512s204.8 456 456 456 456-204.8 456-456S763.2 56 512 56z" p-id="7497" fill="#f2bd6f"></path><path d="M548.8 512L744 316.8c9.6-9.6 9.6-25.6 0-36.8l-1.6-1.6c-9.6-9.6-25.6-9.6-35.2 0L512 475.2 316.8 280c-9.6-9.6-25.6-9.6-35.2 0l-1.6 1.6c-9.6 9.6-9.6 25.6 0 36.8L475.2 512 280 707.2c-9.6 9.6-9.6 25.6 0 36.8l1.6 1.6c9.6 9.6 25.6 9.6 35.2 0L512 548.8 707.2 744c9.6 9.6 25.6 9.6 35.2 0l1.6-1.6c9.6-9.6 9.6-25.6 0-36.8L548.8 512z" p-id="7498" fill="#f2bd6f"></path></svg>
|
||||
|
After Width: | Height: | Size: 955 B |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609311315026" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2543" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M655.36 637.44c17.92 0 30.72-15.36 30.72-30.72 0-17.92-12.8-33.28-30.72-33.28h-84.48l84.48-133.12c10.24-15.36 5.12-35.84-7.68-43.52-15.36-10.24-33.28-5.12-43.52 7.68l-89.6 140.8-89.6-140.8c-10.24-15.36-28.16-17.92-43.52-7.68-15.36 10.24-17.92 28.16-7.68 43.52l84.48 133.12h-81.92c-17.92 0-30.72 15.36-30.72 33.28 0 17.92 12.8 30.72 30.72 30.72h107.52V691.2h-107.52c-17.92 0-30.72 15.36-30.72 33.28s12.8 30.72 30.72 30.72h107.52v81.92c0 17.92 12.8 30.72 30.72 30.72s30.72-15.36 30.72-30.72v-81.92h107.52c17.92 0 30.72-12.8 30.72-30.72s-12.8-33.28-28.16-33.28h-107.52v-53.76h107.52z" p-id="2544"></path><path d="M729.6 0h-435.2C186.88 0 97.28 87.04 97.28 194.56V819.2c0 135.68 64 204.8 192 204.8h442.88c128 0 192-69.12 192-204.8V194.56c2.56-107.52-84.48-194.56-194.56-194.56z m135.68 819.2c0 99.84-38.4 140.8-130.56 140.8H289.28c-94.72 0-130.56-38.4-130.56-140.8V194.56c0-71.68 58.88-130.56 135.68-130.56h435.2c74.24 0 135.68 58.88 135.68 130.56V819.2z" p-id="2545"></path><path d="M734.72 163.84c-209.92 117.76-437.76 0-437.76 0-15.36-7.68-33.28-2.56-40.96 12.8-7.68 15.36-2.56 35.84 12.8 43.52 5.12 5.12 115.2 61.44 258.56 61.44 74.24 0 156.16-15.36 238.08-61.44 15.36-7.68 20.48-28.16 12.8-43.52-10.24-15.36-28.16-20.48-43.52-12.8z" p-id="2546"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609313382203" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3518" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M511.2 949.8c-59.6 0-117.5-11.7-171.9-34.7-52.7-22.3-99.9-54.1-140.6-94.8-40.6-40.6-72.5-87.9-94.8-140.5-23.1-54.5-34.8-112.4-34.8-172.1S80.9 390.1 104 335.6c22.3-52.7 54.2-99.9 94.8-140.6 40.6-40.6 87.9-72.5 140.6-94.8 54.5-23.1 112.4-34.8 172.1-34.8 59.7 0 117.6 11.7 172.1 34.8 52.7 22.3 99.9 54.2 140.6 94.8s72.5 87.9 94.8 140.6c23.1 54.5 34.8 112.4 34.8 172.1 0 84-23.6 165.7-68.3 236.2-4.7 7.5-14.6 9.7-22.1 5-7.5-4.7-9.7-14.6-5-22.1 41.4-65.4 63.3-141.1 63.3-219 0-55.4-10.8-109.1-32.2-159.7-20.7-48.8-50.2-92.7-87.9-130.4s-81.5-67.3-130.4-87.9c-50.6-21.4-104.3-32.2-159.7-32.2-55.4 0-109.1 10.8-159.7 32.2-48.8 20.7-92.7 50.3-130.4 87.9-37.7 37.7-67.3 81.6-87.9 130.4-21.4 50.6-32.2 104.3-32.2 159.6 0 55.4 10.8 109.1 32.2 159.6 20.7 48.8 50.2 92.7 87.9 130.4s81.5 67.3 130.4 87.9c50.5 21.4 104.2 32.2 159.5 32.2h0.2c47.8 0 94.6-8.1 139.1-24.2 43-15.5 82.9-38 118.6-66.9 6.9-5.6 16.9-4.5 22.5 2.4 5.6 6.9 4.5 16.9-2.4 22.5-38.5 31.1-81.6 55.4-127.9 72.1-48 17.3-98.4 26-149.9 26-0.2 0.1-0.2 0.1-0.3 0.1z" fill="" p-id="3519"></path><path d="M360.9 433.9m-50 0a50 50 0 1 0 100 0 50 50 0 1 0-100 0Z" fill="" p-id="3520"></path><path d="M659.4 433.9m-50 0a50 50 0 1 0 100 0 50 50 0 1 0-100 0Z" fill="" p-id="3521"></path><path d="M518.4 745.1c-49.5 0-98.5-18.1-142-52.5-33-26.1-51.2-52.3-51.9-53.4-5-7.3-3.2-17.2 4.1-22.3 7.3-5 17.2-3.2 22.2 4.1 0.2 0.2 16.8 23.9 46.2 47.1 38.6 30.3 80 45.5 123 45h0.2c0.2 0 26.8-0.3 59.3-11.8 42.7-15.2 75.2-41.8 96.7-79.1 4.4-7.7 14.2-10.3 21.8-5.9 7.7 4.4 10.3 14.2 5.9 21.8-25.2 43.9-65 76.3-114.9 93.7-36.8 12.9-66.5 13.3-68.8 13.3h-1.8z" fill="" p-id="3522"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609313797105" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5118" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M562.048 365.824h124.928v155.52h-124.928z" fill="#F4BD50" p-id="5119"></path><path d="M858.88 32.256H165.12c-69.76 0-126.976 57.088-126.976 126.976v693.76c0 69.76 57.088 126.976 126.976 126.976h693.76c69.76 0 126.976-57.088 126.976-126.976v-693.76c0-69.888-57.088-126.976-126.976-126.976zM525.824 171.392c27.776 33.28 52.736 64.896 75.008 94.464l-63.872 33.28c-20.352-31.488-43.52-62.976-69.376-94.464l58.24-33.28zM264.832 826.88c-16.64 1.792-38.912 2.816-66.688 2.816L184.32 760.192h50.048c22.272 1.792 32.384-8.32 30.592-30.592V574.208l-83.328 24.96-5.504-80.512c7.424-1.792 20.352-4.608 38.912-8.32 22.272-5.504 38.912-9.216 50.048-11.136V360.32h-77.824v-66.688h77.824v-116.608h69.376v116.608H398.08v66.688h-63.872v119.424c22.272-5.504 44.416-11.136 66.688-16.64v72.192l-66.688 19.456v197.248c1.92 49.92-21.248 74.88-69.376 74.88z m583.168-238.848h-88.832v244.352h-72.192V588.032h-127.744c-1.92 98.176-42.624 182.4-122.24 252.672-7.424-7.424-20.352-19.456-38.912-36.096-9.344-7.424-15.744-12.928-19.456-16.64 68.48-53.632 104.576-120.32 108.288-199.936h-80.512v-66.688h83.328V365.824h-61.056v-63.872h225.024c31.488-51.84 56.448-95.36 75.008-130.56l69.376 27.776c-9.344 11.136-22.272 29.696-38.912 55.552-12.928 20.48-23.168 36.096-30.592 47.232h102.784v63.872h-72.192v155.52h88.832v66.688z" fill="#F4BD50" p-id="5120"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 20 KiB |
7
xjs-study/netty-project/netty-springboot/src/main/resources/static/js/bootstrap.min.js
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
$(function () {
|
||||
//这里需要注意的是,prompt有两个参数,前面是提示的话,后面是当对话框出来后,在对话框里的默认值
|
||||
var username = "";
|
||||
while (true) {
|
||||
//弹出一个输入框,输入一段文字,可以提交
|
||||
username = prompt("请输入您的名字", ""); //将输入的内容赋给变量 name ,
|
||||
if (username.trim() === "")//如果返回的有内容
|
||||
{
|
||||
alert("名称不能输入空")
|
||||
} else {
|
||||
$("#username").text(username);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let ws = new WebSocket("ws://localhost:8071/chat");
|
||||
|
||||
ws.onopen = function (){
|
||||
console.log("连接成功...")
|
||||
}
|
||||
|
||||
ws.onmessage = function (evt){
|
||||
showMessage(evt.data)
|
||||
}
|
||||
|
||||
ws.onerror =function (){
|
||||
console.log("连接异常")
|
||||
}
|
||||
|
||||
ws.onclose = function (){
|
||||
console.log("连接异常")
|
||||
}
|
||||
|
||||
|
||||
function showMessage(message) {
|
||||
|
||||
let str = message.split(":");
|
||||
|
||||
|
||||
$("#msg_list").append(`<li class="active"}>
|
||||
<div class="main">
|
||||
<img class="avatar" width="30" height="30" src="/img/user.png">
|
||||
<div>
|
||||
<div class="user_name">${str[0]}</div>
|
||||
<div class="text">${str[1]}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>`);
|
||||
// 置底
|
||||
setBottom();
|
||||
}
|
||||
|
||||
$('#my_test').bind({
|
||||
focus: function (event) {
|
||||
event.stopPropagation()
|
||||
$('#my_test').val('');
|
||||
$('.arrow_box').hide()
|
||||
},
|
||||
keydown: function (event) {
|
||||
event.stopPropagation()
|
||||
if (event.keyCode === 13) {
|
||||
if ($('#my_test').val().trim() === '') {
|
||||
this.blur()
|
||||
$('.arrow_box').show()
|
||||
setTimeout(() => {
|
||||
this.focus()
|
||||
}, 1000)
|
||||
} else {
|
||||
$('.arrow_box').hide()
|
||||
//发送消息
|
||||
sendMsg();
|
||||
this.blur()
|
||||
setTimeout(() => {
|
||||
this.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#send').on('click', function (event) {
|
||||
event.stopPropagation()
|
||||
if ($('#my_test').val().trim() === '') {
|
||||
$('.arrow_box').show()
|
||||
} else {
|
||||
sendMsg();
|
||||
}
|
||||
})
|
||||
|
||||
function sendMsg() {
|
||||
var message = $("#my_test").val();
|
||||
$("#msg_list").append(`<li class="active"}>
|
||||
<div class="main self">
|
||||
<div class="text">` + message + `</div>
|
||||
</div>
|
||||
</li>`);
|
||||
$("#my_test").val('');
|
||||
|
||||
//发送消息
|
||||
message = username + ":" + message;
|
||||
ws.send(message)
|
||||
|
||||
|
||||
// 置底
|
||||
setBottom();
|
||||
}
|
||||
|
||||
// 置底
|
||||
function setBottom() {
|
||||
// 发送消息后滚动到底部
|
||||
const container = $('.m-message')
|
||||
const scroll = $('#msg_list')
|
||||
container.animate({
|
||||
scrollTop: scroll[0].scrollHeight - container[0].clientHeight + container.scrollTop() + 100
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
var ws = new WebSocket("ws://localhost:9875/ws");
|
||||
ws.onopen = function()
|
||||
{
|
||||
console.log("连接成功");
|
||||
};
|
||||
ws.onmessage = function(evt)
|
||||
{
|
||||
var data = JSON.parse(evt.data);
|
||||
for(var i in protocol){
|
||||
if(protocol[i].code == data.code){
|
||||
protocol[i].handler(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = function(evt)
|
||||
{
|
||||
console.log("WebSocketClosed!");
|
||||
};
|
||||
ws.onerror = function(evt)
|
||||
{
|
||||
console.log("WebSocketError!");
|
||||
};
|
||||
|
||||
/** 发送消息 */
|
||||
function send(code,params){
|
||||
var obj = new Object();
|
||||
obj.code = code;
|
||||
obj.params = params;
|
||||
var json = JSON.stringify(obj);
|
||||
ws.send(json);
|
||||
}
|
||||
|
||||
/** 协议封装 */
|
||||
function protocolHandler(code,handler){
|
||||
var obj = {
|
||||
code: code,
|
||||
handler: handler
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var joinRoom = function(data){
|
||||
$("#first").hide();
|
||||
$("#second").hide();
|
||||
$("#room").show();
|
||||
$("#roomCode").text(data.params.code);
|
||||
}
|
||||
|
||||
var protocol = new Object();
|
||||
//登录返回
|
||||
protocol.login = protocolHandler(100,function(data){
|
||||
$("#first").hide();
|
||||
$("#second").show();
|
||||
$("#room").hide();
|
||||
$("#username").text(data.params.name);
|
||||
$("#user").text(data.params.name);
|
||||
})
|
||||
//发送消息到所有
|
||||
protocol.msgAll = protocolHandler(102,function(data){
|
||||
$("#allTable").append("<tr class='user'>" +
|
||||
"<td style='text-align: right'>" + data.params.username + ":</td>" +
|
||||
"<td style='text-align: left'>" + data.params.msg + "</td></tr>");
|
||||
})
|
||||
//私聊
|
||||
protocol.msgOne = protocolHandler(103,function(data){
|
||||
$("#oneTable").append("<tr class='user'>" +
|
||||
"<td style='text-align: right'>" + data.params.username + ":</td>" +
|
||||
"<td style='text-align: left'>" + data.params.msg + "</td></tr>");
|
||||
})
|
||||
//加入房间
|
||||
protocol.joinRoomById = protocolHandler(104,joinRoom);
|
||||
protocol.joinRoomRandom = protocolHandler(105,joinRoom);
|
||||
protocol.createRoom = protocolHandler(106,joinRoom);
|
||||
//离开房间
|
||||
protocol.leaveRoom = protocolHandler(107,function(data){
|
||||
$("#first").hide();
|
||||
$("#second").show();
|
||||
$("#room").hide();
|
||||
$("#allTable").empty();
|
||||
})
|
||||
//用户加入
|
||||
protocol.userJoinRoom = protocolHandler(-101,function(data){
|
||||
$("#allTable").append("<tr class='system-msg'>" +
|
||||
"<td style='text-align: right'>系统消息:</td>" +
|
||||
"<td style='text-align: left'><" + data.params.username + ">加入房间</td></tr>");
|
||||
})
|
||||
//用户离开
|
||||
protocol.userLeaveRoom = protocolHandler(-102,function(data){
|
||||
$("#allTable").append("<tr class='system-msg'>" +
|
||||
"<td style='text-align: right'>系统消息:</td>" +
|
||||
"<td style='text-align: left'><" + data.params.username + ">离开房间</td></tr>");
|
||||
})
|
||||
//服务器主动推送
|
||||
protocol.serverPush = protocolHandler(-200,function(data){
|
||||
|
||||
})
|
||||
//错误信息
|
||||
protocol.errorMsg = protocolHandler(-400,function(data){
|
||||
layer.msg(data.msg);
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
9831
xjs-study/netty-project/netty-springboot/src/main/resources/static/js/jquery-2.2.0.js
vendored
Normal file
4
xjs-study/netty-project/netty-springboot/src/main/resources/static/js/jquery.1.7.2.min.js
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
jQuery tagEditor v1.0.3
|
||||
Copyright (c) 2014 Simon Steinberger / Pixabay
|
||||
GitHub: https://github.com/Pixabay/jQuery-tagEditor
|
||||
License: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
(function($){
|
||||
// modified autoGrowInput - stackoverflow.com/questions/931207
|
||||
// lets input fields grow dynamically on change
|
||||
$.fn.autoGrowInput=function(o){o=$.extend({maxWidth:250,minWidth:20,comfortZone:0},o);this.filter('input:text').each(function(){var minWidth=o.minWidth||$(this).width(),val=' ',input=$(this),comfortZone=o.comfortZone?o.comfortZone:parseInt(parseInt($(this).css('fontSize'))*0.9),dummy=$('<dummy/>').css({position:'absolute',top:-9999,left:-9999,width:'auto',fontSize:input.css('fontSize'),fontFamily:input.css('fontFamily'),fontWeight:input.css('fontWeight'),letterSpacing:input.css('letterSpacing'),whiteSpace:'nowrap'}),check=function(){if(val===(val=input.val()))return;dummy.html(val.replace(/&/g,'&').replace(/\s/g,' ').replace(/</g,'<').replace(/>/g,'>'));var newWidth=dummy.width()+comfortZone;if(newWidth>o.maxWidth)newWidth=o.maxWidth;if(newWidth<o.minWidth)newWidth=o.minWidth;if(newWidth!=input.width())input.width(newWidth);};dummy.insertAfter(input);$(this).bind('keyup keydown blur focus autogrow',check);});return this;};
|
||||
|
||||
// code.accursoft.com/caret/overview
|
||||
// used to set cursor position in input fields
|
||||
!function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return 0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery);
|
||||
|
||||
// plugin with val as parameter for public methods
|
||||
$.fn.tagEditor = function(options, val, next){
|
||||
|
||||
// build options dictionary with default values
|
||||
var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this;
|
||||
|
||||
// store regex and default delimiter in options for later use
|
||||
o.dregex = new RegExp('['+o.delimiter.replace('-', '\-')+']', 'g');
|
||||
|
||||
// public methods
|
||||
if (typeof options == 'string') {
|
||||
|
||||
// depending on selector, response may contain tag lists of multiple editor instances
|
||||
var response = [];
|
||||
selector.each(function(){
|
||||
// the editor is the next sibling to the hidden, original field
|
||||
var el = $(this), o = el.data('options'), ed = el.next('.tag-editor');
|
||||
if (options == 'getTags')
|
||||
response.push({field: el[0], editor: ed, tags: ed.data('tags')});
|
||||
else if (options == 'addTag') {
|
||||
// insert new tag
|
||||
$('<li><div class="tag-editor-spacer"> '+o.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(ed).find('.tag-editor-tag')
|
||||
.html('<input type="text" maxlength="'+o.maxLength+'">').addClass('active').find('input').val(val).blur();
|
||||
if (next != 'blur') ed.click();
|
||||
else $('.placeholder', ed).remove();
|
||||
} else if (options == 'removeTag') {
|
||||
// trigger delete on matching tag, then click editor to create a new tag
|
||||
$('.tag-editor-tag', ed).filter(function(){return $(this).html()==val;}).closest('li').find('.tag-editor-delete').click();
|
||||
if (next != 'blur') ed.click();
|
||||
} else if (options == 'destroy') {
|
||||
el.css('display', o.elDisplay).removeData('options').next('.tag-editor').remove();
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
// delete selected tags on backspace, delete, ctrl+x
|
||||
function delete_selected_tags(e){
|
||||
if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) {
|
||||
var sel = getSelection(), el = $(sel.getRangeAt(0).commonAncestorContainer);
|
||||
if (el.hasClass('tag-editor')) {
|
||||
var tags = [], splits = sel.toString().split(el.prev().data('options').dregex);
|
||||
for (i=0; i<splits.length; i++){ var tag = $.trim(splits[i]); if (tag) tags.push(tag); }
|
||||
$('.tag-editor-tag', el).each(function(){
|
||||
if (~$.inArray($(this).html(), tags)) $(this).closest('li').find('.tag-editor-delete').click();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', delete_selected_tags);
|
||||
|
||||
return selector.each(function(){
|
||||
var el = $(this), tag_list = [];
|
||||
|
||||
// create editor (ed) instance
|
||||
o.elDisplay = el.css('display'); // store for destroy method
|
||||
el.css('display', 'none');
|
||||
var ed = $('<ul '+(o.clickDelete ? 'oncontextmenu="return false;" ' : '')+'class="tag-editor"></ul>').insertAfter(el);
|
||||
el.data('options', o); // set data on hidden field
|
||||
|
||||
// add dummy item for min-height on empty editor
|
||||
ed.append('<li style="width:.1px"> </li>');
|
||||
|
||||
// markup for new tag
|
||||
var new_tag = '<li><div class="tag-editor-spacer"> '+o.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';
|
||||
|
||||
// helper: update global data
|
||||
function set_placeholder(){
|
||||
if (o.placeholder && !tag_list.length && !$('.deleted, .placeholder, input', ed).length)
|
||||
ed.append('<li class="placeholder"><div>'+o.placeholder+'</div></li>');
|
||||
}
|
||||
|
||||
// helper: update global data
|
||||
function update_globals(init){
|
||||
var old_tags = tag_list.toString();
|
||||
tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function(i, e) {
|
||||
var val = $.trim($(this).hasClass('active') ? $(this).find('input').val() : $(e).text());
|
||||
if (val) return val;
|
||||
}).get();
|
||||
ed.data('tags', tag_list);
|
||||
el.val(tag_list.join(o.delimiter[0]));
|
||||
// change callback except for plugin init
|
||||
if (!init) if (old_tags != tag_list.toString()) o.onChange(el, ed, tag_list);
|
||||
set_placeholder();
|
||||
}
|
||||
|
||||
ed.click(function(e){
|
||||
// do not create tag when user selects tags by text selection
|
||||
if (window.getSelection && getSelection() != '') return;
|
||||
|
||||
blur_result = true
|
||||
$('input:focus', ed).blur();
|
||||
if (!blur_result) return false;
|
||||
blur_result = true
|
||||
|
||||
// always remove placeholder on click
|
||||
$('.placeholder', ed).remove();
|
||||
|
||||
// calculate tag closest to click position
|
||||
var d, dist = 99999, closest_tag, loc;
|
||||
$('.tag-editor-tag', ed).each(function(){
|
||||
var tag = $(this), to = tag.offset(), tag_x = to.left, tag_y = to.top;
|
||||
if (e.pageY >= tag_y && e.pageY <= tag_y+tag.height()) {
|
||||
if (e.pageX < tag_x) loc = 'before', d = tag_x - e.pageX;
|
||||
else loc = 'after', d = e.pageX - tag_x - tag.width();
|
||||
if (d < dist) dist = d, closest_tag = tag;
|
||||
}
|
||||
});
|
||||
if (loc == 'before')
|
||||
$(new_tag).insertBefore(closest_tag.closest('li')).find('.tag-editor-tag').click();
|
||||
else if (loc == 'after')
|
||||
$(new_tag).insertAfter(closest_tag.closest('li')).find('.tag-editor-tag').click();
|
||||
else // empty editor
|
||||
$(new_tag).appendTo(ed).find('.tag-editor-tag').click();
|
||||
return false;
|
||||
});
|
||||
|
||||
ed.on('click', '.tag-editor-delete', function(e){
|
||||
// delete icon is hidden when input is visible; place cursor near invisible delete icon on click
|
||||
if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; }
|
||||
|
||||
var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
|
||||
if (o.beforeTagDelete(el, ed, tag_list, tag.html()) === false) return false;
|
||||
tag.addClass('deleted').animate({width: 0}, 175, function(){ li.remove(); set_placeholder(); });
|
||||
update_globals();
|
||||
return false;
|
||||
});
|
||||
|
||||
// delete on right mouse click or ctrl+click
|
||||
if (o.clickDelete)
|
||||
ed.on('mousedown', '.tag-editor-tag', function(e){
|
||||
if (e.ctrlKey || e.which > 1) {
|
||||
var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
|
||||
if (o.beforeTagDelete(el, ed, tag_list, tag.html()) === false) return false;
|
||||
tag.addClass('deleted').animate({width: 0}, 175, function(){ li.remove(); set_placeholder(); });
|
||||
update_globals();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ed.on('click', '.tag-editor-tag', function(e){
|
||||
// delete on right click or ctrl+click -> exit
|
||||
if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false;
|
||||
|
||||
if (!$(this).hasClass('active')) {
|
||||
var tag = $(this).html();
|
||||
// guess cursor position in text input
|
||||
var left_percent = Math.abs(($(this).offset().left - e.pageX)/$(this).width()), caret_pos = parseInt(tag.length*left_percent),
|
||||
input = $(this).html('<input type="text" maxlength="'+o.maxLength+'" value="'+tag+'">').addClass('active').find('input');
|
||||
input.data('old_tag', tag).focus().autoGrowInput().trigger('autogrow').caret(caret_pos);
|
||||
if (o.autocomplete) {
|
||||
var aco = o.autocomplete;
|
||||
// extend user provided autocomplete select method
|
||||
var ac_select = 'select' in aco ? o.autocomplete.select : '';
|
||||
aco.select = function(){ if (ac_select) ac_select(); setTimeout(function(){ $('.active', ed).find('input').trigger('autogrow'); }, 20); };
|
||||
input.autocomplete(aco);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// helper: split into multiple tags, e.g. after paste
|
||||
function split_cleanup(input){
|
||||
var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), old_tag = input.data('old_tag');
|
||||
var old_tags = tag_list.slice(0); // copy tag_list
|
||||
for (i in sub_tags) {
|
||||
tag = $.trim(sub_tags[i]).slice(0, o.maxLength);
|
||||
if (tag) {
|
||||
if (o.forceLowercase) tag = tag.toLowerCase();
|
||||
o.beforeTagSave(el, ed, old_tags, old_tag, tag);
|
||||
// remove duplicates
|
||||
if (~$.inArray(tag, old_tags))
|
||||
$('.tag-editor-tag', ed).each(function(){ if ($(this).html() == tag) $(this).closest('li').remove(); });
|
||||
old_tags.push(tag);
|
||||
li.before('<li><div class="tag-editor-spacer"> '+o.delimiter[0]+'</div><div class="tag-editor-tag">'+tag+'</div><div class="tag-editor-delete"><i></i></div></li>');
|
||||
}
|
||||
}
|
||||
input.attr('maxlength', o.maxLength).removeData('old_tag').val('').focus();
|
||||
update_globals();
|
||||
}
|
||||
|
||||
ed.on('blur', 'input', function(e){
|
||||
var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0]));
|
||||
if (!tag) {
|
||||
if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) {
|
||||
input.val(old_tag).trigger('autogrow').focus();
|
||||
blur_result = false;
|
||||
update_globals();
|
||||
return;
|
||||
}
|
||||
try { input.closest('li').remove(); } catch(e){}
|
||||
if (old_tag) update_globals();
|
||||
}
|
||||
else if (tag.indexOf(o.delimiter[0])>=0) { split_cleanup(input); return; }
|
||||
else if (tag != old_tag) {
|
||||
if (o.forceLowercase) tag = tag.toLowerCase();
|
||||
o.beforeTagSave(el, ed, tag_list, old_tag, tag);
|
||||
// remove duplicates
|
||||
$('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).html() == tag) $(this).closest('li').remove(); });
|
||||
}
|
||||
input.parent().html(tag).removeClass('active');
|
||||
if (tag != old_tag) update_globals();
|
||||
set_placeholder();
|
||||
});
|
||||
|
||||
var pasted_content;
|
||||
ed.on('paste', 'input', function(e){
|
||||
$(this).removeAttr('maxlength');
|
||||
pasted_content = $(this);
|
||||
setTimeout(function(){ split_cleanup(pasted_content); }, 30);
|
||||
});
|
||||
|
||||
// keypress delimiter
|
||||
var inp;
|
||||
ed.on('keypress', 'input', function(e){
|
||||
if (o.delimiter.indexOf(String.fromCharCode(e.which))>=0) {
|
||||
inp = $(this);
|
||||
setTimeout(function(){ split_cleanup(inp); }, 20);
|
||||
}
|
||||
});
|
||||
|
||||
ed.on('keydown', 'input', function(e){
|
||||
var $t = $(this);
|
||||
// left/up key + backspace key on empty field
|
||||
if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) {
|
||||
var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
|
||||
if (prev_tag.length) prev_tag.click().find('input').caret(-1);
|
||||
else if ($t.val()) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
|
||||
return false;
|
||||
}
|
||||
// right/down key
|
||||
else if ((e.which == 39 || !o.autocomplete && e.which == 40) && ($t.caret() == $t.val().length)) {
|
||||
var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
|
||||
if (next_tag.length) next_tag.click().find('input').caret(0);
|
||||
else if ($t.val()) ed.click();
|
||||
return false;
|
||||
}
|
||||
// tab key
|
||||
else if (e.which == 9) {
|
||||
if (e.shiftKey) { // jump left
|
||||
var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
|
||||
if (prev_tag.length) prev_tag.click().find('input').caret(0);
|
||||
else if ($t.val()) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
|
||||
} else { // jump right
|
||||
var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
|
||||
if (next_tag.length) next_tag.click().find('input').caret(0);
|
||||
else if ($t.val()) ed.click();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// del key
|
||||
else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret() == $t.val().length))) {
|
||||
var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
|
||||
if (next_tag.length) next_tag.click().find('input').caret(0);
|
||||
else if ($t.val()) ed.click();
|
||||
return false;
|
||||
}
|
||||
// enter key
|
||||
else if (e.which == 13) {
|
||||
var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
|
||||
if (next_tag.length) next_tag.click().find('input').caret(0);
|
||||
else if ($t.val()) ed.click();
|
||||
return false;
|
||||
}
|
||||
// pos1
|
||||
else if (e.which == 36 && !$t.caret()) ed.find('.tag-editor-tag').first().click();
|
||||
// end
|
||||
else if (e.which == 35 && $t.caret() == $t.val().length) ed.find('.tag-editor-tag').last().click();
|
||||
// esc
|
||||
else if (e.which == 27) {
|
||||
$t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// create initial tags
|
||||
var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex);
|
||||
for (i in tags) {
|
||||
var tag = $.trim(tags[i].replace(/ +/, ' '));
|
||||
if (tag) {
|
||||
if (o.forceLowercase) tag = tag.toLowerCase();
|
||||
tag_list.push(tag);
|
||||
ed.append('<li><div class="tag-editor-spacer"> '+o.delimiter[0]+'</div><div class="tag-editor-tag">'+tag+'</div><div class="tag-editor-delete"><i></i></div></li>');
|
||||
}
|
||||
}
|
||||
update_globals(true); // true -> no onChange callback
|
||||
|
||||
// init sortable
|
||||
if (o.sortable && $.fn.sortable) ed.sortable({
|
||||
distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone',
|
||||
update: function(){ update_globals(); }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.tagEditor.defaults = {
|
||||
initialTags: [],
|
||||
maxLength: 50,
|
||||
delimiter: ',;',
|
||||
placeholder: '',
|
||||
forceLowercase: true,
|
||||
clickDelete: false,
|
||||
sortable: true, // jQuery UI sortable
|
||||
autocomplete: null, // options dict for jQuery UI autocomplete
|
||||
|
||||
// callbacks
|
||||
onChange: function(){},
|
||||
beforeTagSave: function(){},
|
||||
beforeTagDelete: function(){}
|
||||
};
|
||||
}(jQuery));
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>聊天室</title>
|
||||
<link rel="stylesheet" href="/css/chat.css">
|
||||
<script src="/js/jquery.1.7.2.min.js"></script>
|
||||
<script src="/js/chat.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="chat">
|
||||
<div class="sidebar">
|
||||
<div class="m-card">
|
||||
<header>
|
||||
<p class="name" >姓名:</p>
|
||||
<p class="name" id="username"></p>
|
||||
</header>
|
||||
</div>
|
||||
<div class="m-list">
|
||||
<ul id="user_list">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="m-message">
|
||||
<ul id="msg_list">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-text">
|
||||
<textarea placeholder="按 Enter 发送" id="my_test"></textarea>
|
||||
<div class="pager_btn">
|
||||
<button id="send">发送</button>
|
||||
</div>
|
||||
<div class="arrow_box">
|
||||
发送内容不能为空
|
||||
<div class="arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>xjs-study</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
<name>Netty项目</name>
|
||||
<modules>
|
||||
<module>netty-springboot</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>netty-project</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
<name>学习</name>
|
||||
<modules>
|
||||
<module>xjs-study-base</module>
|
||||
<module>netty-project</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>xjs-study</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
@ -3,13 +3,15 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>xjs-business</artifactId>
|
||||
<groupId>com.xjs</groupId>
|
||||
<artifactId>xjs-study</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>学习-基础内容</name>
|
||||
|
||||
<artifactId>xjs-learn</artifactId>
|
||||
<groupId>com.xjs</groupId>
|
||||
<artifactId>xjs-study-base</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class BlockingQueueDemo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//创建阻塞队列
|
||||
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
|
||||
|
||||
//第一组
|
||||
System.out.println(blockingQueue.add("A"));
|
||||
|
||||
System.out.println(blockingQueue.add("B"));
|
||||
|
||||
System.out.println(blockingQueue.add("C"));
|
||||
|
||||
//System.out.println(blockingQueue.add("D"));
|
||||
|
||||
|
||||
System.out.println(blockingQueue.remove());
|
||||
System.out.println(blockingQueue.remove());
|
||||
System.out.println(blockingQueue.remove());
|
||||
//System.out.println(blockingQueue.remove());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class CompleableFutureDemo {
|
||||
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
CompletableFuture<Void> c =CompletableFuture.runAsync(()->{
|
||||
System.out.println(Thread.currentThread().getName());
|
||||
});
|
||||
c.get();
|
||||
|
||||
|
||||
CompletableFuture<String> cc =CompletableFuture.supplyAsync(()->{
|
||||
System.out.println(Thread.currentThread().getName());
|
||||
return Thread.currentThread().getName();
|
||||
});
|
||||
|
||||
cc.whenComplete((t,u)->{
|
||||
System.out.println(t); //返回值
|
||||
System.out.println(u); //异常
|
||||
}).get();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class DeadLock {
|
||||
|
||||
static Object a = new Object();
|
||||
static Object b = new Object();
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Thread(()->{
|
||||
synchronized (a) {
|
||||
System.out.println("A");
|
||||
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
synchronized (b) {
|
||||
System.out.println("B");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
new Thread(()->{
|
||||
synchronized (b) {
|
||||
System.out.println("a");
|
||||
|
||||
synchronized (a) {
|
||||
System.out.println("b");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-03
|
||||
*/
|
||||
public class LockShare {
|
||||
|
||||
|
||||
//初始值
|
||||
private int number = 0;
|
||||
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
private Condition condition=lock.newCondition();
|
||||
|
||||
|
||||
|
||||
public void incr() throws InterruptedException {
|
||||
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
while (number != 0) {
|
||||
condition.await();
|
||||
}
|
||||
//如果number是0,则+1
|
||||
number++;
|
||||
|
||||
System.out.println(Thread.currentThread().getName() + ":" + number);
|
||||
|
||||
//通知其他线程
|
||||
condition.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void decr() throws InterruptedException {
|
||||
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
|
||||
//使用while判断防止wait虚假唤醒问题
|
||||
while (number != 1) {
|
||||
condition.await();
|
||||
}
|
||||
//如果number是0,则+1
|
||||
number--;
|
||||
|
||||
System.out.println(Thread.currentThread().getName() + ":" + number);
|
||||
|
||||
//通知其他线程
|
||||
condition.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LockShare share = new LockShare();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true){
|
||||
try {
|
||||
share.incr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "A").start();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
share.decr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "B").start();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
share.decr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "C").start();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
share.decr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "D").start();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Lock关键字学习
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-02
|
||||
*/
|
||||
public class LockTicket {
|
||||
|
||||
//票总数
|
||||
private int number = 3000;
|
||||
|
||||
//创建可重入锁
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
|
||||
//卖票方法
|
||||
public void sale() {
|
||||
//上锁
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
if (number > 0) {
|
||||
System.out.println(Thread.currentThread().getName() + ":卖出:" + (number--) + "剩下:" + number);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
LockTicket lockTicket = new LockTicket();
|
||||
|
||||
new Thread(() -> {
|
||||
for (int i = 0; i < 9999; i++) {
|
||||
lockTicket.sale();
|
||||
}
|
||||
|
||||
}, "A").start();
|
||||
|
||||
new Thread(() -> {
|
||||
for (int i = 0; i < 9999; i++) {
|
||||
lockTicket.sale();
|
||||
}
|
||||
|
||||
}, "B").start();
|
||||
|
||||
new Thread(() -> {
|
||||
for (int i = 0; i < 9999; i++) {
|
||||
lockTicket.sale();
|
||||
}
|
||||
|
||||
}, "C").start();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 6辆车,3停车位
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class SemaphoreDemo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//创建Semaphore,设置许可数量
|
||||
|
||||
Semaphore semaphore = new Semaphore(10);
|
||||
|
||||
for (int i = 1; i <=60; i++) {
|
||||
new Thread(()->{
|
||||
//抢占车位
|
||||
try {
|
||||
semaphore.acquire();
|
||||
|
||||
System.out.println(Thread.currentThread().getName()+"抢到了车位");
|
||||
|
||||
//设置随机停车时间
|
||||
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
|
||||
|
||||
System.out.println(Thread.currentThread().getName()+"---离开了车位");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
//释放
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
},String.valueOf(i)).start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-03
|
||||
*/
|
||||
public class Share {
|
||||
|
||||
//初始值
|
||||
private int number = 0;
|
||||
|
||||
public synchronized void incr() throws InterruptedException {
|
||||
|
||||
if (number != 0) {
|
||||
this.wait();
|
||||
}
|
||||
|
||||
//如果number是0,则+1
|
||||
number++;
|
||||
|
||||
System.out.println(Thread.currentThread().getName() + ":" + number);
|
||||
|
||||
//通知其他线程
|
||||
this.notify();
|
||||
|
||||
}
|
||||
|
||||
//-1
|
||||
public synchronized void decr() throws InterruptedException {
|
||||
|
||||
if (number != 1) {
|
||||
this.wait();
|
||||
}
|
||||
number--;
|
||||
System.out.println(Thread.currentThread().getName() + ":" + number);
|
||||
|
||||
//通知其他线程
|
||||
this.notify();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Share share = new Share();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true){
|
||||
try {
|
||||
share.incr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "A").start();
|
||||
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
share.decr();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}, "B").start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 创建资源类
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
|
||||
class ShareResource {
|
||||
|
||||
//定义标志位
|
||||
private int flag = 1; //1 AA 2 BB 3 CC
|
||||
|
||||
//创建Lock锁
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
//创建3个condition
|
||||
private Condition c1 = lock.newCondition();
|
||||
private Condition c2 = lock.newCondition();
|
||||
private Condition c3 = lock.newCondition();
|
||||
|
||||
|
||||
public void print5(int loop) throws InterruptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
while (flag != 1) {
|
||||
c1.await();
|
||||
}
|
||||
//干活
|
||||
for (int i = 1; i <=5; i++) {
|
||||
System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
|
||||
|
||||
}
|
||||
flag = 2;
|
||||
c1.signal();
|
||||
|
||||
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void print10(int loop) throws InterruptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
while (flag != 2) {
|
||||
c1.await();
|
||||
}
|
||||
//干活
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
|
||||
|
||||
}
|
||||
flag = 3;
|
||||
c1.signal();
|
||||
|
||||
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void print15(int loop) throws InterruptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
while (flag != 3) {
|
||||
c1.await();
|
||||
}
|
||||
//干活
|
||||
for (int i = 1; i <=15; i++) {
|
||||
System.out.println(Thread.currentThread().getName()+"::"+i+":轮数:"+loop);
|
||||
|
||||
}
|
||||
flag = 1;
|
||||
c1.signal();
|
||||
|
||||
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class ThreadDemo3 {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ShareResource shareResource = new ShareResource();
|
||||
|
||||
new Thread(() ->{
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
try {
|
||||
shareResource.print5(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
},"AA").start();
|
||||
|
||||
new Thread(() ->{
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
try {
|
||||
shareResource.print10(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
},"BB").start();
|
||||
|
||||
new Thread(() ->{
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
try {
|
||||
shareResource.print15(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
},"CC").start();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* 线程不安全List特点
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class ThreadDemo4 {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//创建ArrayList
|
||||
//List<String> list = new Vector<>(); //通过Vector解决
|
||||
|
||||
//List<String> list = Collections.synchronizedList(new ArrayList<>()); //Collections工具类解决
|
||||
|
||||
CopyOnWriteArrayList<Object> list = new CopyOnWriteArrayList<>(); //写时复制技术解决
|
||||
|
||||
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
new Thread(() -> {
|
||||
//像集合中添加内容
|
||||
list.add(UUID.randomUUID().toString().substring(0, 5));
|
||||
|
||||
System.out.println(list);
|
||||
|
||||
}, String.valueOf(i)).start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.xjs.juc;
|
||||
|
||||
/**
|
||||
* 练习juc
|
||||
* @author xiejs
|
||||
* @since 2022-03-02
|
||||
*/
|
||||
public class Ticket {
|
||||
|
||||
private int number = 30;
|
||||
|
||||
public synchronized void sale() {
|
||||
|
||||
if (number > 0) {
|
||||
System.out.println(Thread.currentThread().getName()+":卖出:"+(number--)+"剩下:"+number);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class SaleTicket {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Ticket ticket = new Ticket();
|
||||
|
||||
new Thread(() ->{
|
||||
|
||||
//调用卖票方法
|
||||
for (int i = 0; i < 40; i++) {
|
||||
ticket.sale();
|
||||
}
|
||||
|
||||
},"AA").start();
|
||||
|
||||
new Thread(() ->{
|
||||
|
||||
//调用卖票方法
|
||||
for (int i = 0; i < 40; i++) {
|
||||
ticket.sale();
|
||||
}
|
||||
|
||||
},"BB").start();
|
||||
|
||||
new Thread(() ->{
|
||||
|
||||
//调用卖票方法
|
||||
for (int i = 0; i < 40; i++) {
|
||||
ticket.sale();
|
||||
}
|
||||
|
||||
},"CC").start();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.xjs.juc.callable;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class Demo1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Thread(new MyThread1(),"AA").start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class MyThread1 implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MyThread2 implements Callable{
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.xjs.juc.poll;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class ThreadPollDemo1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//一池多线程
|
||||
|
||||
ExecutorService pool = Executors.newFixedThreadPool(100);
|
||||
|
||||
try {
|
||||
//10个请求
|
||||
|
||||
for (int i = 1; i <= 10000000; i++) {
|
||||
|
||||
//执行
|
||||
pool.execute(()->{
|
||||
System.out.println(Thread.currentThread().getName()+"办理业务");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
|
||||
}finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.xjs.juc.poll;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class ThreadPoolDemo2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
|
||||
2, //保持线程数
|
||||
5, //最大线程数量
|
||||
2L, //线程保持时间
|
||||
TimeUnit.SECONDS, //保持时间单位
|
||||
new ArrayBlockingQueue<>(3), //阻塞队列
|
||||
Executors.defaultThreadFactory(), //线程工厂
|
||||
new ThreadPoolExecutor.AbortPolicy() //拒绝策略
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.xjs.juc.readwrite;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-07
|
||||
*/
|
||||
public class ReadWriteLockDemo {
|
||||
|
||||
private volatile Map<String, String> map = new HashMap();
|
||||
|
||||
//放数据
|
||||
public void put(String key, Object value) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.xjs.netty;
|
||||
|
||||
import com.xjs.netty.codec.MessageDecoder;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
/**
|
||||
* Netty客户端
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyClient {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
//1、创建线程组
|
||||
NioEventLoopGroup group = new NioEventLoopGroup();
|
||||
|
||||
//2、创建客户端启动助手
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
|
||||
//3、设置线程组
|
||||
bootstrap.group(group)
|
||||
//4、设置通道实现为NIO
|
||||
.channel(NioSocketChannel.class)
|
||||
//5、创建一个通道初始化对象
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast("MessageDecoder", new MessageDecoder());
|
||||
|
||||
//6、向pipeline中添加自定义业务处理handler
|
||||
ch.pipeline().addLast(new NettyClientHandler());
|
||||
}
|
||||
});
|
||||
|
||||
//7、启动客户端,等待连接服务端,同时将异步改为同步
|
||||
ChannelFuture future = bootstrap.connect("127.0.0.1", 9997).sync();
|
||||
|
||||
//8、关闭通道和关闭连接池
|
||||
future.channel().closeFuture().sync();
|
||||
group.shutdownGracefully();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.xjs.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandler;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* 客户端处理类
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyClientHandler implements ChannelInboundHandler {
|
||||
|
||||
/**
|
||||
* 通道就绪事件
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
ChannelFuture future = ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我是Netty客户端", CharsetUtil.UTF_8));
|
||||
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("数据发送成功");
|
||||
}else {
|
||||
System.out.println("数据发送失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道读就绪事件
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
|
||||
ByteBuf byteBuf = (ByteBuf) msg;
|
||||
System.out.println("服务端发送的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.xjs.netty;
|
||||
|
||||
import com.xjs.netty.codec.MessageDecoder;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
|
||||
/**
|
||||
* Netty服务器
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyServer {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
//1、创建bossGroup线程组,处理网络事件-连接事件
|
||||
NioEventLoopGroup boosGroup = new NioEventLoopGroup(1);
|
||||
|
||||
//2、创建workerGroup线程组,处理网络事件-读写事件 2*处理器线程数
|
||||
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
//3、创建服务端启动助手
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
|
||||
//4、设置bossGroup线程组和workerGroup线程组
|
||||
serverBootstrap.group(workerGroup, workerGroup)
|
||||
//5、设置服务端通道实现为NIO
|
||||
.channel(NioServerSocketChannel.class)
|
||||
//6、参数设置
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
//6、参数设置
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
|
||||
//7、创建一个通道初始化对象
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
//添加解码器
|
||||
ch.pipeline().addLast("MessageDecoder", new MessageDecoder());
|
||||
|
||||
//8、向pipeline中添加自定义业务处理handler
|
||||
ch.pipeline().addLast(new NettyServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
//9、启动服务端并绑定端口,同时将异步改为同步
|
||||
ChannelFuture future = serverBootstrap.bind(9997).sync();
|
||||
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("端口绑定成功");
|
||||
}else {
|
||||
System.out.println("端口绑定失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("服务端启动成功");
|
||||
|
||||
//10、关闭通道(并不是真正意义上的关闭,而是监听关闭的状态)和关闭连接池
|
||||
future.channel().closeFuture().sync();
|
||||
boosGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.xjs.netty;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandler;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* 自定义处理handler
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyServerHandler implements ChannelInboundHandler {
|
||||
|
||||
/**
|
||||
* 通道读取事件
|
||||
*
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
//ByteBuf byteBuf = (ByteBuf) msg;
|
||||
|
||||
//System.out.println("客户端发送过来的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
|
||||
System.out.println("客户端发送过来的消息:" + msg);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道读取完毕事件
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||
ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我是Netty服务端", CharsetUtil.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道异常事件
|
||||
* @param ctx
|
||||
* @param cause
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.xjs.netty.chat;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* 聊天室的客户端
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyChatClient {
|
||||
|
||||
private static final String ip = "127.0.0.1";
|
||||
|
||||
private final static Integer port = 9997;
|
||||
|
||||
public static void run() {
|
||||
//1、创建线程组
|
||||
NioEventLoopGroup group = null;
|
||||
try {
|
||||
group = new NioEventLoopGroup();
|
||||
|
||||
//2、创建客户端启动助手
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
|
||||
//3、设置线程组
|
||||
bootstrap.group(group)
|
||||
//4、设置通道实现为NIO
|
||||
.channel(NioSocketChannel.class)
|
||||
//5、创建一个通道初始化对象
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
//添加解码器
|
||||
ch.pipeline().addLast( new StringDecoder());
|
||||
ch.pipeline().addLast( new StringEncoder());
|
||||
|
||||
//6、向pipeline中添加自定义业务处理handler
|
||||
ch.pipeline().addLast(new NettyChatClientHandler());
|
||||
}
|
||||
});
|
||||
|
||||
//7、启动客户端,等待连接服务端,同时将异步改为同步
|
||||
ChannelFuture future = bootstrap.connect(ip, port).sync();
|
||||
Channel channel = future.channel();
|
||||
System.out.println("------------"+channel.localAddress().toString().substring(1)+"------------");
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
while ((scanner.hasNextLine())) {
|
||||
String msg = scanner.nextLine();
|
||||
|
||||
//向服务端发送消息
|
||||
channel.writeAndFlush(msg);
|
||||
}
|
||||
|
||||
//8、关闭通道和关闭连接池
|
||||
channel.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.xjs.netty.chat;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
/**
|
||||
* 聊天室处理类
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
/**
|
||||
* 通道读取就绪事件
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.xjs.netty.chat;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
|
||||
/**
|
||||
* 聊天室服务端
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyChatServer {
|
||||
|
||||
private final static Integer port = 9997;
|
||||
|
||||
public static void main(String[] args) {
|
||||
run();
|
||||
}
|
||||
|
||||
|
||||
public static void run() {
|
||||
//1、创建bossGroup线程组,处理网络事件-连接事件
|
||||
NioEventLoopGroup boosGroup = null;
|
||||
|
||||
//2、创建workerGroup线程组,处理网络事件-读写事件 2*处理器线程数
|
||||
NioEventLoopGroup workerGroup = null;
|
||||
|
||||
|
||||
try {
|
||||
boosGroup = new NioEventLoopGroup(1);
|
||||
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
|
||||
//3、创建服务端启动助手
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
|
||||
//4、设置bossGroup线程组和workerGroup线程组
|
||||
serverBootstrap.group(workerGroup, workerGroup)
|
||||
//5、设置服务端通道实现为NIO
|
||||
.channel(NioServerSocketChannel.class)
|
||||
//6、参数设置
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
//6、参数设置
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
|
||||
//7、创建一个通道初始化对象
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
//添加解码器
|
||||
ch.pipeline().addLast( new StringDecoder());
|
||||
ch.pipeline().addLast( new StringEncoder());
|
||||
|
||||
//8、向pipeline中添加自定义业务处理handler
|
||||
ch.pipeline().addLast(new NettyChatServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
//9、启动服务端并绑定端口,同时将异步改为同步
|
||||
ChannelFuture future = serverBootstrap.bind(port).sync();
|
||||
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("端口绑定成功");
|
||||
} else {
|
||||
System.out.println("端口绑定失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("服务端启动成功");
|
||||
|
||||
//10、关闭通道(并不是真正意义上的关闭,而是监听关闭的状态)和关闭连接池
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
boosGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.xjs.netty.chat;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天室业务处理类
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private static List<Channel> channelList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 通道就绪事件
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
//当有新的客户端连接的时候,将通道放入集合
|
||||
channelList.add(channel);
|
||||
|
||||
System.out.println("[Server]:"+channel.remoteAddress().toString().substring(1)+"在线");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道读取事件
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
|
||||
//当前发送消息的通道,当前发送的客户端连接
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
for (Channel channel1 : channelList) {
|
||||
//排除自身通道
|
||||
if (channel != channel1) {
|
||||
channel1.writeAndFlush("[" + channel.remoteAddress().toString().substring(1) + "]说:" + msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道未就绪 --channel下线
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
//当有客户端断开连接的时候,就移出对应的通道
|
||||
channelList.remove(channel);
|
||||
|
||||
System.out.println("[Server]"+channel.remoteAddress().toString().substring(1)+"下线");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 异常处理事件
|
||||
* @param ctx
|
||||
* @param cause
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
Channel channel = ctx.channel();
|
||||
System.out.println("[Server]:"+channel.remoteAddress().toString().substring(1)+"异常");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.xjs.netty.codec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息解码器
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class MessageDecoder extends MessageToMessageDecoder {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
|
||||
System.out.println("正在进行消息解码...");
|
||||
|
||||
ByteBuf byteBuf = (ByteBuf) msg;
|
||||
|
||||
//传递到下一个handler
|
||||
out.add(byteBuf.toString(CharsetUtil.UTF_8));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package com.xjs.netty.http;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
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.HttpServerCodec;
|
||||
|
||||
/**
|
||||
* 聊天室服务端
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyHttpServer {
|
||||
|
||||
private final static Integer port = 8089;
|
||||
|
||||
public static void main(String[] args) {
|
||||
run();
|
||||
}
|
||||
|
||||
|
||||
public static void run() {
|
||||
//1、创建bossGroup线程组,处理网络事件-连接事件
|
||||
NioEventLoopGroup boosGroup = null;
|
||||
|
||||
//2、创建workerGroup线程组,处理网络事件-读写事件 2*处理器线程数
|
||||
NioEventLoopGroup workerGroup = null;
|
||||
|
||||
|
||||
try {
|
||||
boosGroup = new NioEventLoopGroup(1);
|
||||
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
|
||||
//3、创建服务端启动助手
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
|
||||
//4、设置bossGroup线程组和workerGroup线程组
|
||||
serverBootstrap.group(workerGroup, workerGroup)
|
||||
//5、设置服务端通道实现为NIO
|
||||
.channel(NioServerSocketChannel.class)
|
||||
//6、参数设置
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
//6、参数设置
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
|
||||
//7、创建一个通道初始化对象
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
//添加解码器
|
||||
ch.pipeline().addLast(new HttpServerCodec());
|
||||
|
||||
//8、向pipeline中添加自定义业务处理handler
|
||||
ch.pipeline().addLast(new NettyHttpServerHandler());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//9、启动服务端并绑定端口,同时将异步改为同步
|
||||
ChannelFuture future = serverBootstrap.bind(port).sync();
|
||||
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("端口绑定成功");
|
||||
} else {
|
||||
System.out.println("端口绑定失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("HTTP服务端启动成功");
|
||||
|
||||
//10、关闭通道(并不是真正意义上的关闭,而是监听关闭的状态)和关闭连接池
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
boosGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.xjs.netty.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* http服务器处理类
|
||||
* @author xiejs
|
||||
* @since 2022-03-09
|
||||
*/
|
||||
public class NettyHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
|
||||
/**
|
||||
* 读取就绪事件
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||
|
||||
//1、判断请求是不是http请求
|
||||
if (msg instanceof HttpRequest) {
|
||||
|
||||
DefaultHttpRequest request = (DefaultHttpRequest) msg;
|
||||
|
||||
System.out.println("浏览器请求路径:"+request.uri());
|
||||
|
||||
if ("/favicon.ico".equals(request.uri())) {
|
||||
System.out.println("图标不响应");
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuf byteBuf = Unpooled.copiedBuffer("Hello,I am Netty Server !", CharsetUtil.UTF_8);
|
||||
|
||||
//2、给浏览器响应
|
||||
DefaultFullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1 ,HttpResponseStatus.OK,byteBuf
|
||||
);
|
||||
|
||||
//2.1、设置响应头
|
||||
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,
|
||||
byteBuf.readableBytes());
|
||||
|
||||
ctx.writeAndFlush(response);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.xjs.nio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class CreateBufferDemo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//创建指定长度的缓冲区
|
||||
|
||||
ByteBuffer allocate = ByteBuffer.allocate(5);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.println(allocate.get()); //从缓冲区中拿取数据
|
||||
}
|
||||
|
||||
//System.out.println(allocate.get());
|
||||
|
||||
//创建有内容的缓冲区
|
||||
ByteBuffer wrap = ByteBuffer.wrap("lagou".getBytes());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.println(wrap.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.xjs.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* nio客户端
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class NioClient {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
//打开通道
|
||||
SocketChannel socketChannel = SocketChannel.open();
|
||||
|
||||
//设置连接ip和端口
|
||||
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9998));
|
||||
|
||||
//写出数据
|
||||
socketChannel.write(ByteBuffer.wrap("还钱".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
//读取服务端返回的数据
|
||||
ByteBuffer allocate = ByteBuffer.allocate(1024);
|
||||
int read = socketChannel.read(allocate);
|
||||
System.out.println("服务端消息:"+new String(allocate.array(),0,read,StandardCharsets.UTF_8));
|
||||
|
||||
//释放资源
|
||||
socketChannel.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.xjs.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class NioServer {
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
//打开一个服务端通道
|
||||
ServerSocketChannel open = ServerSocketChannel.open();
|
||||
//绑定对应的端口
|
||||
open.bind(new InetSocketAddress(9998));
|
||||
|
||||
//通道默认阻塞,开启非阻塞
|
||||
open.configureBlocking(false);
|
||||
System.out.println("服务端启动成功");
|
||||
|
||||
while (true) {
|
||||
//检查是否有客户端连接,有客户端连接会返回对应的通道
|
||||
SocketChannel channel = open.accept();
|
||||
if (channel == null) {
|
||||
System.out.println("没有客户端连接,休息2秒");
|
||||
Thread.sleep(2000);
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取客户端传递过来的数据,并且把数据放在byteBuffer这个缓冲区中
|
||||
ByteBuffer allocate = ByteBuffer.allocate(1024);
|
||||
|
||||
//正数:本次读到的有效的字节数 0:本次没有读到数据 -1:读到末尾
|
||||
int read = channel.read(allocate);
|
||||
System.out.println("客户端消息:"+new String(allocate.array(),
|
||||
0,
|
||||
read,
|
||||
StandardCharsets.UTF_8));
|
||||
|
||||
//给客户端回写数据
|
||||
channel.write(ByteBuffer.wrap("没钱".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
//释放资源
|
||||
channel.close();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.xjs.nio.selector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author xiejs
|
||||
* @since 2022-03-08
|
||||
*/
|
||||
public class NioSelectorServer {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
//打开服务端通道
|
||||
ServerSocketChannel open = ServerSocketChannel.open();
|
||||
|
||||
//绑定对应的端口号
|
||||
open.bind(new InetSocketAddress(9998));
|
||||
|
||||
//通道默认阻塞,开启非阻塞
|
||||
open.configureBlocking(false);
|
||||
|
||||
//创建选择器
|
||||
Selector selector = Selector.open();
|
||||
|
||||
//将服务端通道注册到选择器上,并指定注册监听的事件为OP_ACCEPT
|
||||
open.register(selector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
System.out.println("服务器启动成功...");
|
||||
|
||||
while (true) {
|
||||
|
||||
//检查选择器是否有事件
|
||||
int select = selector.select(2000);
|
||||
|
||||
if (select == 0) {
|
||||
System.out.println("没有事件发生");
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取事件集合
|
||||
Set<SelectionKey> selectionKeys = selector.selectedKeys();
|
||||
|
||||
Iterator<SelectionKey> iterator = selectionKeys.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
//判断事件是否是客户端连接事件SelectionKey.isAcceptable()
|
||||
SelectionKey key = iterator.next();
|
||||
if (key.isAcceptable()) {
|
||||
|
||||
//得到客户端通道,并将通道注册到选择器上,并指定监听事件为OP_READ
|
||||
SocketChannel socketChannel = open.accept();
|
||||
System.out.println("有客户端连接...");
|
||||
|
||||
//将通道必须设置为非阻塞,因为selector需要轮询监听每个通道的事件
|
||||
socketChannel.configureBlocking(false);
|
||||
|
||||
//指定监听事件为OP_READ 读就绪事件
|
||||
socketChannel.register(selector, SelectionKey.OP_READ);
|
||||
}
|
||||
|
||||
//判断客户端是否读就绪事件SelectionKey.isReadable()
|
||||
if (key.isReadable()) {
|
||||
//得到客户端通道,读取数据到缓冲区
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
ByteBuffer allocate = ByteBuffer.allocate(1024);
|
||||
int read = socketChannel.read(allocate);
|
||||
if (read > 0) {
|
||||
System.out.println("客户端消息:" + new String(allocate.array(), 0, read, StandardCharsets.UTF_8));
|
||||
|
||||
//给客户端写数据
|
||||
socketChannel.write(ByteBuffer.wrap("没钱".getBytes(StandardCharsets.UTF_8)));
|
||||
socketChannel.close();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//从集合中删除对应事件,防止二次处理
|
||||
iterator.remove();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||