看透SpringMVC系列(二)用NIO自己手动实现HTTP协议
2018-03-14 22:58
267 查看
我们知道HTTP协议是在应用层解析内容的,只需要按照它的报文格式封装和解析数据就可以了,具体的传输还是使用的Socket。
因为HTTP协议是在接受到数据之后才会用到的:代码package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
public class HttpServer {
public static void main(String[] args) throws Exception{
ServerSocketChannel ssc =ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);//非阻塞
//为ssc注册选择器
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(3000) == 0){
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
//启动新线程处理SelectionKey
new Thread(new HttpHandler(key)).run();
it.remove();
}
}
}
private static class HttpHandler implements Runnable{
private int bufferSize = 1024;
private String localChatset ="UTF-8";
SelectionKey key;
public HttpHandler(SelectionKey key) {
this.key = key;
}
public void handleAccept() throws IOException{
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
channel.configureBlocking(false);
channel.register(key.selector(), SelectionKey.OP_READ,
ByteBuffer.allocate(bufferSize));
}
public void handleRead() throws IOException{
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
if(channel.read(buffer) == -1){
channel.close();
}else{
//将buffer转换为读状态
buffer.flip();
String receiveMsg = Charset.forName(localChatset).newDecoder().decode(buffer).toString();
System.out.println("收到数据:" + receiveMsg);
//返回数据给客户端
Strin
4000
gBuilder sendMsg = new StringBuilder();
sendMsg.append("HTTP/1.1 200 OK\r\n");
sendMsg.append("content-type:text/html;charset=" + localChatset
+ "\r\n");
sendMsg.append("\r\n");
sendMsg.append("现在内容将呈现在浏览器中");//响应体
ByteBuffer buffer2 = ByteBuffer.wrap(sendMsg.toString().getBytes());
channel.write(buffer2);
channel.close();
}
}
@Override
public void run() {
try {
if(key.isAcceptable()){
handleAccept();
}
if(key.isReadable()){
handleRead();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}浏览器
控制台输出收到数据:GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.1704055086.1517401277
这里也只是一个简单的事例,目的是了解HTTP协议的实现的方法,这里的功能还不够完善,它不能真正处理请求,实际处理中应该根据不同的url和不同的请求方法进行不同的处理并返回不同的响应报文,另外这里的请求报文也必须在1024范围内,如果太长就会接收不全,而且也不能返回图片等流类型的数据(流类型只需要在响应报文中写清楚Content-Type的类型,并将相应的数据写入报文的主题就可以了),不过对于了解HTTP协议实现的方法以及够用了
因为HTTP协议是在接受到数据之后才会用到的:代码package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
public class HttpServer {
public static void main(String[] args) throws Exception{
ServerSocketChannel ssc =ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);//非阻塞
//为ssc注册选择器
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(3000) == 0){
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
//启动新线程处理SelectionKey
new Thread(new HttpHandler(key)).run();
it.remove();
}
}
}
private static class HttpHandler implements Runnable{
private int bufferSize = 1024;
private String localChatset ="UTF-8";
SelectionKey key;
public HttpHandler(SelectionKey key) {
this.key = key;
}
public void handleAccept() throws IOException{
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
channel.configureBlocking(false);
channel.register(key.selector(), SelectionKey.OP_READ,
ByteBuffer.allocate(bufferSize));
}
public void handleRead() throws IOException{
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
if(channel.read(buffer) == -1){
channel.close();
}else{
//将buffer转换为读状态
buffer.flip();
String receiveMsg = Charset.forName(localChatset).newDecoder().decode(buffer).toString();
System.out.println("收到数据:" + receiveMsg);
//返回数据给客户端
Strin
4000
gBuilder sendMsg = new StringBuilder();
sendMsg.append("HTTP/1.1 200 OK\r\n");
sendMsg.append("content-type:text/html;charset=" + localChatset
+ "\r\n");
sendMsg.append("\r\n");
sendMsg.append("现在内容将呈现在浏览器中");//响应体
ByteBuffer buffer2 = ByteBuffer.wrap(sendMsg.toString().getBytes());
channel.write(buffer2);
channel.close();
}
}
@Override
public void run() {
try {
if(key.isAcceptable()){
handleAccept();
}
if(key.isReadable()){
handleRead();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}浏览器
控制台输出收到数据:GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.1704055086.1517401277
这里也只是一个简单的事例,目的是了解HTTP协议的实现的方法,这里的功能还不够完善,它不能真正处理请求,实际处理中应该根据不同的url和不同的请求方法进行不同的处理并返回不同的响应报文,另外这里的请求报文也必须在1024范围内,如果太长就会接收不全,而且也不能返回图片等流类型的数据(流类型只需要在响应报文中写清楚Content-Type的类型,并将相应的数据写入报文的主题就可以了),不过对于了解HTTP协议实现的方法以及够用了
相关文章推荐
- 利用socket自己实现基于HTTP协议的Web服务器
- 应用层协议实现系列(二)——HTTP服务器之http协议解析
- Netty4系列--实现简单的Http协议
- 利用socket自己实现基于HTTP协议的Web客户端
- Nginx网络epoll多进程系列:应用层协议实现系列(二)——HTTP服务器之http协议解析
- 自己动手实现HTTP协议
- 基于NIO实现客户端通过HTTP协议访问WEB站点
- 自己动手实现HTTP协议
- 应用层协议实现系列(二)——HTTP服务器之http协议解析
- WebApi系列~自主宿主HttpSelfHost的实现
- 快速用springmvc搭建web应用-超越昨天的自己系列(10)
- 【Java Servlet 开发系列之二】创建WebApp详细步骤,通过Servlet实现http简单交互
- SpringMVC 学习系列 (2) 之 经典的HelloWorld 实现
- API入门系列之六 -自己实现MessageBox
- 【Jetty Server 开发系列之一】搭建Jetty Server环境&&Http客户端实现交互
- 用C#语言实现http协议下的多线程文件传输
- 蜜果私塾:http协议学习和总结系列…
- 一起谈.NET技术,用C#实现HTTP协议下的多线程文件传输
- 用TCP/IP实现自己简单的应用程序协议:成帧器部分
- C#实现HTTP协议下的多线程文件传输