Http协议即超文本传输协议,是建立在TCP传输协议之上的应用层协议,他目前主流是针对WEB开发,HTTP协议应用非常广泛,因此掌握HTTP协议的开发非常之重要。我们主要讲解Netty如何基于HTTP协议进行开发,那么使用Netty的HTTP协议也是异步非阻塞的。
简单:客户端请求服务器时主需要指定URL和携带必要的参数即可。
灵活:HTTP协议允许传输任意类型的数据类型,传输内容由HTTP消息头中的Content-Type加以标记。
无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理客户的请求,并受到客户端的应答后,就断开连接。采用这种方式可以节省传输时间。
无状态:HTTP协议是无状态的,无状态指的是协议对事务处理没有记忆能力。这意味着如果后续处理需要之前的信息,则它必须重新获取。也从侧面体现了HTTP的设计是为了使网络传输更加的轻量级、敏捷、负载较轻。
请求行:用来说明请求类型,要访问的资源以及所使用的HTTP版本。
请求头:紧接着请求行(第一行)之后的部分,用来说明服务器要使用的附加信息。
空行:请求头部后面的空行是必须的。即使第四部分的请求正文是空的也必须要有空行。
请求正文(实体内容):可以添加任意的其他数据。
GET:获取Reqeust-URI所标识的资源
POST:在Request-URI所标识的资源附加新的提交数据
HEAD:请求获取由Request-URI所标识的资源的响应消息头
PUT:请求服务器存储一个资源,并用Request_URI作为一个标识
DELETE:请求服务器删除Request-URI所标识的资源
TRACE:请求服务器回送收到的请求信息,主要是测试和诊断使用(@trace)
CONNECT:保留将来使用
OPTIONS:请求查询服务器的性能
响应消息由三部分组成:状态行、消息头、响应正文。
响应状态的种类:
1xx:提示消息。表示请求已经接收继续处理。
2xx:成功。表示请求已经接收成功。
3xx:重定向。要完成的请求必须进行更一步的操作。
4xx:客户端错误。可能是请求语法错误或者请求无法实现。
5xx:服务端错误。服务器未能处理请求(可能内部出现异常)。
常用相应状态码:
200 OK:成功。
400 Bad Request :错误的请求语法,不能被服务器理解。
401 Unauthorized:请求未经授权。
403 Forbidden:服务器收到请求,但请求被服务器拒绝。
404 Not Found:请求资源不存在。
405 Method Not Allowed:请求方式不被允许,如只支持get请求,但客户端使用了post请求。
500 Inernal Server Error:服务器发生不可预测的错误。
503 Server Unavailable:服务器当前不能处理客户端请求,一段时间后可能恢复正常
服务器Server:
package com.demo.testNetty.http;
import java.security.cert.CertificateException;
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 io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
public class HttpHelloWorldServer {
//这个是拿来判断有没有安全协议的。
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));
public static void main(String[] args) throws Exception {
// 配置 SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
//配置服务器
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap sb = new ServerBootstrap();
sb.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
//需要将SSL带到初始化那
.childHandler(new HttpHelloWorldServerInitializer(sslCtx));
//绑定端口PORT
ChannelFuture ch = sb.bind(PORT).sync();
System.err.println("Open your web browser and navigate to " +
(SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
ch.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
服务器Initializer:
package com.demo.testNetty.http;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SslContext;
public class HttpHelloWorldServerInitializer extends ChannelInitializer{
private final SslContext sslCtx;
public HttpHelloWorldServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if(sslCtx != null){ //如果安全协议不为空,则要添加多一个handler
p.addLast(sslCtx.newHandler(ch.alloc()));
}
p.addLast(new HttpServerCodec()); //http通信的必须.通信都使用HttpRequest和HttpResponse(当然了,是Netty自带的)
p.addLast(new HttpHelloWorldServerHandler()); //自己的业务处理Handler
}
}
服务器Handler:
package com.demo.testNetty.http;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
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.HttpHeaderUtil;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter{
private static final byte[] CONTENT = { 'M', 'O', 'S', 'I', ' ', 'M', 'O', 'S', 'I', };
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush(); //加上这个是最稳的,万一真的忘记flush 哈哈
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof HttpRequest){
HttpRequest request = (HttpRequest) msg;
if (HttpHeaderUtil.is100ContinueExpected(request)) {
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
}
boolean keepAlive = HttpHeaderUtil.isKeepAlive(request);
//构造响应 设置消息头
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT));
response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
if (!keepAlive) { //如果是短连接,发送完就关闭通道
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} else { //如果是长连接,消息头设置,连接保持活跃
response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.write(response);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
客户端呢,就是我们的浏览器咯。
执行服务器:
[11:43:05] nioEventLoopGroup-0-0 INFO [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2] REGISTERED
Open your web browser and navigate to http://127.0.0.1:8080/
[11:43:05] nioEventLoopGroup-0-0 INFO [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2] BIND: 0.0.0.0/0.0.0.0:8080
[11:43:05] nioEventLoopGroup-0-0 INFO [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2, /0:0:0:0:0:0:0:0:8080] ACTIVE
我们复制上面的URL再客户端访问以下。
文件的上传和下载就不在这里弄了。
给个代码的连接:http://note.youdao.com/noteshare?id=487954c7b6254e905da5146fcfbc02f7