实现场景: 聊天

服务端,客户端A,客户端B,客户端C。当客户端发送消息给服务端后,服务端在将这条消息广播个所有客户端户端A,客户端B,客户端C。

需求1: 客户端上线后,会通知所有客户端上线。

如客户端A先建立连接,不需要通知。

当客户端B与服务端建立连接,服务端告诉A,客户端B上线。

A和B建立连接后,客户端C和服务端建立连接。服务端广播一条信息给A和B。

需求2: A、B、C都已经建立连接,当A发送一条给服务端,服务端广播这条消息给所有客户端,客户端A会提示这条消息是自己发送的。

一、服务端程序的编写

1、MyChartServer 类

public class MyChartServer {
public static void main(String[] args) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{ ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new MyChatServerInitializer()); ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

  

  

2、MyChatServerInitializer 类

public class MyChatServerInitializer extends ChannelInitializer<SocketChannel>{

    protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new MyChartServerHandle());
}
}

  

3、MyChartServerHandle 类

public class MyChartServerHandle extends SimpleChannelInboundHandler<String>{

    //用于保存所有Channel对象
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//channel为当前客户端发给服务端的channel
Channel channel = ctx.channel();
channelGroup.forEach(ch -> { if(channel != ch){
ch.writeAndFlush(channel.remoteAddress() + " 发送的消息:" + msg + "\n");
} else{
ch.writeAndFlush("[自己]" + msg + " \n");
}
}); } //表示连接建立
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//chanel可以理解成Connection
Channel channel = ctx.channel();
//广播消息给所有的客户端
channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 加入\n");
channelGroup.add(channel);
} //表示连接断掉了
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//广播消息给所有的客户端
channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 离开\n");
//下面这行代码Netty会自动调用
//channelGroup.remove(channel);
} //表示连接时活动状态
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//广播消息给所有的客户端
CommonUtil.println( channel.remoteAddress() + " 上线 \n");
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//广播消息给所有的客户端
CommonUtil.println( channel.remoteAddress() + " 下线 \n");
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();;
ctx.close();
}
}

  

  二、客户端程序编写

1、MyChatClient类

public class MyChatClient {
public static void main(String[] args) throws Exception{
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new MyChatClientInitializer()); //channel表示与服务端的Connection
Channel channel = bootstrap.connect("localhost",8899).sync().channel();
//不断读取客户端输入的内容
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for(;;){
channel.writeAndFlush(br.readLine() + "\r\n");
} }finally {
eventLoopGroup.shutdownGracefully();
}
}
}

  

  2、MyChatClientInitializer  类

public class MyChatClientInitializer  extends ChannelInitializer<SocketChannel> {

    protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new MyChartClientHandle());
}
}

  

3、MyChartClientHandle 类

public class MyChartClientHandle extends SimpleChannelInboundHandler<String> {

    // 对于客户端来说,输入来自控制台
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//仅仅打印来自服务端的信息
CommonUtil.println(msg); } }

  

三、测试

1、启动MyChartServer,然后启动MyChatClient

MyChartServer打印 59786 上线

2、再启动一个客户端

可以发现60635 上线,

并且第一个客户端提示 60635 加入

3、再启动一个客户端

提示60966上线

第一个客户端 提示60966 加入

第二个客户端 提示60966 加入

四、测试2

1、客户端A发送消息: 大家好,我是客户端A

客户端A接收到写信息:  [自己]大家好,我是客户端A

客户端B接收到信息

客户端C接收到的信息

自此,实现了通过多个Socket实现的通信的过程

Netty 多客户端连接与通信的更多相关文章

  1. netty(二)---客户端连接

    概述 先了解一下 netty 大概框架图 ,可以看到客户端的创建和服务端最大的区别 - 服务端传入两个 EventLoopGroup,客户端传入一个 EventLoopGroup - channel ...

  2. netty 处理客户端连接

    Netty如何处理连接事件 上文讲了Netty如何绑定端口,现在我们来阅读下netty如何处理connect事件.上文我们说了NioEventLoop启动后不断去调用select的事件,当客户端连接时 ...

  3. C# Socket服务端及多客户端连接通信实现

    服务端代码(控制台示例): static List<Socket> Sockets = new List<Socket>(); static void Main(string[ ...

  4. Netty源码分析 (六)----- 客户端连接接入accept过程

    通读本文,你会了解到1.netty如何接受新的请求2.netty如何给新请求分配reactor线程3.netty如何给每个新连接增加ChannelHandler netty中的reactor线程 ne ...

  5. 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式

    Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...

  6. 由浅入深了解Thrift之客户端连接池化续

    前文<由浅入深了解Thrift之客户端连接池化>中我们已经实现了服务调用端 连接的池化,实现的过于简陋,离实际的项目运用还很遥远.本文将在进一步改造,主要是两方面:1.服务端如何注册多个服 ...

  7. 配置ORACLE 客户端连接到数据库

    --================================= -- 配置ORACLE 客户端连接到数据库 --================================= Oracle ...

  8. 关系型数据库工作原理-客户端连接管理器(翻译自Coding-Geek文章)

    本文翻译自Coding-Geek文章:< How does a relational database work>.原文链接:http://coding-geek.com/how-data ...

  9. 通过java客户端连接hbase 注意事项

    1.通过Java客户端连接Hbase,其中hbase通过zookeeper去管理,需要注意的是客户端端口. 通过在浏览器端输入地址查看:http://192.168.3.206:60010/maste ...

随机推荐

  1. VLC祥解

    功能部份:   VLC媒體播放器的核心是libvlc ,它提供了界面,應用處理功能,如播放列表管理,音頻和視頻解碼和輸出,線程系統.所有libvlc源文件設在的/src目錄及其子目錄:   # con ...

  2. 1.live555源码分析----RSTPServer创建过程分析

    最近五一回家,终于有机会能安静的看一下流媒体这方面相关的知识,准备分析live555的源码,接下来会把我读源码的过程记录成博客,以供其他的同路人参考. 因为再读源码的过程中,并不是一路顺着读下来,往往 ...

  3. Java开发环境之IntelliJ IDEA

    查看更多Java开发环境配置,请点击<Java开发环境配置大全> 贰章:IntelliJ IDEA安装教程 1)去官网下载IDEA安装包 https://www.jetbrains.com ...

  4. oracle instantclient_12_2安装

    下载地址 http://www.oracle.com/technetwork/database/database-technologies/instant-client/downloads/index ...

  5. angular 升级到angular8 以及报错信息解决

    1.升级全局angular-cli npm install -g @angular/cli@latest 2.升级项目内 angular-cli (在需要升级的项目中运行) npm i @angula ...

  6. cnetos7安装mysql并开启慢日志查询

    参考博客地址https://www.cnblogs.com/luyucheng/p/6265594.html 安装部署(5.7) #下载Yum Repository curl -O https://r ...

  7. Nginx 脚本自动进行日志切割

    随着网站的运行时间越来越长,日志文件也会随之越来越大需要对Nginx日志进行切割,下面是每周六晚上自动进行日志切割的shell脚本 脚本如下: [root@jrjs ~]# vim /usr/loca ...

  8. CH340g使用说明详解

    用串口和CH340模块都可以让计算机和单片机进行通信,但是使用CH340更加方便,省去了使用串口的麻烦 CH340是一个USB总线的转接芯片,实现USB转串口.USB转IrDA红外或者USB转打印口. ...

  9. pdsh工具的使用

    pdsh的全称是parallel distributed shell,与pssh类似,pdsh可并行执行对远程目标主机的操作,在有批量执行命令或分发任务的运维需求时,使用这个命令可达到事半功倍的效果. ...

  10. hdu3486Interviewe(二分是错的)(ST算法RMQ + 判定上下界枚举)

    题目大意是找最小的m使得前m段中每一段的最大值相加严格大于k,每一段长度为[n/m](n/m向下取整,多余的后半部分部分n-m*[n/m]不要) 先给一段我一开始的思路,和网上许多题解思路一样,但其实 ...