HTTP协议开发

post与get的区别

1)get用于信息获取,post用于更新资源。

2)get数据放在请求行中,post数据放在请求体内。

3)get对数据长度有限制(2083字节),post没有限制。

4)post比get安全性高。

Netty Http+Xml协议栈开发

高效的XML绑定JiBx     JOPO <=> XML

package com.netty.ch10;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter; import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException; import com.netty.ch10.httpXML.pojo.Order;
import com.netty.ch10.httpXML.pojo.OrderFactory; /**
* jiBx类库的使用
* @author Administrator
* ANT 脚本生成XML与对象的绑定关系
*1)下载JiBx包
*2)JiBx包 的example下找到一个build.xml 并运行target ==> BindGen 生成 binding.xml 和 pojo.xsd
* <target name="bindgen"> <echo message="Running BindGen tool"/>
<java classpathref="classpath" fork="true" failonerror="true"
classname="org.jibx.binding.generator.BindGen">
<arg value="-s"/>
<arg value="${basedir}/src/com/netty/ch10/httpXML/pojo"/>
<arg value="com.netty.ch10.httpXML.pojo.Order"/>
</java> </target>
3)jibx编译命令 动态修改POJO类 ==> bind
<!-- bind as a separate step -->
<target name="bind" depends="check-binding"> <echo message="Running JiBX binding compiler"/>
<taskdef name="bind" classname="org.jibx.binding.ant.CompileTask">
<classpath>
<fileset dir="${jibx-home}/lib" includes="*.jar"/>
</classpath>
</taskdef>
<bind binding="${basedir}/binding.xml">
<classpath refid="classpath"/>
</bind> </target>
4)运行测试类
*/
public class TestOrder { private IBindingFactory factory = null;
private StringWriter write = null;
private StringReader reader = null;
private final static String CHARSET_NAME = "UTF-8"; /**
* pojo to xml
* @param order
* @return
* @throws JiBXException
* @throws IOException
*/
private String encode2Xml(Order order) throws JiBXException, IOException{
factory = BindingDirectory.getFactory(Order.class);
write = new StringWriter();
IMarshallingContext mctx = factory.createMarshallingContext();
mctx.setIndent();
mctx.marshalDocument(order,CHARSET_NAME,null,write);
String xmlStr = write.toString();
write.close();
System.out.println(xmlStr);
return xmlStr;
} private Order decode2Order(String xmlBody) throws JiBXException{
reader = new StringReader(xmlBody);
IUnmarshallingContext uctx = factory.createUnmarshallingContext();
Order order = (Order)uctx.unmarshalDocument(reader);
return order; }
public static void main(String[] args) throws JiBXException, IOException {
TestOrder test = new TestOrder();
Order order = OrderFactory.create();
String body = test.encode2Xml(order);
Order order2 = test.decode2Order(body);
System.out.println(order2);
} }

Http请求类

package com.netty.ch10.httpXML.request;

import io.netty.handler.codec.http.FullHttpRequest;

public class HttpXmlRequest {
private FullHttpRequest request;
private Object body; /**
* @return the body
*/
public Object getBody() {
return body;
} /**
* @param body the body to set
*/
public void setBody(Object body) {
this.body = body;
} public HttpXmlRequest(FullHttpRequest request, Object body) {
this.request = request;
this.body = body;
} public final FullHttpRequest getRequest() {
return request;
} public final void setRequest(FullHttpRequest request) {
this.request = request;
}
}

Http响应类

package com.netty.ch10.httpXML.response;

import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; public class HttpXmlResponse { private FullHttpResponse httpResponse;
private Object result;
public HttpXmlResponse(FullHttpResponse httpResponse, Object result) {
super();
this.httpResponse = httpResponse;
this.result = result;
}
public HttpXmlResponse() {
super();
// TODO Auto-generated constructor stub
}
/**
* @return the httpResponse
*/
public FullHttpResponse getHttpResponse() {
return httpResponse;
}
/**
* @param httpResponse the httpResponse to set
*/
public void setHttpResponse(FullHttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
/**
* @return the result
*/
public Object getResult() {
return result;
}
/**
* @param result the result to set
*/
public void setResult(Object result) {
this.result = result;
} }

Encoder编码基类

package com.netty.ch10.httpXML.common;

import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.List; import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.MessageToMessageEncoder;
/**
* HTTP+XML 请求消息编码基类
* @author Administrator
*
* @param <T>
*/
public abstract class AbstractHttpXmlEncoder<T> extends MessageToMessageEncoder<T>{ IBindingFactory factory = null;
StringWriter writer = null;
final static String CHARSET_NAME ="UTF-8";
final static Charset UTF_8 = Charset.forName(CHARSET_NAME); protected ByteBuf encode0(ChannelHandlerContext ctx,Object body) throws Exception{
factory = BindingDirectory.getFactory(body.getClass());
writer= new StringWriter();
IMarshallingContext mctx = factory.createMarshallingContext();
mctx.setIndent();
mctx.marshalDocument(body,CHARSET_NAME,null,writer);
String xmlStr = writer.toString();
writer.close();
writer = null;
ByteBuf encodeBuf = Unpooled.copiedBuffer(xmlStr,UTF_8);
return encodeBuf;
} /* (non-Javadoc)
* @see io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(ctx, cause);
if(writer != null){
writer.close();
writer = null;
}
} }

HTTP请求编码类

package com.netty.ch10.httpXML.request;

import java.net.InetAddress;
import java.util.List; import com.netty.ch10.httpXML.common.AbstractHttpXmlEncoder; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
//import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
/**
* HTTP+XML Http 请求消息编码类
* @author Administrator
*
*/
public class HttpXmlRequestEncoder extends AbstractHttpXmlEncoder<HttpXmlRequest>{ @Override
protected void encode(ChannelHandlerContext ctx, HttpXmlRequest msg, List<Object> out) throws Exception {
// TODO Auto-generated method stub
//将业务需要发送的pojo对象Order实例通过JiBx序列化成字符串,随后封装成Netty的ByteBuf。
ByteBuf body = encode0(ctx, msg.getBody()) ;
FullHttpRequest request = msg.getRequest(); //如果业务侧没有设置消息头 则构造新的http请求头
if(request == null){
request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,HttpMethod.GET,"/do",body);
//从request中获取请求头
HttpHeaders header = request.headers();
//设置一系列的请求头 这边以硬编码的方式 产品化可以做成xml配置文件
header.set(HttpHeaderNames.HOST,InetAddress.getLocalHost().getHostAddress());
header.set(HttpHeaderNames.CONNECTION,HttpHeaderValues.CLOSE);
header.set(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.GZIP.toString()
+","+HttpHeaderValues.DEFLATE.toString());
header.set(HttpHeaderNames.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
header.set(HttpHeaderNames.USER_AGENT,"Netty xml Http Client side");
header.set(HttpHeaderNames.ACCEPT,"text/html.application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
header.set(HttpHeaderNames.CONTENT_LENGTH,Integer.toString(body.readableBytes()));
}
//完成消息体xml序列化后重新构造http请求消息加入到out中,由http请求编码器继续对http请求消息进行编码
out.add(request);
} }

Http响应编码类

package com.netty.ch10.httpXML.response;

import java.util.List;

import com.netty.ch10.httpXML.common.AbstractHttpXmlEncoder;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderUtil;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
/**
* Http+xml 应答消息编码类
* @author Administrator
*
*/
public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder<HttpXmlResponse> { private static final HttpVersion HTTP_1_1 = HttpVersion.HTTP_1_1;
private static final HttpResponseStatus OK = HttpResponseStatus.OK;
private static final AsciiString CONTENT_TYPE = HttpHeaderNames.CONTENT_TYPE;
@Override
protected void encode(ChannelHandlerContext ctx, HttpXmlResponse msg, List<Object> out) throws Exception {
ByteBuf body = encode0(ctx,msg.getResult());
FullHttpResponse response = msg.getHttpResponse();
if(response == null){
response = new DefaultFullHttpResponse(HTTP_1_1,OK,body);
}else{
new DefaultFullHttpResponse(msg.getHttpResponse().protocolVersion(),
msg.getHttpResponse().status(), body);
}
response.headers().set(CONTENT_TYPE, "text/xml");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,Integer.toString(body.readableBytes()));
//将编码后的response对象添加到编码结果链表中,由后续的Http编码类进行二次编码
out.add(response);
} }

Decoder编码基类

package com.netty.ch10.httpXML.common;

import java.io.StringReader;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.List; import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IUnmarshallingContext; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.MessageToMessageDecoder; public abstract class AbstractHttpXmlDecoder<T> extends MessageToMessageDecoder<T> { private IBindingFactory factory;
private StringReader reader;
private Class<?> clazz;
private boolean isPrint;
private final static String CHARSET_NAME = "UTF-8";
private final static Charset UTF_8 = Charset.forName(CHARSET_NAME); protected AbstractHttpXmlDecoder(Class<?> clazz){
this(clazz, false);
}
protected AbstractHttpXmlDecoder(Class<?> clazz,boolean isPrint){
this.clazz = clazz;
this.isPrint = isPrint;
}
protected Object decode0(ChannelHandlerContext arg0,ByteBuf body) throws Exception{
factory = BindingDirectory.getFactory(clazz);
String context = body.toString(UTF_8);
if(isPrint)
System.out.println("The body is"+context);
reader =new StringReader(context);
IUnmarshallingContext uctx = factory.createUnmarshallingContext();
Object result = uctx.unmarshalDocument(reader);
reader.close();
reader = null;
return result;
} @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// TODO Auto-generated method stub
super.write(ctx, msg, promise);
if(reader != null){
reader.close();
reader = null;
}
} }

Http请求解码类

package com.netty.ch10.httpXML.request;

import java.util.List;

import com.netty.ch10.httpXML.common.AbstractHttpXmlDecoder;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
/**
* HTTP+XML Http请求消息解码类
* @author Administrator
*
*/
public class HttpXmlRequestDecoder extends AbstractHttpXmlDecoder<FullHttpRequest> { public HttpXmlRequestDecoder(Class<?> clazz) {
this( clazz,false);
}
public HttpXmlRequestDecoder(Class<?> clazz,boolean isPrint) {
super(clazz,isPrint);
}
/* (non-Javadoc)
* @see com.netty.ch10.httpXML.response.AbstractHttpXmlDecoder#decode(io.netty.channel.ChannelHandlerContext, java.lang.Object, java.util.List)
*/
@Override
protected void decode(ChannelHandlerContext arg0, FullHttpRequest arg1, List<Object> arg2) throws Exception {
if(!arg1.decoderResult().isSuccess()){
sendError(arg0, HttpResponseStatus.BAD_REQUEST);
return;
}
//通过FullHttpRequest 和经过decode0将byteBuf反序列化的order对象 构造HttpXmlRequest
HttpXmlRequest request = new HttpXmlRequest(arg1, decode0(arg0,arg1.content()));
arg2.add(request);
} private static void sendError(ChannelHandlerContext ctx,HttpResponseStatus status){
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,status
,Unpooled.copiedBuffer("Failure:"+status.toString()+"\r\n", CharsetUtil.UTF_8));
response.headers().set("contextType","text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} }

Http响应解码类

package com.netty.ch10.httpXML.response;

import java.util.List;

import com.netty.ch10.httpXML.common.AbstractHttpXmlDecoder;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse; public class HttpXmlResponseDecoder extends AbstractHttpXmlDecoder<DefaultFullHttpResponse> { public HttpXmlResponseDecoder(Class<?> clazz) {
this(clazz,false);
// TODO Auto-generated constructor stub
} public HttpXmlResponseDecoder(Class<?> clazz,boolean isPrintLog) {
super(clazz,isPrintLog);
// TODO Auto-generated constructor stub
}
@Override
protected void decode(ChannelHandlerContext ctx, DefaultFullHttpResponse msg, List<Object> out)
throws Exception {
//将反序列化的POJO对象构造成HttpXmlResponse 并添加到解码结果列表中
HttpXmlResponse resHttpXmlResponse =
new HttpXmlResponse(msg,decode0(ctx,msg.content()));
out.add(resHttpXmlResponse); } }

客户端及客户端请求handler

package com.netty.ch10.httpXML;

import java.net.InetSocketAddress;

import com.netty.ch10.httpXML.pojo.Order;
import com.netty.ch10.httpXML.request.HttpXmlRequestEncoder;
import com.netty.ch10.httpXML.response.HttpXmlResponseDecoder; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpRequestEncoder; /**
* Http+xml客户端
* @author Administrator
*1)发起Http连接请求
*2)构造订购请求消息,将其编码成xml,通过http协议发给服务端
*3)构造http服务端的应答消息,将xml应答消息反序列化为订购消息POJO对象
*4)关闭HTTP连接
*
*Handler的执行顺序 :https://my.oschina.net/jamaly/blog/272385
*https://my.oschina.net/freegarden/blog/300348
*
* 执行顺序是:Encoder 先注册的后执行,与OutboundHandler一致;Decoder是先注册的先执行,与InboundHandler一致。
*
*/
public class HttpXmlClient { public void connect(int port) throws Exception{
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//负责将二进制码流解码成HTTP的应答消息
ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
//负责将1个http请求消息的多个部分合并成一个完整的http消息
ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator()); //XML解码器
ch.pipeline().addLast("xml-decoder",new HttpXmlResponseDecoder(Order.class, true));
//【注意】:顺序,编码的时候按照从尾到头的顺序 HttpXmlClientHandler-->HttpXmlRequestEncoder-->HttpRequestEncoder
ch.pipeline().addLast("http-encoder",new HttpRequestEncoder()); //2)封装成完整的httprequest
ch.pipeline().addLast("xml-encoder",new HttpXmlRequestEncoder()); //1)pojo -->bytebuf //write POJO receive POJO
ch.pipeline().addLast("xml-client-handler",new HttpXmlClientHandler());
}
});
//发起异步连接请求
ChannelFuture f = b.connect(new InetSocketAddress(port)).sync();
//等待客户端链路关闭
f.channel().closeFuture().sync();
}finally{
group.shutdownGracefully();
}
} public static void main(String[] args) throws Exception{
int port = ;
if(args != null && args.length > ){
try {
port = Integer.valueOf(args[]);
} catch (Exception e) {
// TODO: handle exception
}
}
new HttpXmlClient().connect(port);
}
}
package com.netty.ch10.httpXML;

import com.netty.ch10.httpXML.request.HttpXmlRequest;
import com.netty.ch10.httpXML.response.HttpXmlResponse; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; public class HttpXmlClientHandler extends SimpleChannelInboundHandler<HttpXmlResponse> { @Override
protected void messageReceived(ChannelHandlerContext ctx, HttpXmlResponse msg) throws Exception {
System.out.println("the client receive response of http header is :"
+ msg.getHttpResponse().headers().names());
System.out.println("the client receive response of http body is :"+ msg.getResult()); } /* (non-Javadoc)
* @see io.netty.channel.ChannelHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
HttpXmlRequest request = new HttpXmlRequest(null, OrderFactory.create());
ctx.writeAndFlush(request); } /* (non-Javadoc)
* @see io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} }

服务端及服务端响应handler

package com.netty.ch10.httpXML;

import java.net.InetSocketAddress;

import com.netty.ch10.httpXML.pojo.Order;
import com.netty.ch10.httpXML.request.HttpXmlRequestDecoder;
import com.netty.ch10.httpXML.response.HttpXmlResponseEncoder; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; /**
* Http 服务端的功能如下
* @author Administrator
*1)接收Http客户端的连接
*2)接收http客户端的xml请求消息,并将其解码成POJO对象
*3)对pojo对象进行业务处理
*4)通过http+xml的格式返回应答消息
*5)主动关闭http连接
*/
public class HttpXmlServer { public void run(final int port) throws Exception{
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator());
ch.pipeline().addLast("xml-decoder",new HttpXmlRequestDecoder(Order.class,true)); //下面顺序相反
ch.pipeline().addLast("http-encoder",new HttpResponseEncoder());
ch.pipeline().addLast("xml-encoder",new HttpXmlResponseEncoder()); ch.pipeline().addLast("xmlServerHandler",new HttpXmlServerHandler()); }
});
ChannelFuture future = b.bind(new InetSocketAddress(port)).sync();
System.out.println("Http 订购服务器启动,网址是:"+"http://localhost:"+port); // Wait until the connection is closed.
future.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
} } public static void main(String[] args) throws Exception {
int port = ;
if(args.length>){
try{
port = Integer.parseInt(args[]);
}catch(Exception e){
e.printStackTrace();
}
}
new HttpXmlServer().run(port);
}
}
package com.netty.ch10.httpXML;

import java.util.ArrayList;
import java.util.List;
import com.netty.ch10.httpXML.pojo.Address;
import com.netty.ch10.httpXML.pojo.Order;
import com.netty.ch10.httpXML.request.HttpXmlRequest;
import com.netty.ch10.httpXML.response.HttpXmlResponse; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; public class HttpXmlServerHandler extends SimpleChannelInboundHandler<HttpXmlRequest> { @Override
public void messageReceived(final ChannelHandlerContext ctx, HttpXmlRequest xmlRequest) throws Exception {
FullHttpRequest request = xmlRequest.getRequest();
Order order = (Order)xmlRequest.getBody();
System.out.println("Http server receive request :"+ order);
dobusiness(order);
ChannelFuture future = ctx.writeAndFlush(new HttpXmlResponse(null,order));
if(!ctx.channel().isActive()){
future.addListener(new GenericFutureListener<Future<? super Void>>() { @Override
public void operationComplete(Future future) throws Exception {
// TODO Auto-generated method stub
ctx.close();
} });
} } private void dobusiness(Order order) {
order.getCustomer().setFirstName("狄");
order.getCustomer().setLastName("仁杰");
List<String> midNames = new ArrayList<String>();
midNames.add("李元芳");
order.getCustomer().setMiddleNames(midNames);
Address address = order.getBillTo();
address.setCity("洛阳");
address.setCountry("大唐");
address.setState("河南道");
address.setPostCode("");
order.setBillTo(address);
// order.setShiping(address); } /*
* (non-Javadoc)
*
* @see
* io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.
* ChannelHandlerContext, java.lang.Throwable)
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
cause.printStackTrace();
if(ctx.channel().isActive()){
sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
}
} private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
Unpooled.copiedBuffer("失败:" + status.toString() + "\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} }

WebSocket协议开发

webSocket将网络套接字引入到客户端和服务端,浏览器和服务端之间可以通过套接字建立持久连接,双方随时都可以互发数据给对方,而不是之前由客户端控制的请求-应答模式。

http协议的弊端:

1)同一时刻只能在一个方向上传输数据(半双工)

2)HTTP消息冗余而繁琐

3)针对服务器推送的黑客攻击,如长时间轮询。

webSocket特点:

1)单一的TCP连接,采用全双工模式通信

2)对代理,防火墙和路由器透明

3)无头部信息,Cookie和身份验证

4)无安全开销

5)通过“ping/pong”帧保持链路激活

6)服务器可以主动传递消息给客户端,不再需要客户端轮询。

server

package com.netty.ch11;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler; /**
* webSocketServer 启动端
*
* @author Administrator
*
*/
public class WebSocketServer { public void run(int port) throws Exception {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//将请求和应答消息编码和解码为http消息
pipeline.addLast("http-codec", new HttpServerCodec());
//将Http消息的多个部分组合成一条完整的http消息
pipeline.addLast("aggregator", new HttpObjectAggregator());
//来向客户端发送html5文件
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast("handler", new WebSocketServerHandler());
}
}); Channel ch = b.bind(port).sync().channel();
System.out.println("Web socket server started at port " + port + '.');
System.out.println("Open your browser and navigate to http://localhost:" + port + '/'); ch.closeFuture().sync();
} finally {
System.out.println("stop server");
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
int port = ;
if (args.length > ) {
try {
port = Integer.parseInt(args[]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new WebSocketServer().run(port);
}
}

websocket处理类

package com.netty.ch11;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil; import java.util.logging.Level;
import java.util.logging.Logger; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; /**
* @version 1.0
* @date 2014年2月14日
*/
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger logger = Logger
.getLogger(WebSocketServerHandler.class.getName()); //握手处理类
private WebSocketServerHandshaker handshaker; @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg)
throws Exception {
// 传统的HTTP接入
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
}
// WebSocket接入 前端请求websocket socket.onmessage
else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} private void handleHttpRequest(ChannelHandlerContext ctx,
FullHttpRequest req) throws Exception { // 如果HTTP解码失败,返回HHTP异常
if (!req.decoderResult().isSuccess()
|| (!"websocket".equals(req.headers().get("Upgrade")))) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
BAD_REQUEST));
return;
} // 构造握手响应返回,本机测试
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
"ws://localhost:8080/websocket", null, false);
//创建握手消息处理类
handshaker = wsFactory.newHandshaker(req);
//无法处理的websocket
if (handshaker == null) {
WebSocketServerHandshakerFactory
.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);
}
} private void handleWebSocketFrame(ChannelHandlerContext ctx,
WebSocketFrame frame) { // 判断是否是关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(),
(CloseWebSocketFrame) frame.retain());
return;
}
// 判断是否是Ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(
new PongWebSocketFrame(frame.content().retain()));
return;
}
// 本例程仅支持文本消息,不支持二进制消息
if (!(frame instanceof TextWebSocketFrame)) {
throw new UnsupportedOperationException(String.format(
"%s frame types not supported", frame.getClass().getName()));
} // 返回应答消息
String request = ((TextWebSocketFrame) frame).text();
if (logger.isLoggable(Level.FINE)) {
logger.fine(String.format("%s received %s", ctx.channel(), request));
}
ctx.channel().write(
new TextWebSocketFrame(request
+ " , 欢迎使用Netty WebSocket服务,现在时刻:"
+ new java.util.Date().toString()));
} private static void sendHttpResponse(ChannelHandlerContext ctx,
FullHttpRequest req, FullHttpResponse res) {
// 返回应答给客户端
if (res.status().code() != ) {
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(),
CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
res.headers().set(HttpHeaderNames.CONTENT_LENGTH
,Integer.toString(res.content().readableBytes()));
} // 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!ctx.channel().isActive() || res.status().code() != ) {
f.addListener(ChannelFutureListener.CLOSE);
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
} }

client html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
Netty WebSocket 时间服务器
</head>
<br>
<body>
<br>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/websocket");
socket.onmessage = function (event) {
var ta = document.getElementById('responseText');
ta.value = "";
ta.value = event.data
};
socket.onopen = function (event) {
var ta = document.getElementById('responseText');
ta.value = "打开WebSocket服务正常,浏览器支持WebSocket!";
};
socket.onclose = function (event) {
var ta = document.getElementById('responseText');
ta.value = "";
ta.value = "WebSocket 关闭!";
};
}
else {
alert("抱歉,您的浏览器不支持WebSocket协议!");
} function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
}
else {
alert("WebSocket连接没有建立成功!");
}
}
</script>
<form onsubmit="return false;">
<input type="text" name="message" value="Netty最佳实践"/>
<br><br>
<input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/>
<hr color="blue"/>
<h3>服务端返回的应答消息</h3>
<textarea id="responseText" style="width:500px;height:300px;"></textarea>
</form>
</body>
</html>

私有协议栈开发

绝大多数的私有协议传输层都是基于TCP/IP,所以利用Netty的NIO TCP协议栈可以很方便的进行私有协议的定制和开发。

Netty协议栈功能设计

通信模型

1)Netty协议栈client发送握手消息,携带节点IP等有效身份认证信息。

2)Netty协议栈服务端对握手请求消息进行合法性的认证,包括节点ID有效性检验,节点重复登录校验和IP地址合法性校验,校验通过后,返回登录成功的握手应答消息。

3)链路建立成功后,client发送业务消息。

4)链路成功后,server发送心跳消息。

5)链路建立成功之后,client发送心跳消息。

6)链路建立成功后,server发送业务消息。

7)server退出后,server关闭连接,client感知对方关闭连接后,被动关闭连接。

注意:双方采用全双工的通信方式,并不区分server与client。双方之间采用ping-pong心跳机制。链路断开client主动关闭连接,间隔T周期发起重连,直至成功。

消息定义:消息头,消息体。

链路的建立:创建握手请求消息,同时server基于IP地址进行黑白名单安全认证机制,或者通过密钥+数字证书进行接入认证。

链路的关闭:双方通过心跳和业务消息维持链路的长连接,任何一方都不需要主动关闭连接,但是以下情况需要关闭连接,

  1)对方宕机或重启。

  2)消息读写发生IO异常。

  3)心跳发生IO异常或者超市。

  4)发生编码异常等不可恢复。

心跳机制(ping-pong):在网络空闲时采用心跳机制检验链路的互通性,一旦发现网络故障,立即关闭链路,主动连接。

  1)当网络处于空闲状态,连续T周期没有读写,则client主动发送ping心跳消息给服务端。

  2)如果在下个周期T到来时client没有收到对方发送的Pong心跳应答消息或者读取到服务端发送的其他业务消息,则心跳计数器+1.

  3)一旦收到业务消息或者Pong消息,则心跳计数器=0,N次没有收到服务端应答,则关闭链路,间隔INTERVAL时间后发起重连。

  4)server网络空闲状态时间T后,心跳失败计数器+1,N次没有接受到client请求则关闭链路等待client重连,一旦接受到ping或业务消息,计数器清零。

注意:通过Ping-Pong双向心跳机制,保证无论哪一方出现问题,都被及时检测出来。N次检测防止对方短时间繁忙没有及时应答而造成的误判。

重连机制: 间隔INTERVAL时间不断重连,保证服务端有足够的时间释放句柄资源,再发起重新连接,直至成功。

重复登录保护:在缓存地址表中查看client是否已经登录,如果已经登录,则拒绝重复登录,返回错误码-1,并关闭链路。如心跳失败同时也清空该client的地址缓存表信息。

消息缓存重发:链路中断后,待发送的消息缓存在消息队列不能丢失,等恢复链路后消息重新发送,同时设置队列上线,防止OOM,超过上限聚集添加新的消息。

安全性:IP黑白名单,SSL/TSL会话。

私有栈协议开发

数据结构定义:

package com.netty.ch12.msg;

import java.util.HashMap;
import java.util.Map; public class Header { private int crcCode = 0xabef0101;
private int length ;//消息长度
private long sessionID;//会话ID
private byte type;//消息类型
private byte priority;//消息优先级
private Map<String,Object> attachment = new HashMap<String,Object>();//附件 同时便于扩展
/**
* @return the crcCode
*/
public int getCrcCode() {
return crcCode;
}
/**
* @param crcCode the crcCode to set
*/
public void setCrcCode(int crcCode) {
this.crcCode = crcCode;
}
/**
* @return the length
*/
public int getLength() {
return length;
}
/**
* @param length the length to set
*/
public void setLength(int length) {
this.length = length;
}
/**
* @return the sessionID
*/
public long getSessionID() {
return sessionID;
}
/**
* @param sessionID the sessionID to set
*/
public void setSessionID(long sessionID) {
this.sessionID = sessionID;
}
/**
* @return the type
*/
public byte getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(byte type) {
this.type = type;
}
/**
* @return the priority
*/
public byte getPriority() {
return priority;
}
/**
* @param priority the priority to set
*/
public void setPriority(byte priority) {
this.priority = priority;
}
/**
* @return the attachment
*/
public Map<String, Object> getAttachment() {
return attachment;
}
/**
* @param attachment the attachment to set
*/
public void setAttachment(Map<String, Object> attachment) {
this.attachment = attachment;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Header [crcCode=" + crcCode + ", length=" + length + ", sessionID=" + sessionID + ", type=" + type
+ ", priority=" + priority + ", attachment=" + attachment + "]";
} }
package com.netty.ch12.msg;

/**
* 对netty协议栈使用到的数据结构进行定义
* @author Administrator
*
*/
public final class NettyMessage { private Header header; //消息头
private Object body;//消息体
/**
* @return the header
*/
public final Header getHeader() {
return header;
}
/**
* @param header the header to set
*/
public final void setHeader(Header header) {
this.header = header;
}
/**
* @return the body
*/
public final Object getBody() {
return body;
}
/**
* @param body the body to set
*/
public final void setBody(Object body) {
this.body = body;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "NettyMessage [header=" + header + ", body=" + body + "]";
} }

Netty消息编码工具类:

package com.netty.ch12.util;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider; public class NettyMarshallingDecoder extends MarshallingDecoder{ public NettyMarshallingDecoder(UnmarshallerProvider provider) {
super(provider);
} public NettyMarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize){
super(provider, maxObjectSize);
} public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
return super.decode(ctx, in);
} }
package com.netty.ch12.util;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider; public class NettyMarshallingEncoder extends MarshallingEncoder{ public NettyMarshallingEncoder(MarshallerProvider provider) {
super(provider);
} public void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception{
super.encode(ctx, msg, out);
}
}
package com.netty.ch12.util;

import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider; import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration; public class MarshallingCodeCFactory {
public static NettyMarshallingDecoder buildMarshallingDecoder(){
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
NettyMarshallingDecoder decoder = new NettyMarshallingDecoder(provider, 1024);
return decoder;
} public static NettyMarshallingEncoder buildMarshallingEncoder(){
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
NettyMarshallingEncoder encoder = new NettyMarshallingEncoder(provider);
return encoder;
}
}

消息编解码:

package com.netty.ch12.handler;

import java.io.IOException;
import java.util.List;
import java.util.Map; import com.netty.ch12.msg.NettyMessage;
import com.netty.ch12.util.MarshallingCodeCFactory;
import com.netty.ch12.util.NettyMarshallingEncoder; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
/**
* nettty 消息编码类
* @author Administrator
*
*/
public class NettyMessageEncoder extends MessageToMessageEncoder<NettyMessage> { private NettyMarshallingEncoder marshallingEncoder; public NettyMessageEncoder(){
marshallingEncoder = MarshallingCodeCFactory.buildMarshallingEncoder();
}
@Override
protected void encode(ChannelHandlerContext ctx, NettyMessage msg, List<Object> out) throws Exception {
if(msg == null || msg.getHeader() == null)
throw new Exception("The encode message is null");
ByteBuf sendBuf = Unpooled.buffer();
sendBuf.writeInt(msg.getHeader().getCrcCode());
sendBuf.writeInt(msg.getHeader().getLength());
sendBuf.writeLong(msg.getHeader().getSessionID());
sendBuf.writeByte(msg.getHeader().getType());
sendBuf.writeByte(msg.getHeader().getPriority());
sendBuf.writeInt(msg.getHeader().getAttachment().size()); String key = null;
byte[] keyArray = null;
Object value = null;
for(Map.Entry<String, Object> param : msg.getHeader().getAttachment().entrySet()){
key = param.getKey();
keyArray = key.getBytes("utf-8");
sendBuf.writeInt(keyArray.length);
sendBuf.writeBytes(keyArray);
value = param.getValue();
marshallingEncoder.encode(ctx,value, sendBuf);
} key = null;
keyArray = null;
value = null;
if(msg.getBody() != null){
marshallingEncoder.encode(ctx,msg.getBody(), sendBuf);
}//else
//sendBuf.writeInt(0);
// 在第4个字节处写入Buffer的长度
sendBuf.setInt(4, sendBuf.readableBytes());
// 把Message添加到List传递到下一个Handler
out.add(sendBuf);
} }
package com.netty.ch12.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import java.util.HashMap;
import java.util.Map; import com.netty.ch12.msg.Header;
import com.netty.ch12.msg.NettyMessage;
import com.netty.ch12.util.MarshallingCodeCFactory;
import com.netty.ch12.util.NettyMarshallingDecoder;
/**
* netty 消息解码类
* @author Administrator
*
*/
public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder{ private NettyMarshallingDecoder marshallingDecoder; public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset,
int lengthFieldLength) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
marshallingDecoder = MarshallingCodeCFactory.buildMarshallingDecoder();
} public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset,
int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength,lengthAdjustment, initialBytesToStrip);
marshallingDecoder = MarshallingCodeCFactory.buildMarshallingDecoder();
}
public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception{
ByteBuf frame = (ByteBuf)super.decode(ctx, in);
//为空说明是半包消息,直接返回由i/o线程读取后续的码流
if(frame == null){
return null;
} NettyMessage message = new NettyMessage();
Header header = new Header();
header.setCrcCode(frame.readInt());
header.setLength(frame.readInt());
header.setSessionID(frame.readLong());
header.setType(frame.readByte());
header.setPriority(frame.readByte()); int size = frame.readInt();
if(size > 0){
Map<String, Object> attach = new HashMap<String, Object>(size);
int keySize = 0;
byte[] keyArray = null;
String key = null;
for(int i=0; i<size; i++){
keySize = frame.readInt();
keyArray = new byte[keySize];
in.readBytes(keyArray);
key = new String(keyArray, "UTF-8");
attach.put(key, marshallingDecoder.decode(ctx, frame));
}
key = null;
keyArray = null;
header.setAttachment(attach);
}
if(frame.readableBytes() > 0){
message.setBody(marshallingDecoder.decode(ctx, frame));
}
message.setHeader(header);
return message;
}
}

common类

package com.netty.ch12.comon;

public enum MessageType {

    LOGIN_REQ{
public byte value(){
return (byte)1;
}
},LOGIN_RESP{
public byte value(){
return (byte)2;
}
},HEARTBEAT_REQ{ @Override
public byte value() {
// TODO Auto-generated method stub
return (byte)3;
} },HEARTBEAT_RESP{ @Override
public byte value() {
// TODO Auto-generated method stub
return (byte)4;
} };
public abstract byte value();
}
package com.netty.ch12.comon;

public class NettyConstant {
public static final String LOCALIP = "127.0.0.1";
public static final String REMOTEIP = "127.0.0.1";
public static final int LOCAL_PORT = 12088;
public static final int PORT = 8080; }

握手和安全认证:

握手的发起是在客户端和服务端TCP链路建立成功通道激活时,握手消息的接入和安全认证在服务端处理。

package com.netty.ch12.handler;

import com.netty.ch12.comon.MessageType;
import com.netty.ch12.msg.Header;
import com.netty.ch12.msg.NettyMessage; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class LoginAuthReqHandler extends ChannelHandlerAdapter { public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(buildLoginReq());
} public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyMessage message = (NettyMessage) msg;
if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()) {
System.out.println("Received from server response");
byte loginResult = (byte) message.getBody();
if (loginResult != (byte) 0) {
// 握手失败 关闭连接
ctx.close();
} else {
System.out.println("Login is ok :" + message);
ctx.fireChannelRead(msg);
}
} else {
ctx.fireChannelRead(msg);
} } private NettyMessage buildLoginReq() {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_REQ.value());
message.setHeader(header);
message.setBody("It is request");
return message;
} public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
}
package com.netty.ch12.handler;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import com.netty.ch12.comon.MessageType;
import com.netty.ch12.msg.Header;
import com.netty.ch12.msg.NettyMessage; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class LoginAuthRespHandler extends ChannelHandlerAdapter {
//地址缓存
private Map<String,Boolean> nodeCheck = new ConcurrentHashMap<String,Boolean>();
private String[] whiteList = {"127.0.0.1","192.168.1.104"};
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
NettyMessage message = (NettyMessage)msg;
//如果是握手请求消息则处理 其他消息透传
if(message.getHeader() != null
&& message.getHeader().getType() == MessageType.LOGIN_REQ.value()){
String nodeIndex = ctx.channel().remoteAddress().toString();
NettyMessage loginResp = null;
//重复登录 拒绝
if(nodeCheck.containsKey(nodeIndex)){
loginResp = buildLoginResponse((byte)-1);
}else{
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
String ip = address.getAddress().getHostAddress();
System.out.println("获取client连接:"+ip);
boolean isOk = false;
for(String WIP : whiteList){
if(WIP.equals(ip)){
isOk = true;
break;
}
}
loginResp = isOk ? buildLoginResponse((byte)0) : buildLoginResponse((byte)-1);
if(isOk){
nodeCheck.put(nodeIndex, true);
}
System.out.println("The login response is :"
+ loginResp + "body [" + loginResp.getBody() + "]");
ctx.writeAndFlush(loginResp);
} }else{
ctx.fireChannelRead(msg);
} } private NettyMessage buildLoginResponse(byte result) {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_RESP.value());
message.setHeader(header);
message.setBody(result);
return message;
} public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
nodeCheck.remove(ctx.channel().remoteAddress().toString());//删除缓存
ctx.close();
ctx.fireExceptionCaught(cause);
}
}

心跳检测机制

package com.netty.ch12.handler;

import java.util.concurrent.TimeUnit;

import com.netty.ch12.comon.MessageType;
import com.netty.ch12.msg.Header;
import com.netty.ch12.msg.NettyMessage; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.ScheduledFuture; public class HeartBeatReqHandler extends ChannelHandlerAdapter{ private volatile ScheduledFuture<?> heartBeat; /* (non-Javadoc)
* @see io.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
NettyMessage message = (NettyMessage) msg;
//握手成功(如果握手不成功,则已经关闭连接,不会执行至此) 主动发送心跳消息
if(message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()){
heartBeat = ctx.executor().scheduleAtFixedRate(new HeartBeatTask(ctx), 0, 5000, TimeUnit.MILLISECONDS);
}else if(message.getHeader().getType() == MessageType.HEARTBEAT_RESP.value()){
System.out.println("Client receive server heart beat message : -- >" + message);
}else{
ctx.fireChannelRead(msg);
} } //心跳探测
private class HeartBeatTask implements Runnable{ private final ChannelHandlerContext ctx; public HeartBeatTask(final ChannelHandlerContext ctx){
this.ctx = ctx;
}
@Override
public void run() {
NettyMessage heatBeat = buildHeatBeat();
System.out.println("client send heart beat message to server : --> " + heartBeat);
ctx.writeAndFlush(heatBeat);
} private NettyMessage buildHeatBeat(){
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.HEARTBEAT_REQ.value());
message.setHeader(header);
return message;
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
if(heartBeat != null){
heartBeat.cancel(true);
heartBeat = null;
}
ctx.fireExceptionCaught(cause);
} }
package com.netty.ch12.handler;

import com.netty.ch12.comon.MessageType;
import com.netty.ch12.msg.Header;
import com.netty.ch12.msg.NettyMessage; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class HeartBeatRespHandler extends ChannelHandlerAdapter{ @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyMessage message = (NettyMessage) msg;
//返回心跳应答消息
if(message.getHeader() != null
&& message.getHeader().getType() == MessageType.HEARTBEAT_REQ.value()){
System.out.println("Receive client heart beat message : ---> " + message);
NettyMessage heartBeat = buildHeatBeat();
System.out.println("Send heart beat response message to client : --->" +heartBeat);
ctx.writeAndFlush(heartBeat);
}else{
ctx.fireChannelRead(message);
}
} private NettyMessage buildHeatBeat(){
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.HEARTBEAT_RESP.value());
message.setHeader(header);
return message;
}
}

客户端

package com.netty.ch12;

import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import com.netty.ch12.comon.NettyConstant;
import com.netty.ch12.handler.HeartBeatReqHandler;
import com.netty.ch12.handler.LoginAuthReqHandler;
import com.netty.ch12.handler.NettyMessageDecoder;
import com.netty.ch12.handler.NettyMessageEncoder; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler; /**
* 客户端主要用于初始化系统资源,根据配置信息发起连接
* @author Administrator
*
*/
public class NettyClient { private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
EventLoopGroup group = new NioEventLoopGroup(); public void connect(int port ,String host) throws Exception{
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4, -8, 0));
ch.pipeline().addLast("MessageEncoder",new NettyMessageEncoder());
//心跳超时的实现非常简单,利用ReadTimeoutHandler机制,当一定周期内(50s)没有读取到对方任何消息,需要主动关闭连接。
//如果是client,发起重新连接,如果是server,释放资源,清除客户端登录缓存信息,等待服务端重连。
ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50));
ch.pipeline().addLast("LoginAuthHandler",new LoginAuthReqHandler());
ch.pipeline().addLast("HeartBeatHandler",new HeartBeatReqHandler());
}
});
//发起异步连接操作
ChannelFuture future = b.connect(new InetSocketAddress(host,port)
,new InetSocketAddress(NettyConstant.LOCALIP
,NettyConstant.LOCAL_PORT));
System.out.println("client to server "+host+":"+port);
future.channel().closeFuture().sync();
}catch(Exception e){
e.printStackTrace();
}finally{
//所有资源释放完成之后,清空资源,再次发起重新连接
executor.execute(new Runnable(){ @Override
public void run() { try {
//等待5s
TimeUnit.SECONDS.sleep(5);
//发起重新连接
connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
} catch (Exception e) {
e.printStackTrace();
} } });
} } public static void main(String[] args) throws Exception{
new NettyClient().connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
}
}

服务端

package com.netty.ch12;

import com.netty.ch12.comon.NettyConstant;
import com.netty.ch12.handler.HeartBeatRespHandler;
import com.netty.ch12.handler.LoginAuthReqHandler;
import com.netty.ch12.handler.LoginAuthRespHandler;
import com.netty.ch12.handler.NettyMessageDecoder;
import com.netty.ch12.handler.NettyMessageEncoder; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler; /**
* 服务端 : 主要的工作就是握手的接入认证 不关心断连重连的问题
* @author Administrator
* netstat -ano|findstr "8080"
*/
public class NettyServer { public void bind() throws Exception{
//配置服务端的NIO线程组
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4, -8, 0));
ch.pipeline().addLast(new NettyMessageEncoder());
//当一定周期内没有读取对方任何消息 则主动关闭链路
ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50));
ch.pipeline().addLast(new LoginAuthRespHandler());
ch.pipeline().addLast("HeartBeatHandler",new HeartBeatRespHandler());
}
}); ChannelFuture f = b.bind(NettyConstant.REMOTEIP,NettyConstant.PORT).sync();
System.out.println("Netty server start ok : "
+(NettyConstant.REMOTEIP + ":" + NettyConstant.PORT));
f.channel().closeFuture().sync();
} public static void main(String[] args) throws Exception {
new NettyServer().bind();
} }

Netty多协议开发的更多相关文章

  1. (高级篇 Netty多协议开发和应用)第十章-Http协议开发应用(基于Netty的HttpServer和HttpClient的简单实现)

    1.HttpServer package nettyHttpTest; import io.netty.bootstrap.ServerBootstrap; import io.netty.chann ...

  2. Netty WebSocket 开发

    代码: Server package netty.protocol.websocket.server; import io.netty.bootstrap.ServerBootstrap; impor ...

  3. Netty权威指南

    Netty权威指南(异步非阻塞通信领域的经典之作,国内首本深入剖析Netty的著作,全面系统讲解原理.实战和源码,带你完美进阶Netty工程师.) 李林锋 著   ISBN 978-7-121-233 ...

  4. 《深入浅出Netty》【PDF】下载

    <深入浅出Netty>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062563 内容简介 本文档主要讲述的是深入浅出Netty: ...

  5. 《Netty权威指南》目录

    一.基础篇 走进Java NIO 1. Java 的 I/O 演进之路:https://www.cnblogs.com/zengzhihua/p/9930652.html 2. NIO 入门:http ...

  6. 《Netty权威指南》

    <Netty权威指南> 基本信息 作者: 李林锋 出版社:电子工业出版社 ISBN:9787121233432 上架时间:2014-5-29 出版日期:2014 年6月 开本:16开 页码 ...

  7. 《Netty 权威指南(第2 版)》目录

    图书简介:<Netty 权威指南(第2 版)>是异步非阻塞通信领域的经典之作,基于最新版本的Netty 5.0 编写,是国内很难得一见的深入介绍Netty 原理和架构的书籍,也是作者多年实 ...

  8. 重磅!阿里P8费心整理Netty实战+指南+项目白皮书PDF,总计1.08G

    前言 Netty是一款用于快速开发高性能的网络应用程序的Java框架.它封装了网络编程的复杂性,使网络编程和Web技术的最新进展能够被比以往更广泛的开发人员接触到. Netty不只是一个接口和类的集合 ...

  9. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

随机推荐

  1. 题解 洛谷 P4899 【[IOI2018] werewolf 狼人】

    先考虑狼形,其只能走编号小于\(R\)的点.若将每条边赋边权为其两端点编号的较大值,然后按最小生成树的顺序构建\(Kruskal\)重构树. 那么从原图的一个点\(x\)在树上倍增,到达满足要求且深度 ...

  2. 痞子衡嵌入式:16MB以上NOR Flash使用不当可能会造成软复位后i.MXRT无法正常启动

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT上使用16MB以上NOR Flash软复位无法正常启动问题的分析解决经验. 痞子衡这几天在支持一个i.MXRT1050客户项 ...

  3. DVWA SQL 注入关卡初探

    1. 判断回显 给id参数赋不同的值,发现有不同的返回信息 2. 判断参数类型 在参数后加 ' ,查看报错信息 数字型参数左右无引号,字符型参数左右有引号 4. 引号闭合与布尔类型判断 由于是字符型参 ...

  4. 在npm发布自己造的轮子

    提到封装组件,发布到npm,很多同学都会觉得很神秘.但其实,npm包无非就是我们平时写的比较独立且可复用的模块.当然,想要发布,除了基础组件的编写外,还要进行一些包装.下文通过一个简单的案例,和大家一 ...

  5. Canal简介

    以下内容主要摘自Canal 官方wiki和网友博客:https://www.jianshu.com/p/6299048fad66 一.背景 早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨 ...

  6. Spring Security和Swagger2集成报错

    出现问题的项目算是一个新项目,但基本的脚手架代码是从另一个项目里迁过来的,原项目并没有报错,只有新项目才报异常.看报错内容,判断发生冲突的主要是spring-boot-starter-security ...

  7. CentOS部署RabbitMQ

    CentOS版本:CentOS-7-x86_64-DVD-1804 RabbitMQ版本:3.7.24 1. 下载安装包 因为RabbitMQ是erlang语言开发的,所以需要提前安装erlang环境 ...

  8. day16.内置方法与模块

    一.内置方法 1.abs 绝对值函数 res = abs(-100) print(res) 2.round 四舍五入 (n.5 n为偶数则舍去 n.5 n为奇数,则进一!) ""& ...

  9. 字节真题 ZJ26-异或:使用字典树减少计算次数

    原题链接 题目描述: 个人分析:从输入数据看,要处理的元素个数(n)没有到达 10^9 或 10^8 级,或许可以使用暴力?但是稍微计算一下,有 10^5 * (10^5 - 1) / 2 = 10^ ...

  10. 使用javaScript 取cookie时需要注意的

    function getCookie(name) { var cookies = window.top.document.cookie.split('; ');//分号后面有个空格 for (var ...