客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)
后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息。为后面做铺垫
服务端实现
我们先完成服务端的逻辑,逻辑很简单,把客户端请求的内容加上服务器时间戳一并返回
public void run() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,4096)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(Integer.MAX_VALUE));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new Handler());
}
});
System.out.println("服务启动"+port);
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
}finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String data = (String) msg;
System.out.println("服务端接收数据:" + data);
data = data + " now:"+System.currentTimeMillis();
ctx.writeAndFlush(Unpooled.copiedBuffer(data, CharsetUtil.UTF_8));
}
服务端用了LineBasedFrameDecoder,以防止半包读写问题,客户端需要进行配合
客户端实现
这个案例客户端实现有两个难点:
1、客户端方法如何能请求到SimpleChannelInboundHandler,即把需要发送的消息传到handler中
2、如何能等待服务端响应同步返回
第一个问题其实是如何把客户端输入的参数传入handler,所以我们需要把参数以构造函数传参的方式以此传入ChannelInitializer、SimpleChannelInboundHandler,这样handler就可以拿到客户端输入的数据了
下面的Controller里需要提前把channel准备好,如果RPC框架需要考虑通道与服务的关系
@RestController
public class Controller {
static String ip = "127.0.0.1";
static int port = 9876;
static Bootstrap bootstrap;
static NioEventLoopGroup worker;
static {
bootstrap = new Bootstrap();
worker = new NioEventLoopGroup();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel .class)
.option(ChannelOption.TCP_NODELAY, true)
.remoteAddress(new InetSocketAddress(ip, port));
} @GetMapping("/nio/netty/server")
public String test(String param){
CustomerChannelInitializer customerChannelInitializer = new CustomerChannelInitializer(param);
bootstrap.handler(customerChannelInitializer);
ChannelFuture channelFuture= null;
String msg = "";
try {
channelFuture = bootstrap.connect().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
return customerChannelInitializer.getResponse();
}
}
public class CustomerChannelInitializer extends ChannelInitializer<SocketChannel> {
private String response;
private CountDownLatch countDownLatch;
private String param;
private ClientChannelHandlerAdapter clientChannelHandlerAdapter;
public CustomerChannelInitializer(String param) {
this.param = param;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
countDownLatch = new CountDownLatch(1);
clientChannelHandlerAdapter = new ClientChannelHandlerAdapter(param, this);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(clientChannelHandlerAdapter);
}
public String getResponse() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return response;
}
public void setResponse(String response) {
this.response = response;
countDownLatch.countDown();
}
}
public class ClientChannelHandlerAdapter extends SimpleChannelInboundHandler<ByteBuf> {
private String param;
private CustomerChannelInitializer customerChannelInitializer;
public ClientChannelHandlerAdapter(String param, CustomerChannelInitializer customerChannelInitializer) {
this.param = param;
this.customerChannelInitializer = customerChannelInitializer;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("客户端收到返回数据:" + msg.toString(CharsetUtil.UTF_8));
customerChannelInitializer.setResponse(msg.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端准备发送数据");
ctx.writeAndFlush(Unpooled.copiedBuffer(param + System.getProperty("line.separator"), CharsetUtil.UTF_8));
System.out.println("客户端发送数据完成");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("发生异常");
cause.printStackTrace();
ctx.close();
}
}
我们来看第二个问题,由于netty是异步的,所以无法等待到服务端响应后调用客户端的channelRead0方法,controller就已经返回了,导致了网页显示的返回结果一直是空
主线程通过CountDownLatch来锁住没有返回结果的线程,直到工作线程获得结果并解锁
@Override
protected void initChannel(SocketChannel ch) throws Exception {
countDownLatch = new CountDownLatch(1);
…… public String getResponse() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return response;
} public void setResponse(String response) {
this.response = response;
countDownLatch.countDown();
}
客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)的更多相关文章
- Netty学习4—NIO服务端报错:远程主机强迫关闭了一个现有的连接
1 发现问题 NIO编程中服务端会出现报错 Exception in thread "main" java.io.IOException: 远程主机强迫关闭了一个现有的连接. at ...
- 基于NIO的同步非阻塞编程完整案例,客户端发送请求,服务端获取数据并返回给客户端数据,客户端获取返回数据
这块还是挺复杂的,挺难理解,但是多练几遍,多看看研究研究其实也就那样,就是一个Selector轮询的过程,这里想要双向通信,客户端和服务端都需要一个Selector,并一直轮询, 直接贴代码: Ser ...
- Netty源码解析---服务端启动
Netty源码解析---服务端启动 一个简单的服务端代码: public class SimpleServer { public static void main(String[] args) { N ...
- NIO服务端主要创建过程
NIO服务端主要创建过程: 步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下: ServerSocketChannel ...
- 如何通过JavaScript构建Asp.net服务端控件
摘要 虽然ASP.NET的服务器控件一直被大家所诟病,但是用户控件(ACSX)在某些场景下还是非常有用的. 在一些极特珠的情况下,我们会使用JavaScript动态的构建页面中的控件,但假设遇到了我要 ...
- 一次http请求,谁会先断开TCP连接?什么情况下客户端先断,什么情况下服务端先断?
我们有2台内部http服务(nginx): 201:这台服务器部署的服务是account.api.91160.com,这个服务是供前端页面调用: 202:这台服务器部署的服务是hdbs.api.911 ...
- Query通过Ajax向PHP服务端发送请求并返回JSON数据
Query通过Ajax向PHP服务端发送请求并返回JSON数据 服务端PHP读取MYSQL数据,并转换成JSON数据,传递给前端Javascript,并操作JSON数据.本文将通过实例演示了jQuer ...
- Netty(6)源码-服务端与客户端创建
原生的NIO类图使用有诸多不便,Netty向用户屏蔽了细节,在与用户交界处做了封装. 一.服务端创建时序图 步骤一:创建ServerBootstrap实例 ServerBootstrap是Netty服 ...
- NIO服务端和客户端通信demo
代码转自 https://www.jianshu.com/p/a9d030fec081 服务端: package nio; import java.io.IOException; import jav ...
随机推荐
- 洛谷P3694 邦邦的大合唱站队/签到题
P3694 邦邦的大合唱站队/签到题 题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶 ...
- 2017-10-3 清北刷题冲刺班a.m
P99zhx a [问题描述]你是能看到第一题的 friends 呢.——hja怎么快速记单词呢?也许把单词分类再记单词是个不错的选择.何大爷给出了一种分单词的方法,何大爷认为两个单词是同一类的当这两 ...
- Jmeter-线程时间
随手记录下自己在学习遇到的线程时间问题 1.线程数14个,要求每秒进入2个线程,这设置准备时长因为7秒 及准备时长 = 线程数/每秒需要进入的线程数量 如上列中:准备时间 = 1 ...
- STP-16-根防护,BPDU防护和BPDU过滤
网络设计者很可能并不打算让终端用户在用于连接终端用户设备的Access端口上连接交换机.然而,这种事情有时却会发生——例如,有人可能需要大厅的会议室里有更多的端口,于是他觉得他可以把一个小的便宜的交换 ...
- RabbitMQ权限
RabbitMQ 引言 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议. MQ全称为Message Queue, 消息队 ...
- java insert mysql 中文乱码
jdbc:mysql://192.168.1.77:3306/db360?useUnicode=true&characterEncoding=UTF-8 drop database if ex ...
- spring容器中的beanName
1. 一个类实现多个接口 如下图中的JobService.java, 此时这个beanName=jobService, 没有包名,类名字首字母小写 可以使用下面三种方式获得这个bean IProce ...
- 宋宝华:swappiness=0究竟意味着什么?
http://mp.weixin.qq.com/s/BixMISiPz3sR9FDNfVSJ6w 本文解释swappiness的作用,以及swappiness=0究竟意味着什么. 内存回收 我们都知道 ...
- 安全性测试入门 (五):Insecure CAPTCHA 验证码绕过
本篇继续对于安全性测试话题,结合DVWA进行研习. Insecure Captcha不安全验证码 1. 验证码到底是怎么一回事 这个Captcha狭义而言就是谷歌提供的一种用户验证服务,全称为:Com ...
- 【Unity3D】简要分析unity3d中剪不断理还乱的yield
在学习unity3d的时候很容易看到下面这个例子: void Start () { StartCoroutine(Destroy()); } IEnumerator Destroy(){ yield ...