Netty網(wǎng)絡(luò)編程實(shí)戰(zhàn)之開發(fā)聊天室功能
在 Netty網(wǎng)絡(luò)編程實(shí)戰(zhàn)1,搭建第一個Netty服務(wù)器中,使用curl作為客戶端訪問,下面將通過Netty實(shí)現(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,用于實(shí)際處理請求,并給出響應(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ò)編程實(shí)戰(zhàn)之開發(fā)聊天室功能的文章就介紹到這了,更多相關(guān)Netty聊天室內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring Boot 應(yīng)用通過Docker 來實(shí)現(xiàn)構(gòu)建、運(yùn)行、發(fā)布流程
這篇文章主要介紹了spring Boot 應(yīng)用通過Docker 來實(shí)現(xiàn)構(gòu)建、運(yùn)行、發(fā)布流程,圖文詳解,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-11-11手?jǐn)]一個Spring?Boot?Starter并上傳到Maven中央倉庫
本文主要介紹了手?jǐn)]一個Spring?Boot?Starter并上傳到Maven中央倉庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05超詳細(xì)的Spring Boot入門筆記(總結(jié))
本篇文章主要介紹了超詳細(xì)的Spring Boot入門筆記(總結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11吊打Java面試官之Lambda表達(dá)式 Stream API
這篇文章主要介紹了吊打Java之jdk8的新特性包括Lambda表達(dá)式、函數(shù)式接口、Stream API全面刨析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09java 中用split分割字符串,最后的空格等不被拆分的方法
下面小編就為大家?guī)硪黄猨ava 中用split分割字符串,最后的空格等不被拆分的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02