Netty5-应答服务器
需求:
服务端:接收客户端请求,返回当前系统时间
客户端:发起时间请求
服务端
package org.zln.netty.five.timer; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 时间服务器服务端
* Created by sherry on 16/11/5.
*/
public class TimerServer {
/**
* 服务端绑定端口号
*/
private int PORT; public TimerServer(int PORT) {
this.PORT = PORT;
} /**
* 日志
*/
private static Logger logger = LoggerFactory.getLogger(TimerServer.class); public void bind() {
/*
NioEventLoopGroup是线程池组
包含了一组NIO线程,专门用于网络事件的处理
bossGroup:服务端,接收客户端连接
workGroup:进行SocketChannel的网络读写
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
/*
ServerBootstrap:用于启动NIO服务的辅助类,目的是降低服务端的开发复杂度
*/
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)//配置TCP参数,能够设置很多,这里就只设置了backlog=1024,
.childHandler(new TimerServerInitializer());//绑定I/O事件处理类
logger.debug("绑定端口号:" + PORT + ",等待同步成功");
/*
bind:绑定端口
sync:同步阻塞方法,等待绑定完成,完成后返回 ChannelFuture ,主要用于通知回调
*/
ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();
logger.debug("等待服务端监听窗口关闭");
/*
closeFuture().sync():为了阻塞,服务端链路关闭后才退出.也是一个同步阻塞方法
*/
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
} finally {
logger.debug("优雅退出,释放线程池资源");
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
package org.zln.netty.five.timer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel; /**
* Created by sherry on 16/11/5.
*/
public class TimerServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new TimerServerHandler());
}
}
package org.zln.netty.five.timer; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date; /**
* Handler主要用于对网络事件进行读写操作,是真正的业务类
* 通常只需要关注 channelRead 和 exceptionCaught 方法
* Created by sherry on 16/11/5.
*/
public class TimerServerHandler extends ChannelHandlerAdapter { /**
* 日志
*/
private Logger logger = LoggerFactory.getLogger(TimerServerHandler.class); @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//ByteBuf,类似于NIO中的ByteBuffer,但是更强大
ByteBuf reqBuf = (ByteBuf) msg;
//获取请求字符串
String req = getReq(reqBuf);
logger.debug("From:"+ctx.channel().remoteAddress());
logger.debug("服务端收到:" + req); if ("GET TIME".equals(req)){
String timeNow = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date());
String resStr = "当前时间:" + timeNow; //获取发送给客户端的数据
ByteBuf resBuf = getRes(resStr); logger.debug("服务端应答数据:\n" + resStr);
ctx.write(resBuf);
}else {
//丢弃
logger.debug("丢弃");
ReferenceCountUtil.release(msg);
} } /**
* 获取发送给客户端的数据
*
* @param resStr
* @return
*/
private ByteBuf getRes(String resStr) throws UnsupportedEncodingException {
byte[] req = resStr.getBytes("UTF-8");
ByteBuf pingMessage = Unpooled.buffer(req.length);
//将字节数组信息写入到ByteBuf
pingMessage.writeBytes(req); return pingMessage;
} /**
* 获取请求字符串
*
* @param buf
* @return
*/
private String getReq(ByteBuf buf) {
byte[] con = new byte[buf.readableBytes()];
//将ByteByf信息写出到字节数组
buf.readBytes(con);
try {
return new String(con, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将消息发送队列中的消息写入到SocketChannel中发送给对方
logger.debug("channelReadComplete");
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//发生异常时,关闭 ChannelHandlerContext,释放ChannelHandlerContext 相关的句柄等资源
logger.error("exceptionCaught");
ctx.close();
}
}
客户端
package org.zln.netty.five.timer; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 时间服务器客户端
* Created by sherry on 16/11/5.
*/
public class TimerClient {
/**
* 日志
*/
private Logger logger = LoggerFactory.getLogger(TimerServer.class); private String HOST;
private int PORT; public TimerClient(String HOST, int PORT) {
this.HOST = HOST;
this.PORT = PORT;
} public void connect(){
//配置客户端NIO线程组
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new TimerClientInitializer());
//发起异步连接操作
logger.debug("发起异步连接操作 - start");
ChannelFuture channelFuture = bootstrap.connect(HOST,PORT).sync();
logger.debug("发起异步连接操作 - end");
//等待客户端链路关闭
logger.debug("等待客户端链路关闭 - start");
channelFuture.channel().closeFuture().sync();
logger.debug("等待客户端链路关闭 - end");
} catch (InterruptedException e) {
logger.error(e.getMessage(),e);
}finally {
//优雅的关闭
eventLoopGroup.shutdownGracefully();
}
}
}
package org.zln.netty.five.timer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel; /**
* Created by sherry on 16/11/5.
*/
public class TimerClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimerClientHandler());
}
}
package org.zln.netty.five.timer; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; /**
* Created by sherry on 16/11/5.
*/
public class TimerClientHandler extends ChannelHandlerAdapter { /**
* 日志
*/
private Logger logger = LoggerFactory.getLogger(TimerClientHandler.class); @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.debug("客户端连接上了服务端"); //发送请求
ByteBuf reqBuf = getReq("GET TIME"); ctx.writeAndFlush(reqBuf);
} /**
* 将字符串包装成ByteBuf
* @param s
* @return
*/
private ByteBuf getReq(String s) throws UnsupportedEncodingException {
byte[] data = s.getBytes("UTF-8");
ByteBuf reqBuf = Unpooled.buffer(data.length);
reqBuf.writeBytes(data);
return reqBuf;
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
String resStr = getRes(byteBuf);
logger.debug("客户端收到:"+resStr);
} private String getRes(ByteBuf buf) {
byte[] con = new byte[buf.readableBytes()];
buf.readBytes(con);
try {
return new String(con, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
关于ByteBuf的读写,面向的都是ByteBuf,所以对于 read,从ByteBuf中读出来,将数据给字节数组,对于写,将数据从字节数组写入到ByteBuf中
将字符串转化为ByteBuf,有更简单的方法
ByteBuf byteBuf = Unpooled.copiedBuffer(resStr.getBytes("UTF-8"));
Netty5-应答服务器的更多相关文章
- python网络编程【三】(网络服务器)
建立一个服务器需要以下4步: 1.建立socket对象. 2.设置socket选项(可选的) 3.绑定到一个端口(同样,也可以是一个指定的网卡). 4.侦听连接. 下面代码片段可以实现这些功能: ho ...
- linux web服务器及LAMP动态网站平台搭建
(vim补:)vim另存为:x,x w 目标文件路径vim批量删除x:x,x d Web服务:基于B/S架构的web通信服务端:支持HTTP协议的网页提供程序客户端:按标记规范显示网页的浏览器程序客户 ...
- 1. 通过DHCP服务器动态获取IP地址之后无法上网的解决方法
故障:内网正常,在同一个局域网内的其它PC端通过DHCP获取IP地址并且可以正常上网. 1.通过wireshark抓包,使用ipconfig /renew时,wireshark内出现DHCP请求服务, ...
- linux服务器开发三(网络编程)
网络基础 协议的概念 什么是协议 从应用的角度出发,协议可理解为"规则",是数据传输和数据的解释的规则. 假设,A.B双方欲传输文件.规定: 第一次,传输文件名,接收方接收到文件名 ...
- DNS服务器原理简述、搭建主/从DNS服务器并实现智能解析
1. TLD:Top Level Domain 顶级域名 组织域:.com, .net, .org, .gov, .edu, .mil 国家域:.iq, .tw, .hk, .jp, .cn, ... ...
- Java Netty 4.x 用户指南
问题 今天,我们使用通用的应用程序或者类库来实现互相通讯,比如,我们经常使用一个 HTTP 客户端库来从 web 服务器上获取信息,或者通过 web 服务来执行一个远程的调用. 然而,有时候一个通用的 ...
- 协议分析TMP
最近闲来有事, 分析了一个非常低端(非常低端的意思是说你不应该对她是否能取代你现有的QQ客户端作任何可能的奢望,她只是一个实验性的东西)的手机QQ的协议, 是手机QQ3.0, 所用到的TCP ...
- TCP协议
TCP是一个面向连接的协议,在发送数据之前,必须在双方之间建立一条连接. TCP首部 TCP数据封装在IP数据报中 TCP包首部 下面简单说明部分字段的作用: 端口号:通讯双方由IP地址和端口号标识. ...
- C#的HTTP协议中POST与GET的区别
引言 HTTP协议我想任何IT人士都耳熟能详了,大家都能说出个所以然来.但是如果我问你HTTP协议的请求方法有哪些?POST与GET的差异?GET或POST传送数据量的大小有限制吗?HTTP响应的状态 ...
- Http基础
Http基础 这篇文章是讲Android网络请求的先导文章,主要讲Http工作流程,请求报文和响应报文的格式,以及GET和POST方法的具体含义. Http工作流程 HTTP是一个客户端和服务器端请求 ...
随机推荐
- 转:NLog之:文件类型目标(File target)
转:http://www.cnblogs.com/RitchieChen/archive/2012/07/16/2594308.html 英文原文[http://nlog-project.org/wi ...
- Lucene实战构建索引
搭建lucene的步骤这里就不详细介绍了,无外乎就是下载相关jar包,在eclipse中新建java工程,引入相关的jar包即可 本文主要在没有剖析lucene的源码之前实战一下,通过实战来促进研究 ...
- TortoiseSVN使用简介(转)
TortoiseSVN使用简介 1 安装及下载client 端 2 什么是SVN(Subversion)? 3 为甚么要用SVN? 4 怎么样在Windows下面建立SVN Repository? 5 ...
- 【转】IOS中各种常用控件的默认高度,很全
1.状态栏 状态栏一般高度为20像素,在打手机或者显示消息时会放大到40像素高,注意,两倍高度的状态栏在好像只能在纵向的模式下使用.如下图 用户可以隐藏状态栏,也可以将状态栏设置为灰色,黑色或者半 ...
- Swift语言
本文为熟悉使用Objective-C开发iOS快速转入swift开发提供一些便利的途径,没有过多华丽的说明底层原理,也没有过多的概念说明,只提供swift的基本使用以及一些使用示例.高手请让路哈. S ...
- IOS之UI -- 按钮UIButton的细节
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- UVa 104 - Arbitrage(Floyd动态规划)
题目来源:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&pa ...
- 在Dynamics CRM 2015中通过3CX插件(以及3CX windows phone)拨出电话
背景 在On-premises部署的Dynamics CRM中实现通过网页拨通客户电话的功能 要点 3CX 提供了开箱即用的Dynamics CRM Solution,只需要在Microsoft Dy ...
- OOD沉思录 --- 类和对象的关系 --- 包含关系4
4.9 在实现语义约束时,最好根据类定义来实现.但是这经常会导致泛滥成灾的类,在这种情况下约束应当在类的行为中实现,通常在类的构造函数中实现,但不是必须如此. 还是以汽车为例,我们看汽车的定义,为了集 ...
- ubuntu不能访问windows中的文件
出现不能访问某个盘时(例如:OS) Error mounting /dev/sda5 at /media/user/OS: Command-line `mount -t "ntfs" ...