- 浏览: 7744 次
- 性别:
- 来自: 广州
最新评论
Netty多线程服务器与客户端
- 博客分类:
- 开发资料
发此篇博客的目的是,如果有网络通讯方面的大牛看到,希望能给与一些指导.
我相信很多人都能用netty开发出自己的客户端和服务器,但是,此服务器和客户端的可重用性有多高呢?我一直想弄个性能还算不错,然后其他人在此架构上做简单的命令处理即可.
开发这个服务器和客户端的原因是正是如此,也可以说是一个简单的网络平台.让其具备一定的2次开发功能.
以下代码只是一个初步是设想,有兴趣的朋友我们可以一起讨论讨论.
如果你完全不了解netty,请先自己学习一下.^_^,因为我也是菜鸟,无法解释那么多的类是干什么的.^_^
关于netty的下载和其他个jar请自行下载
首先是服务器的初步实现.因为Netty是基于事件的,再加上其无阻塞的特性.我们必须要牢记:
数据发送后,代码不会被阻塞,而是顺序运行,也就是说,做了一件事件后,这件事情不一定已经成功,所以我们不能在下一行代码中百分百的确定其已经发送到了对方,因此,你会发行其很多方法都会返回一个"Future".只要注意到这一点,Netty的使用难度就不是很大了.
(一)handler处理篇
首先,是handler,初次接触netty的朋友要注意,handler不是一个单例.即每个channel下都会有自己的一个handler实例.
Java代码
public class ServerHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(
ServerHandler.class.getName());
private final ThreadLocal<Boolean> COMMAND_FLAG = new ThreadLocal<Boolean>();
private final ServerChannelGroup serverChannelGroup = ServerChannelGroup.newInstance();
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent) {
logger.log(Level.INFO, "Channel state changed: {0}", e);
}
super.handleUpstream(ctx, e);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println(this);
String request = (String) e.getMessage();
//如果接受到客户端发送的bye指令,那么就给客户端回复一个bye指令,客户端接受到后,主动关闭连接
//服务器端通过ChannelFutureListener.CLOSE,当它认为客户端已经接受到服务器发送的bye后,也主动关闭连接
if (request.toLowerCase().equals("bye")) {
ChannelFuture future = e.getChannel().write("bye\r\n");
future.addListener(ChannelFutureListener.CLOSE);
} else {
//以下是我初步解析客户端发送过来的数据,然后决定处理方式
RecevieData receivedaData = MessageDecoder.decode(request);
if (null != receivedaData) {
//服务器第5版
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//然后判断命令是否存在
for (String s : CommandCode.COMMANDS) {
if (s.equals(receivedaData.getActionType())) {
COMMAND_FLAG.set(true);
if (s.equals(CommandCode.KEEP_ALIVE)) {
serverChannelGroup.addChannel(e.getChannel());
}
break;
} else {
COMMAND_FLAG.set(false);
}
}
if (COMMAND_FLAG.get()) {
COMMAND_FLAG.set(false);
//将这个命令传递给下一个handler来处理.
//这里的"下一个handler"即为用户自己定义的处理handler
ctx.sendUpstream(e);
} else {
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.NOT_FOUND, StatusCode.NOT_FOUND_TEXT));
}
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED, StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果格式错误,那么直接返回
e.getChannel().write(MessageEncoder.encode(receivedaData, null, null));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
logger.log(Level.WARNING, "Server side Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
ListenerManager.getListener(ConnectClosedByPeerListener.class).connectClosedByPeer(e.getCause());
}
}
在上面这个handler中,我使用了ctx.sendUpstream(e);来处理,个人觉得此处为了要实现执行运行时代码,也可以使用接口等方式.但既然netty提供了sendUpstream 的方法,我们用这个岂不是更方便^_^
下面是使用SSL连接的handler
Java代码
public class ServerSSLHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(
ServerSSLHandler.class.getName());
private final ThreadLocal<Boolean> COMMAND_FLAG = new ThreadLocal<Boolean>();
private final ServerChannelGroup serverChannelGroup = ServerChannelGroup.newInstance();
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent) {
logger.log(Level.INFO, "Channel state changed: {0}", e);
}
super.handleUpstream(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
//ssl握手
SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
sslHandler.handshake();
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println(this);
String request = (String) e.getMessage();
//如果接受到客户端发送的bye指令,那么就给客户端回复一个bye指令,客户端接受到后,主动关闭连接
//服务器端通过ChannelFutureListener.CLOSE,当它认为客户端已经接受到服务器发送的bye后,也主动关闭连接
if (request.toLowerCase().equals("bye")) {
ChannelFuture future = e.getChannel().write("bye\r\n");
future.addListener(ChannelFutureListener.CLOSE);
} else {
//以下是我初步解析客户端发送过来的数据,然后决定处理方式
RecevieData receivedaData = MessageDecoder.decode(request);
if (null != receivedaData) {
//服务器第5版
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//然后判断命令是否存在
for (String s : CommandCode.COMMANDS) {
if (s.equals(receivedaData.getActionType())) {
COMMAND_FLAG.set(true);
if (s.equals(CommandCode.KEEP_ALIVE)) {
serverChannelGroup.addChannel(e.getChannel());
}
break;
} else {
COMMAND_FLAG.set(false);
}
}
if (COMMAND_FLAG.get()) {
COMMAND_FLAG.set(false);
//将这个命令传递给下一个handler来处理.
//这里的"下一个handler"即为用户自己定义的处理handler
ctx.sendUpstream(e);
} else {
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.NOT_FOUND, StatusCode.NOT_FOUND_TEXT));
}
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED, StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果格式错误,那么直接返回
e.getChannel().write(MessageEncoder.encode(receivedaData, null, null));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
logger.log(Level.WARNING, "Server side Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
ListenerManager.getListener(ConnectClosedByPeerListener.class).connectClosedByPeer(e.getCause());
}
}
关于SSL连接需要用到的一些其他东西,稍后在介绍
当我们有了2个handler后,当然就是要把他们添加到我们的Pipeline中
Java代码
public class ServerPipelineFactory implements
ChannelPipelineFactory {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = pipeline();
ServerConfig config = ServerConfig.getInstance();
try {
if (config.ssl()) {
SSLEngine engine =
SecureSslContextFactory.getServerContext().createSSLEngine();
//说明是服务器端SslContext
engine.setUseClientMode(false);
pipeline.addLast("ssl", new SslHandler(engine));
}
//此Decoder可以自动解析一句以\r\n结束的命令,我为了方便,也用了这个Decoder
//使用这个Decoder,我不用刻意发送命令长度用于解析,只要没有收到\r\n说明数据还
//没有发送完毕.这个Decoder会等到收到\r\n后调用下个handler
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
//字串解码,可以自己设置charset
pipeline.addLast("decoder", new StringDecoder());
//字串编码,可以自己设置charset
pipeline.addLast("encoder", new StringEncoder());
if (config.ssl()) {
//如果开启了SSL,那么使用sslhandler
pipeline.addLast("sslhandler", new ServerSSLHandler());
} else {
//如果没有开启SSL,那么使用普通handler
pipeline.addLast("handler", new ServerHandler());
}
//遍历配置文件中的服务器handler,将其添加进Pipeline链中
for (Element e : config.handler()) {
pipeline.addLast(e.attribute(e.getQName("id")).getValue().trim(),
(ChannelHandler) Class.forName(e.attribute(e.getQName("class")).getValue().trim()).newInstance());
}
} catch (DocumentException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (InstantiationException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
}
return pipeline;
}
}
下面是xml处理类
Java代码
public class ServerConfig {
private static final String HOST = "host";
private static final String PORT = "port";
private static final String HANDLER = "handler";
private static final String CLIENTHANDLER = "clienthandler";
private static final String SSL = "ssl";
private static final ServerConfig SERVER_CONFIG = new ServerConfig();
private static final String XML_PATH = "lib/server.xml";
private static final SAXReader SAR_READER = new SAXReader();
private ServerConfig() {
super();
}
public String host() throws DocumentException {
return this.rootElement().element(HOST).getTextTrim().trim();
}
public int port() throws DocumentException {
return Integer.parseInt(this.rootElement().element(PORT).getTextTrim().trim());
}
public boolean ssl() throws DocumentException {
return Integer.parseInt(this.rootElement().element(SSL).getTextTrim().trim()) == 1 ? true : false;
}
public List<Element> handler() throws DocumentException {
return this.rootElement().elements(HANDLER);
}
public List<Element> clienthandler() throws DocumentException {
return this.rootElement().elements(CLIENTHANDLER);
}
private Element rootElement() throws DocumentException {
return SAR_READER.read(new File(XML_PATH)).getRootElement();
}
public static ServerConfig getInstance() {
return SERVER_CONFIG;
}
}
server.xml,放到lib下即可,注意其中的handler 以及clienthandler 项,如果你新建了自己的handler,那么需要在此xml中配置一下.
Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!-- 配置主机地址 -->
<host>127.0.0.1</host>
<!-- 配置服务端口 -->
<port>8080</port>
<!-- 是否启用ssl,1为启用,0为停用 -->
<ssl>0</ssl>
<!--服务器业务handler -->
<handler id="timeHandler" class="com.chinatenet.nio.server.handler.ServerTimeHandler" />
<!--客户端业务handler -->
<clienthandler id="timeHandler" class="com.chinatenet.nio.client.handler.ClientTimeHandler" />
</root>
到此,一个简单的可扩展handler的服务器雏形就出来了
下面,我们添加一个自定义的服务器处理handler进来
Java代码
public class ServerTimeHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
RecevieData receivedaData = MessageDecoder.decode((String) e.getMessage());
if (CommandCode.GET_TIME.equals(receivedaData.getActionType())
|| CommandCode.KEEP_ALIVE.equals(receivedaData.getActionType())) {
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//回复客户端后,即可进行自己的业务.当然.这里可以根据需要,看
//是先回复再处理还是等处理结果出来后,将结果返回客户端
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.OK,
System.currentTimeMillis() / 1000 + ""));
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED,
StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果不是此handler处理的命令,那么流下去
ctx.sendUpstream(e);
}
}
}
最后测试一下
Java代码
public class Server {
public static void main(String[] args) throws DocumentException {
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ServerPipelineFactory());
//因为我要用到长连接
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
ServerConfig config = ServerConfig.getInstance();
bootstrap.bind(new InetSocketAddress(Integer.valueOf(config.port())));
}
}
总结:在整个服务器编码中,刚开始会遇到"远程主机强迫关闭了一个现有的连接。"等这类错误,最后修改成"相互告知对方我要关闭了"再进行关闭就可以了.
最后再完善一下异常处理即可.
我相信很多人都能用netty开发出自己的客户端和服务器,但是,此服务器和客户端的可重用性有多高呢?我一直想弄个性能还算不错,然后其他人在此架构上做简单的命令处理即可.
开发这个服务器和客户端的原因是正是如此,也可以说是一个简单的网络平台.让其具备一定的2次开发功能.
以下代码只是一个初步是设想,有兴趣的朋友我们可以一起讨论讨论.
如果你完全不了解netty,请先自己学习一下.^_^,因为我也是菜鸟,无法解释那么多的类是干什么的.^_^
关于netty的下载和其他个jar请自行下载
首先是服务器的初步实现.因为Netty是基于事件的,再加上其无阻塞的特性.我们必须要牢记:
数据发送后,代码不会被阻塞,而是顺序运行,也就是说,做了一件事件后,这件事情不一定已经成功,所以我们不能在下一行代码中百分百的确定其已经发送到了对方,因此,你会发行其很多方法都会返回一个"Future".只要注意到这一点,Netty的使用难度就不是很大了.
(一)handler处理篇
首先,是handler,初次接触netty的朋友要注意,handler不是一个单例.即每个channel下都会有自己的一个handler实例.
Java代码
public class ServerHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(
ServerHandler.class.getName());
private final ThreadLocal<Boolean> COMMAND_FLAG = new ThreadLocal<Boolean>();
private final ServerChannelGroup serverChannelGroup = ServerChannelGroup.newInstance();
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent) {
logger.log(Level.INFO, "Channel state changed: {0}", e);
}
super.handleUpstream(ctx, e);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println(this);
String request = (String) e.getMessage();
//如果接受到客户端发送的bye指令,那么就给客户端回复一个bye指令,客户端接受到后,主动关闭连接
//服务器端通过ChannelFutureListener.CLOSE,当它认为客户端已经接受到服务器发送的bye后,也主动关闭连接
if (request.toLowerCase().equals("bye")) {
ChannelFuture future = e.getChannel().write("bye\r\n");
future.addListener(ChannelFutureListener.CLOSE);
} else {
//以下是我初步解析客户端发送过来的数据,然后决定处理方式
RecevieData receivedaData = MessageDecoder.decode(request);
if (null != receivedaData) {
//服务器第5版
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//然后判断命令是否存在
for (String s : CommandCode.COMMANDS) {
if (s.equals(receivedaData.getActionType())) {
COMMAND_FLAG.set(true);
if (s.equals(CommandCode.KEEP_ALIVE)) {
serverChannelGroup.addChannel(e.getChannel());
}
break;
} else {
COMMAND_FLAG.set(false);
}
}
if (COMMAND_FLAG.get()) {
COMMAND_FLAG.set(false);
//将这个命令传递给下一个handler来处理.
//这里的"下一个handler"即为用户自己定义的处理handler
ctx.sendUpstream(e);
} else {
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.NOT_FOUND, StatusCode.NOT_FOUND_TEXT));
}
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED, StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果格式错误,那么直接返回
e.getChannel().write(MessageEncoder.encode(receivedaData, null, null));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
logger.log(Level.WARNING, "Server side Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
ListenerManager.getListener(ConnectClosedByPeerListener.class).connectClosedByPeer(e.getCause());
}
}
在上面这个handler中,我使用了ctx.sendUpstream(e);来处理,个人觉得此处为了要实现执行运行时代码,也可以使用接口等方式.但既然netty提供了sendUpstream 的方法,我们用这个岂不是更方便^_^
下面是使用SSL连接的handler
Java代码
public class ServerSSLHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(
ServerSSLHandler.class.getName());
private final ThreadLocal<Boolean> COMMAND_FLAG = new ThreadLocal<Boolean>();
private final ServerChannelGroup serverChannelGroup = ServerChannelGroup.newInstance();
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent) {
logger.log(Level.INFO, "Channel state changed: {0}", e);
}
super.handleUpstream(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
//ssl握手
SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
sslHandler.handshake();
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println(this);
String request = (String) e.getMessage();
//如果接受到客户端发送的bye指令,那么就给客户端回复一个bye指令,客户端接受到后,主动关闭连接
//服务器端通过ChannelFutureListener.CLOSE,当它认为客户端已经接受到服务器发送的bye后,也主动关闭连接
if (request.toLowerCase().equals("bye")) {
ChannelFuture future = e.getChannel().write("bye\r\n");
future.addListener(ChannelFutureListener.CLOSE);
} else {
//以下是我初步解析客户端发送过来的数据,然后决定处理方式
RecevieData receivedaData = MessageDecoder.decode(request);
if (null != receivedaData) {
//服务器第5版
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//然后判断命令是否存在
for (String s : CommandCode.COMMANDS) {
if (s.equals(receivedaData.getActionType())) {
COMMAND_FLAG.set(true);
if (s.equals(CommandCode.KEEP_ALIVE)) {
serverChannelGroup.addChannel(e.getChannel());
}
break;
} else {
COMMAND_FLAG.set(false);
}
}
if (COMMAND_FLAG.get()) {
COMMAND_FLAG.set(false);
//将这个命令传递给下一个handler来处理.
//这里的"下一个handler"即为用户自己定义的处理handler
ctx.sendUpstream(e);
} else {
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.NOT_FOUND, StatusCode.NOT_FOUND_TEXT));
}
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED, StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果格式错误,那么直接返回
e.getChannel().write(MessageEncoder.encode(receivedaData, null, null));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
logger.log(Level.WARNING, "Server side Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
ListenerManager.getListener(ConnectClosedByPeerListener.class).connectClosedByPeer(e.getCause());
}
}
关于SSL连接需要用到的一些其他东西,稍后在介绍
当我们有了2个handler后,当然就是要把他们添加到我们的Pipeline中
Java代码
public class ServerPipelineFactory implements
ChannelPipelineFactory {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = pipeline();
ServerConfig config = ServerConfig.getInstance();
try {
if (config.ssl()) {
SSLEngine engine =
SecureSslContextFactory.getServerContext().createSSLEngine();
//说明是服务器端SslContext
engine.setUseClientMode(false);
pipeline.addLast("ssl", new SslHandler(engine));
}
//此Decoder可以自动解析一句以\r\n结束的命令,我为了方便,也用了这个Decoder
//使用这个Decoder,我不用刻意发送命令长度用于解析,只要没有收到\r\n说明数据还
//没有发送完毕.这个Decoder会等到收到\r\n后调用下个handler
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
//字串解码,可以自己设置charset
pipeline.addLast("decoder", new StringDecoder());
//字串编码,可以自己设置charset
pipeline.addLast("encoder", new StringEncoder());
if (config.ssl()) {
//如果开启了SSL,那么使用sslhandler
pipeline.addLast("sslhandler", new ServerSSLHandler());
} else {
//如果没有开启SSL,那么使用普通handler
pipeline.addLast("handler", new ServerHandler());
}
//遍历配置文件中的服务器handler,将其添加进Pipeline链中
for (Element e : config.handler()) {
pipeline.addLast(e.attribute(e.getQName("id")).getValue().trim(),
(ChannelHandler) Class.forName(e.attribute(e.getQName("class")).getValue().trim()).newInstance());
}
} catch (DocumentException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (InstantiationException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(ServerPipelineFactory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
}
return pipeline;
}
}
下面是xml处理类
Java代码
public class ServerConfig {
private static final String HOST = "host";
private static final String PORT = "port";
private static final String HANDLER = "handler";
private static final String CLIENTHANDLER = "clienthandler";
private static final String SSL = "ssl";
private static final ServerConfig SERVER_CONFIG = new ServerConfig();
private static final String XML_PATH = "lib/server.xml";
private static final SAXReader SAR_READER = new SAXReader();
private ServerConfig() {
super();
}
public String host() throws DocumentException {
return this.rootElement().element(HOST).getTextTrim().trim();
}
public int port() throws DocumentException {
return Integer.parseInt(this.rootElement().element(PORT).getTextTrim().trim());
}
public boolean ssl() throws DocumentException {
return Integer.parseInt(this.rootElement().element(SSL).getTextTrim().trim()) == 1 ? true : false;
}
public List<Element> handler() throws DocumentException {
return this.rootElement().elements(HANDLER);
}
public List<Element> clienthandler() throws DocumentException {
return this.rootElement().elements(CLIENTHANDLER);
}
private Element rootElement() throws DocumentException {
return SAR_READER.read(new File(XML_PATH)).getRootElement();
}
public static ServerConfig getInstance() {
return SERVER_CONFIG;
}
}
server.xml,放到lib下即可,注意其中的handler 以及clienthandler 项,如果你新建了自己的handler,那么需要在此xml中配置一下.
Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!-- 配置主机地址 -->
<host>127.0.0.1</host>
<!-- 配置服务端口 -->
<port>8080</port>
<!-- 是否启用ssl,1为启用,0为停用 -->
<ssl>0</ssl>
<!--服务器业务handler -->
<handler id="timeHandler" class="com.chinatenet.nio.server.handler.ServerTimeHandler" />
<!--客户端业务handler -->
<clienthandler id="timeHandler" class="com.chinatenet.nio.client.handler.ClientTimeHandler" />
</root>
到此,一个简单的可扩展handler的服务器雏形就出来了
下面,我们添加一个自定义的服务器处理handler进来
Java代码
public class ServerTimeHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
RecevieData receivedaData = MessageDecoder.decode((String) e.getMessage());
if (CommandCode.GET_TIME.equals(receivedaData.getActionType())
|| CommandCode.KEEP_ALIVE.equals(receivedaData.getActionType())) {
if (VersionCode.V5.equals(receivedaData.getVersion())) {
//回复客户端后,即可进行自己的业务.当然.这里可以根据需要,看
//是先回复再处理还是等处理结果出来后,将结果返回客户端
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.OK,
System.currentTimeMillis() / 1000 + ""));
} else {
//版本错误
e.getChannel().write(MessageEncoder.encode(receivedaData, StatusCode.VERSION_NOT_SUPPORTED,
StatusCode.VERSION_NOT_SUPPORTED_TXET));
}
} else {
//如果不是此handler处理的命令,那么流下去
ctx.sendUpstream(e);
}
}
}
最后测试一下
Java代码
public class Server {
public static void main(String[] args) throws DocumentException {
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ServerPipelineFactory());
//因为我要用到长连接
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
ServerConfig config = ServerConfig.getInstance();
bootstrap.bind(new InetSocketAddress(Integer.valueOf(config.port())));
}
}
总结:在整个服务器编码中,刚开始会遇到"远程主机强迫关闭了一个现有的连接。"等这类错误,最后修改成"相互告知对方我要关闭了"再进行关闭就可以了.
最后再完善一下异常处理即可.
发表评论
-
msql分区
2014-06-12 18:03 546一、什么是表分区 通俗地讲表分区是将一大表,根据条件分割成若干 ... -
java中使用redis
2013-01-10 15:00 729Redis本质上是一个Key-Value类型的内存数据库。 ... -
二.redis 数据类型
2013-01-10 14:33 5111. keys redis本质上一个key ... -
一.redis 环境搭建
2013-01-10 14:28 5791.简介 redis是一个开源的key-value数据库。它又 ... -
Netty初步之hello world
2012-09-25 13:00 737Java的网络操作一直比较复杂,虽然说在加入NIO之后简单了些 ... -
Apache MINA 服务端与客户端实例
2012-09-25 11:49 686Apache的MINA是一个被用来 ... -
XMPP的客户端连接实例
2012-09-21 17:16 669连接,并向一个用户发送信息,只需要三行代码: XMPPCon ... -
Android SharedPreferences和File
2012-09-21 11:52 767本讲内容:SharedPreferences 和 Androi ... -
Mina之IoBuffer
2012-09-21 11:33 938原文:http://mina.apache.org/iobuf ... -
Apache MINA 快速入门
2012-09-21 11:20 6421。介绍 Apache MINA 是一个网络应用框架,有助于 ...
相关推荐
第8讲:Netty多客户端连接与通信 第9讲:Netty读写检测机制与长连接要素 第10讲:Netty对WebSocket的支援 第11讲:Netty实现服务器端与客户端的长连接通信 第12讲:Google Protobuf详解 第13讲:定义Protobuf...
8_Netty多客户端连接与通信 9_Netty读写检测机制与长连接要素 10_Netty对WebSocket的支援 11_Netty实现服务器端与客户端的长连接通信 12_Google Protobuf详解 13_定义Protobuf文件及消息详解 14_Protobuf完整实例...
找了好久的资源,分享一下基于netty封装的一个java game server 使用了jetlang 多线程并发包,对于java服务器端开发是个不错的选择,提供了各种协议,包括了AMF3Protocol、AMF3StringProtocol,WebSocketProtocol ,...
7_Netty的Socket编程详解 8_Netty多客户端连接与通信 9_Netty读写检测机制与长连接要素 10_Netty对WebSocket的支援 11_Netty实现服务器端与客户端的长连接通信 12_Google Protobuf详解 13_定义Protobuf文件及消息...
Netty是一个NIO client-server(客户端服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提 供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty...
4)使用多线程拓展netty服务端,netty只负责收发消息,逻辑提交到其它线程处理 5)使用java反射技术改造程序,比如telnet用upload就调用upload方法上传数据, 调用display就调用display方法显示服务端上一次接收到的...
NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 AIO:一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用...
Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。Netty 提供高性能和可扩展性,让你可以自由地专注于你真正感兴趣的东西,你的独特的应用!《Netty 实战...
2、直接使用NIO需要需要额外的技能,例如Java多线程,网络编程; 3、要编写一个可靠的、易维护的、高性能的NIO服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务...
Netty 是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。 简单地说Netty封装了JDK的NIO,不用再写一大堆复杂的代码。既然代替了原生的NIO,肯定有比它好的理由,主要有如下几点: ...
继续上面的例子,就是当有人输入了 通过Selector 告诉线程说某个Socket有操作,赶紧处理下,这样就不用开那么多线程了,而且所有Socket都没操作的时候,这个线程还能去干别的事,多自由 这就是书本上说的总体来看,...
简单的单服务器开发与集群开发的切换。使用Actor处理高并发。易于测试。服务的插件管理。高性能,可伸缩的Java Tcp服务器架构,1.Avalon基于Akka构建的服务器核心。天生分布式基因便于横向拓展;2.网络部分使用Netty...
BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线 程开销大。 伪异步 IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。 NIO:一个请求一个线程,但客户端发送的连接...
建立在 Netty 之上,专为高并发场景而设计,其中多个线程可以使用同一个客户端实例,而无需担心外部或内部同步,它可以帮助您减少初始化和/或准备时间以及资源浪费。 在许多小的优化中,只要有可能就重用连接,这...
netty是基于NIO的同步非阻塞框架。原生API中,服务器端只有一个主线程在轮询Selector,当有大量的...这个时候就需要利用多线程组来处理,引入多线程组就需要解决多线程同步的问题。就是有这样的需求,netty框架应运而生
Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。JDK原生也有一套网络应用程序API,NIO,但是存在一些问题使得用起来不是很方便,主要如下:NIO的类库和API繁杂,使用...
java多线程tcp socket server源码 simple-rpc - RPC with service discovery BASED ON NETTY ======================================================== Quick Start 本地下载启动Zookeeper 运行 ClientBuilderTest...
【redis官方推荐】Redisson 是使用熟悉的Java数据结构来发挥Redis的威力,基于lettuce Redis客户端和Netty 4 ,兼容 Redis 2.6 and JDK 1.6 ,使用Apache License 2.0授权协议,阅读 wiki 来获取更多使用信息...
java8 源码 netty-learn 这是一个用于netty学习的工程 ##NIO基础 三大组件 Channel & Buffer channel有点类似于stream,它就是读写数据的...Selector的作用就是配合一个线程来管理多个channel,获取这些channel上发生
3.6“多线程服务器的适用场合”例释与答疑.. . . . . . . . . . . . . . . . . 74 第4章C++ 多线程系统编程精要83 4.1基本线程原语的选用. . . . . . . . . . . . . . . . .. . . . . . . . . . . . 84 4.2C/C++ ...