Netty 多客户端连接与通信
实现场景: 聊天
服务端,客户端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 多客户端连接与通信的更多相关文章
- netty(二)---客户端连接
概述 先了解一下 netty 大概框架图 ,可以看到客户端的创建和服务端最大的区别 - 服务端传入两个 EventLoopGroup,客户端传入一个 EventLoopGroup - channel ...
- netty 处理客户端连接
Netty如何处理连接事件 上文讲了Netty如何绑定端口,现在我们来阅读下netty如何处理connect事件.上文我们说了NioEventLoop启动后不断去调用select的事件,当客户端连接时 ...
- C# Socket服务端及多客户端连接通信实现
服务端代码(控制台示例): static List<Socket> Sockets = new List<Socket>(); static void Main(string[ ...
- Netty源码分析 (六)----- 客户端连接接入accept过程
通读本文,你会了解到1.netty如何接受新的请求2.netty如何给新请求分配reactor线程3.netty如何给每个新连接增加ChannelHandler netty中的reactor线程 ne ...
- 一个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 ...
- 由浅入深了解Thrift之客户端连接池化续
前文<由浅入深了解Thrift之客户端连接池化>中我们已经实现了服务调用端 连接的池化,实现的过于简陋,离实际的项目运用还很遥远.本文将在进一步改造,主要是两方面:1.服务端如何注册多个服 ...
- 配置ORACLE 客户端连接到数据库
--================================= -- 配置ORACLE 客户端连接到数据库 --================================= Oracle ...
- 关系型数据库工作原理-客户端连接管理器(翻译自Coding-Geek文章)
本文翻译自Coding-Geek文章:< How does a relational database work>.原文链接:http://coding-geek.com/how-data ...
- 通过java客户端连接hbase 注意事项
1.通过Java客户端连接Hbase,其中hbase通过zookeeper去管理,需要注意的是客户端端口. 通过在浏览器端输入地址查看:http://192.168.3.206:60010/maste ...
随机推荐
- 使用Prometheus针对自己的服务器采集自定义的参数
用一个简单的例子来说明. 我用express和http搭了一个最简单的服务器,监听在8081端口上. 在metrics endpoint上,我会打印出这个服务器从启动至今,服务了多少次请求.这里我只是 ...
- Redis_初识
一.简介 Redis(Remote Dictionary Server)本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作吧数据库flush到硬盘上 ...
- springboot注解@NotNull,@NotBlank,@Valid自动判定空值
一.前言 搭建springboot项目,我们都是采用的Restful接口,那么问题来了,当前端调用接口或者是其他项目调用时,我们不能单一靠调用方来控制参数的准确性,自己也要对一些非空的值进行判定. 二 ...
- reboot 示例代码
#include <stdio.h> #define LINUX_REBOOT_CMD_RESTART 0x01234567 int main() { reboot(LINUX_REBOO ...
- 使用flannel+canal实现k8s的NetworkPolicy
目录 1.NetworkPolicy概述 2.NetworkPolicy策略模型 3.NetworkPolicy默认策略 4.NetworkPolicy的实现 5.使用flannel+canal实现k ...
- $(...) is null
删冲突插件,jquery作为基础库,当然是没有理由被删了.这个方法最直接了. (2)将jquery的$方法改名,具体改名方法如下: jQuery.noConflict();//将变量$的控制权让渡给给 ...
- hexo博客微博图床失效解决办法
最近在v2ex上看到有人说微博图床开始限制外链了.当时我看了看我的博客,图片还好.第二天再去看的时候就挂了.评论里有人说改一个no-ferrer能解决. 记录一下操作方法. N:\blog\theme ...
- springboot+Mybatis+MySql 一个update标签中执行多条update sql语句
Mysql是不支持这种骚操作的,但是不代表并不能实现,只需要在jdbc配置文件中稍微做一下修改就行. driver=com.mysql.jdbc.Driver url=jdbc:mysql://127 ...
- Alpha冲刺笔记十:第十天
课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(十天冲刺) 团队名称:葫芦娃队 作业目标:在十天冲刺里对每天的任务进行总结. 随笔汇总:https://www.cnblogs ...
- 12.基于vue-router的案例
案例分析 用到的路由技术要点: 路由的基础用法 嵌套路由 路由重定向 路由传参 编程式导航 根据项目的整体布局划分好组件结构,通过路由导航控制组件的显示 1.抽离并渲染 App根组件 2.将左侧菜 单 ...