使用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.xdnf.cn/news/1938.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

链栈的基本操作实现

链栈的结构体定义 /*定义*/ typedef struct stacklnode{char data;struct stacklnode *next; }stacklnode,*linkstack; 初始化 /*初始化*/ void f1(linkstack *q){*qNULL; } 入栈 /*入栈*/ void f2(linkstack *q,char e ){linkstack p(linkstack)malloc(sizeof(stacklno…

c语言-常量和变量

文章目录 一、常量是什么&#xff1f;&#xff08;1&#xff09;整型常量&#xff1a;&#xff08;2&#xff09;实型常量&#xff1a;&#xff08;3&#xff09;字符常量&#xff1a;&#xff08;4&#xff09;字符串常量&#xff08;5&#xff09;地址常量 二、define 和 con…

UI设计软件全景:13款工具助力创意实现

选择恰当的UI设计工具对于创建美观且用户体验良好的应用程序界面至关重要。不同的APP功能可能需要不同的界面设计软件&#xff0c;但并非所有工具都需要精通&#xff0c;熟练掌握几个常用的就足够了。以下是13款APP界面设计软件&#xff0c;它们能够为你的团队提供绘制APP界面所…

mysql 8.0.39 Caused by: java.sql.SQLException: Illegal mix of collations 异常解决

java服务可以正常启动&#xff0c;页面发现查询报错Illegal mix of collations 报错信息&#xff1a; ### Cause: java.sql.SQLException: Illegal mix of collations (utf8mb4_general_ci,COERCIBLE) and (utf8mb4_0900_ai_ci,COERCIBLE) for operation ; uncategorized SQ…

C++友元类的分文件编写

在学习C的关于友元类的知识时&#xff0c;网课例程中是在main函数文件中编写实现&#xff0c;但是我们知道&#xff0c;一般情况下&#xff0c;类以及类的成员函数实现都是在不同文件中实现的&#xff0c;因此&#xff0c;我们会自然的想到友元类是如何在不同文件下编写实现的&…

vue2与vue3生命周期差异整理

1、vue3 与 vue2 生命周期对比 生命周期整体分为四个阶段&#xff0c;分别是&#xff1a;创建、挂载、更新、销毁&#xff0c;每个阶段都有两个钩子&#xff0c;一前一后。 生命周期Vue 2Vue 3说明创建阶段beforeCreatesetup开始创建组件之前,实例被创建&#xff0c;还没有初…

c语言之在结构体里面定义函数指针

还是在看redis3.0源码的时候&#xff0c;遇到如下问题&#xff1a; 这个时候我们回到list这个结构体的设计上面 首先我们必须要注意的是函数名字就可以看成指针地址。 所以下面我们写一个简单的实例看一下具体的用法&#xff1a; #include <stdio.h> #include <stdl…

代码随想录一刷——1.两数之和

当我们需要查询一个元素是否出现过&#xff0c;或者一个元素是否在集合里的时候&#xff0c;就要第一时间想到哈希法。 C&#xff1a; unordered_map class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int…

EDA二维码生成工具 V1.2

EDA二维码生成工具用于在原理图、PCB环境中生成矢量二维码&#xff0c;具有生成速度快、生成效率高的特点&#xff0c;支持中文字符、英文字符生成。 此工具可直接输出在原理图、原理图库文档、PCB、PCB库文档中&#xff0c;可同时输出多种格式&#xff0c;可在Altium …

鸿蒙生态崛起带来的机遇与挑战

目录 1.概述 2.生态崛起 2.1.鸿蒙生态的认知和了解 2.2.鸿蒙生态的崛起分析 2.3.开发者的机遇 2.4.华为开发者大会 3.鸿蒙生态开发的挑战 3.1.开发工具 3.2.技术难度 3.3.生态竞争 3.4.抓住机遇、应对挑战 4.鸿蒙生态未来发展趋势 4.1.发展趋势 4.2.18N 4.3.开发…

JavaCV 图像边缘检测 之 Sobel算子 算法

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

二百七十四、Kettle——ClickHouse中对错误数据表中进行数据修复(实时)

一、目的 在完成数据清洗、错误数据之后&#xff0c;需要根据修复规则对错误数据进行修复 二、Hive中原有代码 insert into table hurys_db.dwd_queue partition(day) selecta3.id,a3.device_no,a3.source_device_type,a3.sn,a3.model,a3.create_time,a3.lane_no,a3.lane_…

第2章 Android App开发基础

第 2 章 Android App开发基础 bilibili学习地址 github代码地址 本章介绍基于Android系统的App开发常识&#xff0c;包括以下几个方面&#xff1a;App开发与其他软件开发有什么不一 样&#xff0c;App工程是怎样的组织结构又是怎样配置的&#xff0c;App开发的前后端分离设计…

ARM base instruction -- csetm

Conditional Set Mask sets all bits of the destination register to 1 if the condition is TRUE, and otherwise sets all bits to 0. 如果条件为TRUE&#xff0c;则条件设置掩码将目标寄存器的所有位设置为1&#xff0c;否则将所有位设为0。 32-bit variant Applies w…

rom定制系列------小米8青春版定制安卓14批量线刷固件 原生系统

&#x1f49d;&#x1f49d;&#x1f49d;小米8青春版。机型代码platina。官方最终版为 12.5.1安卓10的版本。客户需要安卓14的固件以便使用他们的软件。根据测试&#xff0c;原生pixeExpe固件适配兼容性较好。为方便客户批量进行刷写。修改固件为可fast批量刷写。整合底层分区…

java项目之校园资料分享平台(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园资料分享平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 校园资料分享平台的主要…

Jmeter5.X性能测试

Jmeter5.X性能测试 文章目录 Jmeter5.X性能测试一、掌握Http基础协议1.1 浏览器的B/S架构和C/S架构1.2 HyperText Transfer Protocol 超文本传输协议1.3 超文本传输协议Http消息体拆分讲解1.4 HTTP的九种请求方法和响应码介绍1.5 Http请求头/响应头1.6 Http常见请求/响应头cont…

qt管理系统框架(好看界面、漂亮界面、好看的界面、漂亮的界面)

概述 最近一个项目用QT开发&#xff0c;然后找了美工帮设计了下界面。总算完工&#xff0c;后想一下干脆抽出一个基础框架&#xff0c;方便以后用。 功能 支持mysql、echarts。 支持加载动态权限菜单&#xff0c;轻松权限控制。 支持遮罩对话框、抽屉 支持开机启动动画界面 内…

力扣——相同的树(C语言)

1.题目&#xff1a; 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 2.原理&#xff1a; 这里直接将两个数的结点进行比较&#xff0c;向下递归&…

2024年双11买什么东西划算?2024年双十一必买清单好物大总结

双十一购物节&#xff0c;海量商品与诱人折扣并存&#xff0c;正是我们精打细算、理性消费的好时机。面对琳琅满目的爆款与折扣&#xff0c;记得先问问自己&#xff1a;这真的是我需要的吗&#xff1f;在日常中能否发挥其价值&#xff1f;基于这样的考量&#xff0c;我们精选了…