Netty網(wǎng)絡(luò)編程實戰(zhàn)之開發(fā)聊天室功能
在 Netty網(wǎng)絡(luò)編程實戰(zhàn)1,搭建第一個Netty服務(wù)器中,使用curl作為客戶端訪問,下面將通過Netty實現(xiàn)客戶端,客戶端代碼依然采用Netty老套路 主程序類+自定義初始化器+自定義處理器三部分組成。
一、服務(wù)端
1、主程序類
package com.guor.demo.netty.chat;
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;
public class MyNettyServerTest {
public static void main(String[] args) throws InterruptedException {
/**
* EventLoopGroup:事件循環(huán)組,是一個線程池,也是一個死循環(huán),用于不斷地接收用戶請求;
* serverGroup:用戶監(jiān)聽及建立連接,并把每一個連接抽象為一個channel,最后再將連接交給clientGroup處理;
* clientGroup:真正的處理連接
*/
EventLoopGroup serverGroup = new NioEventLoopGroup();
EventLoopGroup clientGroup = new NioEventLoopGroup();
try {
// 服務(wù)端啟動時的初始化操作
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 1、將serverGroup和clientGroup注冊到服務(wù)端的Channel上;
// 2、注冊一個服務(wù)端的初始化器MyNettyServerInitializer;
// 3、該初始化器中的initChannel()方法會在連接被注冊到Channel后立刻執(zhí)行;
// 5、最后將端口號綁定到8080;
ChannelFuture channelFuture = serverBootstrap.group(serverGroup, clientGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MyNettyServerInitializer()).bind(8080).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
System.out.println(e);
}finally {
serverGroup.shutdownGracefully();
clientGroup.shutdownGracefully();
}
}
}
2、自定義初始化器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 自定義初始化器
*/
public class MyNettyServerInitializer extends ChannelInitializer<SocketChannel> {
// 連接被注冊到Channel后,立刻執(zhí)行此方法
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/**
* LengthFieldBasedFrameDecoder用于解析帶固定長度的數(shù)據(jù)報。
* TCP發(fā)送的數(shù)據(jù)規(guī)則:可以將數(shù)據(jù)進(jìn)行拆分或合并,因此對端接收到的數(shù)據(jù)報可能不是初始發(fā)送時的格式;
* 一般的做法是在包頭設(shè)置length長度,指明數(shù)據(jù)包的長度,再由接受方根據(jù)length拼接或剪裁收到的數(shù)據(jù),從而形成完整的數(shù)據(jù)包
*/
pipeline.addLast("LengthFieldBasedFrameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,8,0,8));
// 將上條語句的length加入傳遞的數(shù)據(jù)中心
pipeline.addLast("LengthFieldPrepender",new LengthFieldPrepender(8));
// 傳遞字符串的編碼解碼器
pipeline.addLast("StringDecoder",new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("StringEecoder",new StringEncoder(CharsetUtil.UTF_8));
// 增加自定義處理器MyNettyServerHandler,用于實際處理請求,并給出響應(yīng)
pipeline.addLast("MyNettyServerHandler",new MyNettyServerHandler());
}
}
3、自定義處理器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Scanner;
/**
* 自定義處理器
* Inbound代表"進(jìn)入"的請求
*/
public class MyNettyServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String receiveMsg) throws Exception {
// 通過ctx獲取客戶端的IP和端口號,并打印出客戶端發(fā)來的消息
System.out.println("服務(wù)端接收的請求來自:"+ctx.channel().remoteAddress()+",消息內(nèi)容:"+receiveMsg);
System.out.println("請向客戶端發(fā)送一條消息:");
String sendMsg = new Scanner(System.in).nextLine();
ctx.channel().writeAndFlush(sendMsg);
}
}
二、客戶端
1、主程序類
package com.guor.demo.netty.chat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* 主程序類
*/
public class MyNettyClientTest {
public static void main(String[] args) {
/**
* 服務(wù)端有兩個EventLoopGroup,serverGroup用于獲取連接并將連接分發(fā)給clientGroup,clientGroup負(fù)責(zé)處理連接;
* 對于客戶端而言,客戶端僅僅需要連接服務(wù)端即可
*/
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
// 客戶端啟動時的初始化操作
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new MyNettyClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
System.out.println(e);
}finally {
eventLoopGroup.shutdownGracefully();
}
}
}2、自定義初始化器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 自定義初始化器
*/
public class MyNettyClientInitializer extends ChannelInitializer<SocketChannel> {
// 連接被注冊后,立即執(zhí)行此方法
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/**
* LengthFieldBasedFrameDecoder用于解析帶固定長度的數(shù)據(jù)報。
* TCP發(fā)送的數(shù)據(jù)規(guī)則:可以將數(shù)據(jù)進(jìn)行拆分或合并,因此對端接收到的數(shù)據(jù)報可能不是初始發(fā)送時的格式;
* 一般的做法是在包頭設(shè)置length長度,指明數(shù)據(jù)包的長度,再由接受方根據(jù)length拼接或剪裁收到的數(shù)據(jù),從而形成完整的數(shù)據(jù)包
*/
pipeline.addLast("LengthFieldBasedFrameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,8,0,8));
// 將上條語句的length加入傳遞的數(shù)據(jù)中心
pipeline.addLast("LengthFieldPrepender",new LengthFieldPrepender(8));
// 傳遞字符串的編碼解碼器
pipeline.addLast("StringDecoder",new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("StringEecoder",new StringEncoder(CharsetUtil.UTF_8));
// 增加自定義處理器MyNettyClientHandler
pipeline.addLast("MyNettyClientHandler",new MyNettyClientHandler());
}
}3、自定義處理器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Scanner;
/**
* 自定義處理器
*/
public class MyNettyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String receiveMsg) throws Exception {
System.out.println("客戶端接收的請求來自:"+ctx.channel().remoteAddress()+",消息內(nèi)容:"+receiveMsg);
System.out.println("請向服務(wù)端發(fā)送一條消息:");
String sendMsg = new Scanner(System.in).nextLine();
ctx.channel().writeAndFlush(sendMsg);
}
public void channelActive(ChannelHandlerContext ctx) throws Exception{
ctx.writeAndFlush("第一條消息...");
}
}三、啟動服務(wù)端、客戶端
1、服務(wù)端:你好,我是服務(wù)端,哪吒編程

2、客戶端:我去,還真連上了,第一次使用Netty通話,真神奇
3、服務(wù)端:土包子

到此這篇關(guān)于Netty網(wǎng)絡(luò)編程實戰(zhàn)之開發(fā)聊天室功能的文章就介紹到這了,更多相關(guān)Netty聊天室內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用JSqlParser解析SQL語句應(yīng)用場景
JSqlParser是一個功能全面的Java庫,用于解析SQL語句,支持多種SQL方言,它可以輕松集成到Java項目中,并提供靈活的操作方式,本文介紹Java使用JSqlParser解析SQL語句總結(jié),感興趣的朋友一起看看吧2024-09-09
Java中ArrayList在foreach里remove的問題詳析
這篇文章主要給大家介紹了關(guān)于Java中ArrayList在foreach里remove問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧2018-09-09
MyBatis實現(xiàn)表連接查詢寫法(三種對應(yīng)關(guān)系)的方法總結(jié)
這篇文章主要介紹了MyBatis實現(xiàn)表連接查詢寫法(一對一關(guān)系、一對多關(guān)系、多對多關(guān)系)的方法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-01-01
Java實現(xiàn)WORD和PDF互相轉(zhuǎn)換以及數(shù)據(jù)填充示例
本文介紹了如何使用Java實現(xiàn)WORD和PDF的互轉(zhuǎn)以及數(shù)據(jù)填充功能,通過導(dǎo)入Aspose庫并使用其工具類,可以輕松實現(xiàn)WORD和PDF模板的填充和轉(zhuǎn)換,需要的朋友可以參考下2025-02-02

