使用Netty实现一个简单的聊天服务器

✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。
🍎个人主页:Hhzzy99
🍊个人信条:坚持就是胜利!
💞当前专栏:Netty
🥭本文内容:Netty实现一个简单的聊天服务器。

文章目录

  • 使用Netty实现一个简单的聊天服务器
    • 一、项目初始化与依赖配置
      • 1.1 创建Maven项目并添加依赖
    • 二、服务器端代码实现
      • 2.1 `ChatServer` - 服务端启动类
      • 2.2 `ChatServerHandler` - 消息处理器
    • 三、控制台客户端实现
      • 3.1 `ChatClient` - 客户端启动类
      • 3.2 `ChatClientHandler` - 客户端消息处理器
    • 四、启动与测试
      • 5.1 启动服务器和控制台客户端
  • 结语


使用Netty实现一个简单的聊天服务器

在本篇教程中,我们将通过Java的Netty框架实现一个简单的聊天服务器。

Netty是一款基于NIO(非阻塞IO)开发的网络框架,适用于高性能、高并发的应用场景。本文会详细展示如何通过Netty实现聊天服务器,实现控制台版客户端。

一、项目初始化与依赖配置

1.1 创建Maven项目并添加依赖

我们使用Maven管理依赖,首先在pom.xml文件中添加Netty依赖:

<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.0</modelVersion><groupId>com.example</groupId><artifactId>netty-chat</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- Netty 依赖 --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.68.Final</version></dependency></dependencies>
</project>

这样配置完成后,Maven会自动下载Netty的相关包。

二、服务器端代码实现

服务器端负责接受客户端连接、广播消息,并在客户端断开时进行处理。接下来我们编写服务器端启动类和处理器。

2.1 ChatServer - 服务端启动类

ChatServer类用于配置Netty服务器的基础设置,包括监听的端口、通道类型(NIO模式)和处理器链。服务器会监听指定端口,并通过处理器将消息分发给各客户端。

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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;public class ChatServer {private final int port;public ChatServer(int port) {this.port = port;  // 设置服务器监听的端口}public void start() throws InterruptedException {// bossGroup:接受客户端连接的线程组EventLoopGroup bossGroup = new NioEventLoopGroup(1);// workerGroup:处理客户端连接的数据传输EventLoopGroup workerGroup = new NioEventLoopGroup();try {// ServerBootstrap用于启动和配置Netty服务器ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup)  // 设置boss和worker线程组.channel(NioServerSocketChannel.class)  // 指定NIO传输的通道类型.option(ChannelOption.SO_BACKLOG, 1024)  // 设置队列容量.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());// 为每个客户端连接配置消息处理器ch.pipeline().addLast(new ChatServerHandler());}});// 绑定端口并启动服务器ChannelFuture f = b.bind(port).sync();System.out.println("Chat server started on port " + port);// 等待服务器关闭f.channel().closeFuture().sync();} finally {// 关闭资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {int port = 8080;  // 定义服务器监听的端口号new ChatServer(port).start();  // 启动服务器}
}

2.2 ChatServerHandler - 消息处理器

ChatServerHandler类负责管理客户端连接和消息的广播。我们会在客户端加入时广播“用户加入”消息,并在消息接收时进行广播。

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;public class ChatServerHandler extends ChannelInboundHandlerAdapter  {// 使用ChannelGroup来保存所有的连接,便于消息广播private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);// 当新的客户端连接加入时调用@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {Channel incoming = ctx.channel();// 广播消息给所有已连接的客户端,通知新的连接加入for (Channel channel : channels) {channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " joined\n");}channels.add(incoming);  // 将新连接加入ChannelGroup}// 当客户端断开连接时调用@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {Channel outgoing = ctx.channel();// 广播消息给所有已连接的客户端,通知连接已断开for (Channel channel : channels) {channel.writeAndFlush("[SERVER] - " + outgoing.remoteAddress() + " left\n");}channels.remove(outgoing);  // 从ChannelGroup中移除断开的连接}// 当服务器接收到客户端发来的消息时调用@Overridepublic void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {Channel incoming = ctx.channel();// 使用StringDecoder与StringEncoder编码解码消息,所以直接字符串String msg = (String) message;System.out.println("[client] : " + msg);// 向其他客户端广播消息,当前发送者不接收自己的消息for (Channel channel : channels) {if (channel != incoming) {channel.writeAndFlush("[" + incoming.remoteAddress() + "] " + msg + "\n");} else {channel.writeAndFlush("[you] " + msg + "\n");}}}// 当出现异常时调用@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();  // 关闭连接}
}

三、控制台客户端实现

在实现浏览器客户端之前,我们先用Java创建一个简单的控制台客户端,以便在控制台中测试服务器的基本功能。通过这种方式,我们可以在不使用HTML页面的情况下,验证服务器的消息广播和客户端连接是否正常工作。

3.1 ChatClient - 客户端启动类

ChatClient类用于建立与服务器的连接,并在连接成功后允许用户发送消息。

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
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;public class ChatClient {private final String host;private final int port;public ChatClient(String host, int port) {this.host = host;this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new ChatClientHandler());}});Channel channel = b.connect(host, port).sync().channel();Scanner scanner = new Scanner(System.in);System.out.println("Enter your messages (type 'exit' to quit):");while (true) {String message = scanner.nextLine();if ("exit".equalsIgnoreCase(message)) {break;}channel.writeAndFlush(message + "\n");}} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {new ChatClient("localhost", 8080).start();}
}

3.2 ChatClientHandler - 客户端消息处理器

ChatClientHandler类用于接收服务器的消息并在控制台显示。

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class ChatClientHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println(msg);  // 输出从服务器收到的消息}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

四、启动与测试

5.1 启动服务器和控制台客户端

  1. 启动ChatServer
  2. 启动ChatClient,进行消息测试。

我们启动服务端ChatServer
chatServer

接着再启动客户端ChatClient
chatClient

客户端ChatClient发送消息,并且服务端转发回来( [YOU] 那一行就是服务端转发回来的):
在这里插入图片描述
服务端接收到消息:
在这里插入图片描述


结语

本文通过实现简单的控制台客户端和HTML客户端,让大家更好地理解Netty服务器的工作原理。希望对大家有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/59653.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用discuz开发一款网站自动秒收录源码功能更强悍更安全

用discuz开发一款网站自动秒收录源码功能更强悍更安全 在当今数字化时代&#xff0c;网站的曝光率和访问量是衡量其成功与否的重要指标。为了提升网站的曝光率&#xff0c;自动秒收录技术应运而生。而Discuz平台作为知名的社区管理系统&#xff0c;为开发者提供了丰富的功能和…

绿色能源发展关键:优化风电运维体系

根据QYResearch调研团队最新发布的《全球风电运维市场报告2023-2029》显示&#xff0c;预计到2029年&#xff0c;全球风电运维市场的规模将攀升至307.8亿美元&#xff0c;并且在接下来的几年里&#xff0c;其年复合增长率&#xff08;CAGR&#xff09;将达到12.5%。 上述图表及…

ER图的规范画法

ER图相较其他结构化分析方法和面向对象分析方法的图结构来说&#xff0c;还是相对容易不少&#xff0c;不过各位也要注重细节的严谨性。 entity&#xff0c;实体&#xff1b;relationship&#xff0c;关系&#xff1b;相信各位的英语水平不难理解ER图的功能&#xff0c;椭圆代表…

【静态页面】尚品汇 1、设计稿分析及资源准备

目录 1. 准备工作2. 理解设计3. 规划项目结构 1. 准备工作 安装必要的工具&#xff1a;确保你的开发环境已经准备好&#xff0c;包括文本编辑器&#xff08;如 VSCode&#xff09;、浏览器等。获取设计文件&#xff1a;获取UI设计稿或者设计文件链接&#xff0c;并确保可以访问…

基于MPPT最大功率跟踪的光伏发电蓄电池控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MPPT最大功率跟踪的光伏发电蓄电池控制系统simulink建模与仿真。本系统包括PV模块&#xff0c;电池模块&#xff0c;电池控制器模块&#xff0c;MPPT模块&#xff0c;PWM模…

nginx代理 proxy_pass

一、location 包含 location /api/ {proxy_pass http://127.0.0.1:85;} 二、location 不包含 location /api/ {proxy_pass http://127.0.0.1:85/;} 三、locaion 包含 location /api {proxy_pass http://127.0.0.1:85;}四、location 包含 location /api {proxy_pass http://127.…

MongoDB笔记01-概念与安装

文章目录 前言一、MongoDB相关概念1.1 业务应用场景具体的应用场景什么时候选择MongoDB 1.2 MongoDB简介1.3 体系结构1.4 数据模型1.5 MongoDB的特点 二、本地单机部署2.1 Windows系统中的安装启动第一步&#xff1a;下载安装包第二步&#xff1a;解压安装启动1.命令行参数方式…

IMS高压发生器维修高压电源维修XRG100/1000

IMS高压发生器的硬件组成&#xff1a; 高压控制发生器主要由高压发生器和高压控制器两部分组成。高压控制器是控制调节X射线管管电压和管电流的机构,高压发生器是管电压和管电流产生的执行机构,通过高压控制器对高压发生器进行控制调节,通过高压电缆将高压发生器与X射线管连接…

133.鸿蒙基础01

鸿蒙基础 1.自定义构建函数1. 构建函数-[Builder ](/Builder )2. 构建函数-传参传递(单向)3. 构建函数-传递参数(双向)4. 构建函数-传递参数练习5. 构建函数-[BuilderParam ](/BuilderParam ) 传递UI 2.组件状态共享1. 状态共享-父子单向2. 状态共享-父子双向3. 状态共享-后代组…

MybatisPlus入门(八)MybatisPlus-DQL编程控制

一、字段映射与表名映射 数据库表和实体类名称一样自动关联&#xff0c;数据库表和实体类有部分情况不一样。 问题一&#xff1a;表名与编码开发设计不同步&#xff0c;表名和实体类名称不一致。 解决办法&#xff1a; 在模型类上方&#xff0c;使用TableName注解&#xf…

RNN中的梯度消失与梯度爆炸问题

梯度消失与梯度爆炸问题 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一类具有短期记忆能力的神经网络&#xff0e;在循环神经网络中&#xff0c;神经元不但可以接受其他神经元的信息&#xff0c;也可以接受自身的信息&#xff0c;形成具有…

Trimble X12三维激光扫描仪正在改变游戏规则【上海沪敖3D】

Trimble X12 三维激光扫描仪凭借清晰、纯净的点云数据和亚毫米级的精度正在改变游戏规则。今天的案例我们将与您分享&#xff0c;X12是如何帮助专业测量咨询公司OR3D完成的一个模拟受损平转桥运动的项目。 由于习惯于以微米为单位工作&#xff0c;专业测量机构OR3D是一家要求…

从分析Vue实例生命周期开始,剖析Vue页面跳转背后执行过程

文章目录 1.概要2.Vue实例生命周期3.生命周期函数解释4.存在父子组件情况页面执行过程5. 分析路由跳转页面执行过程6.扩展补充7.小结 1.概要 本文旨在分析Vue页面进行路由切换时&#xff0c;Vue背后的运行过程&#xff0c;旨在让大家更加清晰地明白Vue页面运行过程中钩子方法的…

git提交冲突的原因及解决方案

一、场景一 1.冲突原因 提交者的版本库 < 远程库 要保障提交者的版本库信息和远程仓库是一致的 2.解决方案 实现本地同步git pull,再提交代码&#xff08;最好每次git push之前都git pull一下&#xff0c;防止这种情况的出现&#xff09; 场景二 1.冲突原因 别人跟你…

【LeetCode】【算法】142. 环形链表II

142环形链表II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#x…

HT7182 21V,14A高效升压转换器

1、特征 输入电压范围:2.7V-21V 输出电压范围:最高21V 固定开关频率:350kHz 可编程峰值电流:最高14A 高转换效率 95% (PVIN 12V, VOUT20V, IOUT 2A) 94% (PVIN 12V, VOUT20V, IOUT 4.5A) 93% (PVIN 7.2V, VOUT12V, IOUT 1.5A) 90% (PVIN 7.2V, VOUT12V, IOUT 5A) 96% (PVI…

《野狗子:裂头怪》角色升级注意事项分享

《野狗子&#xff1a;裂头怪》中的角色升级是游戏里非常重要的事情&#xff0c;不过升级需要注意的事情就是如果你找到一个自己喜欢的角色&#xff0c;我们强烈建议你查看他们的所有被动技能&#xff0c;并尽早解锁一些! 野狗子裂头怪角色升级需要注意什么 在《野狗子 Slitter…

大语言模型训练的全过程:预训练、微调、RLHF

一、 大语言模型的训练过程 预训练阶段&#xff1a;PT&#xff08;Pre training&#xff09;。使用公开数据经过预训练得到预训练模型&#xff0c;预训练模型具备语言的初步理解&#xff1b;训练周期比较长&#xff1b;微调阶段1&#xff1a;SFT&#xff08;指令微调/有监督微调…

MySQL中,GROUP BY 分组函数

文章目录 示例查询&#xff1a;按性别分组统计每组信息示例查询&#xff1a;按性别分组显示详细信息示例查询&#xff1a;按性别分组并计算平均年龄,如果你还想统计每个性别的平均年龄&#xff0c;可以结合AVG()函数&#xff1a;说明 示例查询&#xff1a;按性别分组统计每组信…

兰空图床配置域名访问

图床已经创建完毕并且可以访问了&#xff0c;但是使用IP地址多少还是差点意思&#xff0c;而且不方便记忆&#xff0c;而NAT模式又没法直接像普通服务器一样DNS解析完就可以访问。 尝试了很多办法&#xff0c;nginx配置了半天也没配好&#xff0c;索性直接重定向&#xff0c;反…