1.HttpServer
package nettyHttpTest; import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; public class HttpServer { public void start(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// server端发送的是httpResponse,所以要使用HttpResponseEncoder进行编码
ch.pipeline().addLast(new HttpResponseEncoder());
// server端接收到的是httpRequest,所以要使用HttpRequestDecoder进行解码
ch.pipeline().addLast(new HttpRequestDecoder());
ch.pipeline().addLast(new HttpServerInboundHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
HttpServer server = new HttpServer();
System.out.println("Http Server listening on 8844 ...");
server.start(8844);
}
}
2.HttpServerInboundHandler
package nettyHttpTest; import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpRequest; public class HttpServerInboundHandler extends ChannelHandlerAdapter { private HttpRequest request; @Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
if (msg instanceof HttpRequest) {
request = (HttpRequest) msg; String uri = request.getUri();
System.out.println("Uri:" + uri);
}
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
ByteBuf buf = content.content();
System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
buf.release(); String res = "I am OK";
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
OK, Unpooled.wrappedBuffer(res.getBytes("UTF-8")));
response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().set(CONTENT_LENGTH,
response.content().readableBytes());
if (HttpHeaders.isKeepAlive(request)) {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
}
ctx.write(response);
ctx.flush();
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println(cause.getMessage());
ctx.close();
} }
3.HttpClient
package nettyHttpTest; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpVersion; import java.net.URI; public class HttpClient {
public void connect(String host, int port) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 客户端接收到的是httpResponse响应,所以要使用HttpResponseDecoder进行解码
ch.pipeline().addLast(new HttpResponseDecoder());
// 客户端发送的是httprequest,所以要使用HttpRequestEncoder进行编码
ch.pipeline().addLast(new HttpRequestEncoder());
ch.pipeline().addLast(new HttpClientInboundHandler());
}
}); // Start the client.
ChannelFuture f = b.connect(host, port).sync(); URI uri = new URI("http://127.0.0.1:8844");
String msg = "Are you ok?";
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
uri.toASCIIString(), Unpooled.wrappedBuffer(msg.getBytes("UTF-8"))); // 构建http请求
request.headers().set(HttpHeaders.Names.HOST, host);
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes());
// 发送http请求
f.channel().write(request);
f.channel().flush();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
} } public static void main(String[] args) throws Exception {
HttpClient client = new HttpClient();
client.connect("127.0.0.1", 8844);
}
}
4.HttpClientInboundHandler
package nettyHttpTest; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse; public class HttpClientInboundHandler extends ChannelHandlerAdapter { @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponse)
{
HttpResponse response = (HttpResponse) msg;
System.out.println("CONTENT_TYPE:" + response.headers().get(HttpHeaders.Names.CONTENT_TYPE));
}
if(msg instanceof HttpContent)
{
HttpContent content = (HttpContent)msg;
ByteBuf buf = content.content();
System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
buf.release();
}
}
}
服务器
aaarticlea/png;base64,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" alt="" />
客户端
aaarticlea/png;base64,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" alt="" />