因为毕设是基于netty的即时通讯,需要用到WebSocket服务器,所以先按照网上视频教程写一个简单的练练手。尽可能多的加注释

源码下载:https://download.csdn.net/download/qq_37437983/10929191

服务端:

WebChatServer.java

package webChat;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;
import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;public class WebChatServer {private int port;public WebChatServer(int port) {this.port = port;}public void start() {//NIO selector(轮询)//Netty有线程模型有三种/** 1, Reactor单线程模型:客户端的连入和IO通讯都使用一个线程*      * 只适用于访问量不大的场景*         //不加参数,那么线程池的默认线程数为系统CPU核心数*2*       EventLoopGroup boss = new NioEventLoopGroup(1);*       ServerBootstrap bootstrap = new ServerBootstrap();*        bootstrap.group(boss);*         ChannelFuture future = bootstrap.bind(8080).sync();* * 2, Reactor多线程模型,* boss线程池中一般只有一个线程,这个线程就是Accepter线程,只用于接收客户端的连接请求;* worker线程池会有多个,一般是系统CPU核心数*2,负责已经建立连接的Channel之间的I/O通讯* *      EventLoopGroup boss = new NioEventLoopGroup();*        EventLoopGroup worker = new NioEventLoopGroup();*      ServerBootstrap bootstrap = new ServerBootstrap();*        bootstrap.group(boss,worker);*      ChannelFuture future = bootstrap.bind(8080).sync();* * 3, 主从多线程模型* boss线程池中有可以有多个Accepter线程,用于接收客户端的登录,握手,安全验证;*  服务端的ServerSocketChannel只会与线程池中的一个线程绑定,*  因此在调用NIO的Selector.select轮询客户端的Channel时实际上是在同一个线程中的,*  其他线程没有用到,所以在普通的网络应用(只有一个网络服务)中,boss线程池设置多个线程是没有作用的。*  只有当应用由多个网络服务构成,那么这多个网络服务可以共享同一个bossGroup*         EventLoopGroup boss = new NioEventLoopGroup(2);*       EventLoopGroup workerA = new NioEventLoopGroup();*         EventLoopGroup workerB = new NioEventLoopGroup();*         ServerBootstrap bootstrapA = new ServerBootstrap();*       ServerBootstrap bootstrapB = new ServerBootstrap();*       bootstrapA.group(boss,workerA); *       bootstrapB.group(boss,workerB);*        ChannelFuture futureA = bootstrapA.bind(8080).sync();*         ChannelFuture futureB = bootstrapB.bind(8888).sync();* *///一,创建两个线程组EventLoopGroup boss = new NioEventLoopGroup(); //负责客户端的链接//与已经链接的客户端通讯,进行SocketChannel的网络通讯,数据读写EventLoopGroup worker = new NioEventLoopGroup();try {//对服务器通道进行一系列的配置ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(boss,worker)  //关联两个线程//channel方法指定用于服务端监听socket通道的类,NioServerSocketChannel中会有一个ServerSocketChannel类.channel(NioServerSocketChannel.class).childHandler(new WebChatServerInitializer())//设置服务端的业务处理流水线.option(ChannelOption.SO_BACKLOG, 128) //设置TCP缓冲区.childOption(ChannelOption.SO_KEEPALIVE, true);//保持链接ChannelFuture future = bootstrap.bind(port).sync();System.out.println("[通知]:服务器已经启动");future.channel().closeFuture().sync();System.out.println("[通知]:服务器已经关闭");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {boss.shutdownGracefully();worker.shutdownGracefully();}}public static void main(String[] args) {// TODO Auto-generated method stubnew WebChatServer(8080).start();}}

WebChatServerInitializer.java

package webChat;import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;public class WebChatServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// TODO Auto-generated method stubChannelPipeline pipeline = socketChannel.pipeline();/** Websocket的通信链接的建立* 1,建立WebSocket连接时,客户端浏览器首先要向服务端发送一个握手的请求,这个请求是Http协议的请求*    请求头中有一个头信息是"Upgrade:WebSocket",向服务端表明提升协议升级,使用WebSocket协议* 2,服务端接收到这个请求会生成应答信息,返回给客户端,完成握手,返回的应答信息还是http的response响应* 3,客户端收到响应,则WebSocket的链接就建立完毕,后续的通讯就不再是Http协议的而是Websocket协议的* *//** 客户端往服务端发送数据,依次经过关卡 1,2,5* 服务端往客户端发送数据,依次经过关卡 5,4,3(反过来)* * pipeline.addLast("1",new InBoundHandlerA())*          .addLast("2",new InBoundHandlerB())*          .addLast("3",new OutBoundHandlerC())*         .addLast("4",new OutBoundHandlerD())*         .addLast("5",InBoundOutBoundHandler())* * *///pipeline相当于流水线,addLast依次向流水线上添加处理关卡(队列)pipeline.addLast(new HttpServerCodec())//将请求和应答消息编码解码为http协议的消息.addLast(new HttpObjectAggregator(64*1024))//.addLast(new ChunkedWriteHandler())//向客户端发送Html5的页面文件//区分http请求,读取html页面,并写回客户端浏览器.addLast(new HttpRequestHandler("/chat")).addLast(new WebSocketServerProtocolHandler("/chat")).addLast(new TextWebSocketFrameHandler());}}

HttpRequestHandler.java

注意:记得换主页地址,我这里是绝对路径

package webChat;import java.io.File;
import java.io.RandomAccessFile;import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.stream.ChunkedFile;
import io.netty.handler.stream.ChunkedNioFile;//用来区分用户的请求是html聊天页面,还是发送WebSocket请求
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest>{private final String chatUri; //WebSocket的请求地址(聊天的地址)private static File indexfile;static {//  URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation();try {String path = "/home/ffy/API/index.html";path = !path.contains("file:")?path:path.substring(5);indexfile = new File(path);System.out.println("主页路径为:"+path);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}public HttpRequestHandler(String chatUri) {// TODO Auto-generated constructor stubthis.chatUri = chatUri; }@Overrideprotected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {if(chatUri.equalsIgnoreCase(request.getUri())) {//用户处理的是WebSocket的地址,那么就不做处理,将请求转到管道的下一个处理环节ctx.fireChannelRead(request.retain());}else {//如果不相等,那么就读取聊天界面,并将页面的内容写回给浏览器if(HttpHeaders.is100ContinueExpected(request)) {//100继续,200成功FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE);ctx.writeAndFlush(response);System.out.println("继续"); }//读取默认的WebSocketChatClient.html页面RandomAccessFile file = new RandomAccessFile(indexfile, "r");//写一个HttpResponse,并设置头部 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);ctx.write(new ChunkedNioFile(file.getChannel()));ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);if(!keepAlive) {future.addListener(ChannelFutureListener.CLOSE);}file.close(); }}
}

TextWebSocketFrameHandler.java

package webChat;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;public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//当有客户端链接时执行@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {// TODO Auto-generated method stubChannel inComing  = ctx.channel(); //获得客户端通道//遍历通道队列,发消息for(Channel ch:channels) {if(ch!=inComing) {ch.writeAndFlush(new TextWebSocketFrame("欢迎"+inComing.remoteAddress()+"进入聊天室"+"\n"));}}channels.add(inComing);//将新加入的添加如队列}//当客户端断开链接时执行@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {// TODO Auto-generated method stubChannel outPuting  = ctx.channel(); //获得客户端通道//遍历通道队列,发消息for(Channel ch:channels) {if(ch!=outPuting) {ch.writeAndFlush(new TextWebSocketFrame(outPuting.remoteAddress()+"退出聊天室"+"\n"));}}channels.remove(outPuting);//将离开的客户端删除出队列}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame msg) throws Exception {// TODO Auto-generated method stub//通过上下文对象可以知道谁发过来的数据Channel client = channelHandlerContext.channel();for(Channel ch:channels) {if(ch!=client) {ch.writeAndFlush(new TextWebSocketFrame("用户 "+client.remoteAddress()+" 发来消息:"+msg.text()+"\n"));}else {ch.writeAndFlush(new TextWebSocketFrame("我说:"+msg.text()+"\n"));}}      }}

Web前端

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>多人聊天室</title>
</head>
<script>var socket;if(!window.WebSocket) {alert("不支持");}if(window.WebSocket){socket = new WebSocket("ws://localhost:8080/chat");socket.onmessage = function(event){var textArea = document.getElementById("msgText");textArea.value = textArea.value+"\n"+event.data;}socket.onopen = function(event){var textArea = document.getElementById("msgText");textArea.value = "已链接服务器";}socket.onclose = function(event){var textArea = document.getElementById("msgText");textArea.value = textArea.value+"\n"+"退出聊天室";}socket.onerror = function(event){alert("出错");}}function send(msg){if(!window.WebSocket) {alert("send");return false;}if(socket.readyState == WebSocket.OPEN){socket.send(msg);var textArea = document.getElementById("msgText");}else{alert("链接没有打开")}}
</script>
<body><form onsubmit = "return false" action=""><h1>WebSocket多人聊天室</h1><textarea id = "msgText" rows="20" cols="50"></textarea><br><input type = "text" name = "msg" style = "width:300px"/><input type = "button" value = "发送" onclick = "send(this.form.msg.value)"><input type = "button" value = "清空" onclick = "javascript:document.getElementById('msgText').value=''"/></form>
</body>
</html>

运行结果

注意:最好使用火狐,谷歌进行测试,别的浏览器可能因为版本过低的原因不支持WebSocket协议

Netty+WebSocket服务器完成Web聊天室(纯文字)相关推荐

  1. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  2. 御神楽的学习记录之Springboot+netty实现Web聊天室

    文章目录 前言 一.Netty简介 1.介绍 二.Web聊天室实现 1.Idea项目创建 2.java类编写 3.html测试 总结 参考 前言 WebSocket是一种在单个TCP连接上进行全双工通 ...

  3. WebSocket请求过程分析及实现Web聊天室

    WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex ...

  4. Spring和WebSocket整合并建立简单的Web聊天室

    Spring和WebSocket整合并建立简单的Web聊天室 官方主页 Spring WebSocket 一.概述 WebSocket 是一种网络通信协议.RFC6455 定义了它的通信标准. Web ...

  5. java web 聊天室_Java和WebSocket开发网页聊天室

    小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ 一.项目简介WebSocket是HTML5一种新的 ...

  6. 【项目设计】基于WebSocket的Web聊天室

    文章目录 1. 项目简介 2. 数据库表的设计 3. 实体类以及工具类的设计 3.1 实体类model 3.1.1 lombok的使用 3.2 工具类util 3.2.1 DBUtil 3.2.2 W ...

  7. WebSocket实现简单的web聊天室

    WebSocket实现简单的web聊天室 1.需要Tomcat7.0所以服务器 2.需要JDK7.0 3.手工加入Tomcat7.0中lib目录下的一下三个包catalina.jar.tomcat-c ...

  8. 基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器——《干饭聊天室》

    基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器--<干饭聊天室> 在这里首先感谢前端小伙伴飞鸟 前端技术请看一款基于React.C++,使用TCP/HTTP协 ...

  9. 基于Tomcat7、Java、WebSocket的服务器推送聊天室

    2019独角兽企业重金招聘Python工程师标准>>> 基于Tomcat7.Java.WebSocket的服务器推送聊天室 转载于:https://my.oschina.net/u/ ...

  10. 使用Node+websocket实现简易1v1聊天室(前端+服务器)

    使用Node+websocket实现简易1v1聊天室(前端+服务器) 前提: 安装好node环境~~~ 可使用 node -v 和 npm -v 查看是否装好 实现逻辑: 用户A 用户B 服务器 用户 ...

最新文章

  1. OpenCV 遇到的问题
  2. ubuntu下python+tornado+supervisor+nginx部署
  3. Eclipse 安装配置指南
  4. 汉字内码UNICODE转换表
  5. pyplot 画多个图时搅合到了一起_聚镁Art0X丨马歇尔-他与他的黑人画
  6. 动态规划(Dynamic Programming)例题步骤详解
  7. Spring Boot 中使用 MyBatis 整合 Druid 多数据源
  8. C#.NET里面抽象类和接口有什么区别
  9. android View 绘制完成监听
  10. python lambda函数两个列表大小关系_python lambda结合列表推导式?
  11. WordPress快速开发的博客平台
  12. SQL Server 审计操作概念
  13. Atitit 表达式概论 艾提拉著 目录 1. 表达式分类 2 1.1. 条件表达式 ?:三元符号 2 1.2. 中缀表达式 前缀 后缀表达式 2 1.3. S表达式 2 1.4. 《精通lamb
  14. luogu P4556 [Vani有约会]雨天的尾巴
  15. 通信专业能报国考的计算机类吗,通信工程专业可以报考计算机科学技术类公务员职位么...
  16. 文献翻译:《Basel Face Model 2009》(一种用于姿态和光照不变人脸识别的三维人脸模型,3DMM,BFM2009)
  17. UI设计师必备素材|功能性UI线性图标
  18. 呼叫中心与网络电话的区别,看完懂了!
  19. 使用pandas的话,如何直接删除这个表格里面X值是负数的行?
  20. 爱宝A-1180热转印条码打印机 打印乱码,对不齐的问题

热门文章

  1. JAVA社区疫情防控系统毕业设计 开题报告
  2. Autojs之QQ 群发消息(是QQ 群发,不是QQ群 发)
  3. 36. 有效的数独(技巧)
  4. 基于POP3协议收取邮件
  5. 淘宝新店刚开详情页到底需要怎么做
  6. 125K非接触IC卡读卡头
  7. python爬虫区划代码表
  8. Centos7环境启动mongod报polkit服务启动失败
  9. java打开教程,jar文件打开教程
  10. 2020年阿里巴巴投资者大会集团CEO张勇演讲实录