后端项目地址
前端代码
elemnt-ui官网:
elemnt-ui+vue实现可拖拽对话框
vue模仿qq聊天界面
vue实现dialog对话框
实现可拖拽div使用介绍
实现可拖拽div网站—vue-draggable-resizable官网
实现可拖拽div博客
使用Vue做出DIV拖拽
HttpRequestHandler
package org.lmj.chatroom;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedNioFile;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URISyntaxException;
import java.net.URL;
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private final String wsUri;
private static File indexFile; //文件不可改
static {
URL location = HttpRequestHandler.class
.getProtectionDomain()
.getCodeSource().getLocation();
try {
String path = location.toURI() + "index.html";
path = !path.contains("file:") ? path : path.substring(5);
indexFile = new File(path);
} catch (URISyntaxException e) {
throw new IllegalStateException(
"Unable to locate index.html", e);
}
}
public HttpRequestHandler(String wsUri) {
this.wsUri = wsUri;
}
@Override
public void channelRead0(ChannelHandlerContext ctx,
FullHttpRequest request) throws Exception {
if (wsUri.equalsIgnoreCase(request.getUri())) {
ctx.fireChannelRead(request.retain());
} else {
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
}
RandomAccessFile file = new RandomAccessFile(indexFile, "r");
HttpResponse response = new DefaultHttpResponse(
request.getProtocolVersion(), HttpResponseStatus.OK);
response.headers().set(
HttpHeaders.Names.CONTENT_TYPE,
"text/html; charset=UTF-8");
boolean keepAlive = HttpHeaders.isKeepAlive(request);
if (keepAlive) {
response.headers().set(
HttpHeaders.Names.CONTENT_LENGTH, file.length());
response.headers().set( HttpHeaders.Names.CONNECTION,
HttpHeaders.Values.KEEP_ALIVE);
}
ctx.write(response);
if (ctx.pipeline().get(SslHandler.class) == null) {
ctx.write(new DefaultFileRegion(
file.getChannel(), 0, file.length()));
} else {
ctx.write(new ChunkedNioFile(file.getChannel()));
}
ChannelFuture future = ctx.writeAndFlush(
LastHttpContent.EMPTY_LAST_CONTENT);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
}
private static void send100Continue(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
ctx.writeAndFlush(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
TextWebSocketFrameHandler
package org.lmj.chatroom;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
//纯文本的textsocket数据帧
//public class TextWebSocketFrameHandler implements ChannelHandler, EventExecutorGroup { 简化开发
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static ChannelGroup channels=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//保存所有channel
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//super.handlerAdded(ctx); //当有客户端连接时 连接上来的客户端的通道
Channel incoming=ctx.channel();
for (Channel ch:channels){
if (ch!=incoming){
ch.writeAndFlush("欢迎:"+incoming.remoteAddress()+"进入聊天室");
}
}
channels.add(incoming);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
//super.handlerRemoved(ctx); //有科幻端断开练级
Channel incoming=ctx.channel();
for (Channel ch:channels){
if (ch!=incoming){
ch.writeAndFlush("再见:"+incoming.remoteAddress()+"离开聊天室");
}
}
channels.remove(incoming);
}
//当客户端发送消息自动执行
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
// 读取信息,并转发
Channel incoming=ctx.channel();
//对聊天室的人都遍历一下
for (Channel ch:channels){
if (ch!=incoming){
//被动接收消息的人
ch.writeAndFlush(new TextWebSocketFrame("用户"+incoming.remoteAddress()+"说:"+msg.text()+"\n"));
}else {
//发消息的人
ch.writeAndFlush(new TextWebSocketFrame("我说:"+msg.text()+"\n"));
}
}
}
//,hahandleradd会自动执行,将脸上的记录下来
//督导数据,都有记账本记录一下
//
}
WebChatServer
package org.lmj.chatroom;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class WebChatServer {
private int port;
public WebChatServer(int port){
this.port=port;
}
public void start(){
//定义两个线程
EventLoopGroup boss=new NioEventLoopGroup(); //netty是nio模型,处理队列里的线程去处理
EventLoopGroup worker=new NioEventLoopGroup(); //干活的
try {
ServerBootstrap bootstrap=new ServerBootstrap(); //启动服务
bootstrap.group(boss,worker)
//传入隧道
.channel(NioServerSocketChannel.class)
//自定义类,来服务则服务端的处理逻辑
.childHandler(new WebChatServerInitialize())
//使用官方的默认配置,一下是参数设置
.option(ChannelOption.SO_BACKLOG,128)
//保持连接
.childOption(ChannelOption.SO_KEEPALIVE,true);
ChannelFuture future=bootstrap.bind(port).sync();//让他绑定端口号,然后服务启动
//服务端如何处理这个过程呢
//1。 客户端发信息,服务端进行转发(客户端只需要直到服务端)
System.out.println("服务端启动");
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//先写中间的代码
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String args[]){
new WebChatServer(8080).start();
}
}
WebChatServerInitialize
package org.lmj.chatroom;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
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;
//不使用implements,使用extend简化开发
//netty应用了多种设计模式,来简化的我们代码的开发
public class WebChatServerInitialize extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//是否是websocket请求
//服务器切换频道将http升级成websocket
//将0101转http
//指明工序,谁来做
//获得管道--要流经多少工序
//将请求和应答边界吗为http消息
ChannelPipeline pipeline= socketChannel.pipeline();
/**
* 流经管道需要做四道工序
*/
pipeline.addLast(new HttpServerCodec()) //请求响应编码节码为http
.addLast(new HttpObjectAggregator(64*1024)) //信息汇集公司,聚合器 //整成字节后再次封装 //缓冲区//1024这样写会白屏,缓冲区太小
.addLast(new ChunkedWriteHandler()) //http就发送聊天界面
.addLast(new HttpRequestHandler("/chat")) //chat标识http,否则是http,,,这就是(ChunkedWriteHandler的)现已到工序
//入队,出队。供料,史料就要做座机的协议,发图片也要发自己的协议,加标志位决定是公聊还是史料
.addLast(new WebSocketServerProtocolHandler("/chat")) //是chat就认为是websocket,就用这个类来处理好
.addLast(new TextWebSocketFrameHandler());
//初始化通道
}
//public class WebChatServerInitialize extends ChannelInitializer { 不能这样写,且import java.nio.channels.SocketChannel;
//initChannel(Channel channel)里面要用initChannel(SocketChannel socketChannel) 导报为import io.netty.channel.socket.SocketChannel;
}
index.html
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<body>
<form action="" onsubmit="return false">
<h1>多人聊天h1>
<textarea id="msgTxt" cols="50" rows="20">
textarea><br>
<input type="text" name="msg" style="width: 300px">
<input type="button" value="发送" onclick="send(this.form.msg.value)">
<input type="button" value="清空">
form>
<script type="text/javascript">
var socket //定义对象
if(!window.WebSocket){
//有的浏览器里可能没有Websocket对象,window就代表浏览器,就做一下兼容性处理
window.WebSocket=window.MozWebSocket;
}
if(window.WebSocket){
socket=new WebSocket("ws://localhost:8080/chat"); //websocket用的是ws协议。将localhost改成自己的ip,就可以和同一局域网的人进行群聊了
socket.onmessage=function(event){ //收到客户端消息
var ta=document.getElementById("msgTxt");
ta.value=ta.value+"\n"+event.data;
}
socket.onopen=function(event){
var ta=document.getElementById("msgTxt");
ta.value=ta.value+"\n"+"连上服务器,成功加入了聊天室";
}
socket.onclose=function(event){
var ta=document.getElementById("msgTxt");
ta.value=ta.value+"\n"+"退出聊天室";
}
//socket不断接受服务端传来的数据,然后通过三个事件进行处理
}
//客户端想发送数据
function send(msg){
if(!window.WebSocket){
return false;
}
if(socket.readyState==WebSocket.OPEN){
socket.send(msg);
}else{
alert("连接没有建立");
}
}
script>
body>
html>
<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">
<modelVersion>4.0.0modelVersion>
<groupId>org.lmjgroupId>
<artifactId>chatartifactId>
<version>1.0-SNAPSHOTversion>
<name>chatname>
<url>http://www.example.comurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.17.Finalversion>
<scope>compilescope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>