学习Netty

pull/254/head
xjs 4 years ago
parent fdf3a2de6a
commit 710c4ef2ee

@ -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

Binary file not shown.

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

@ -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);
})

File diff suppressed because one or more lines are too long

@ -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,'&amp;').replace(/\s/g,'&nbsp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'));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">&nbsp;'+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">&nbsp;</li>');
// markup for new tag
var new_tag = '<li><div class="tag-editor-spacer">&nbsp;'+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">&nbsp;'+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">&nbsp;'+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;
/**
* 63
* @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();
}
}
}
}
Loading…
Cancel
Save