Spring MVC源码分析—基于Java中Socket实现HTTP协议
来源:互联网 发布:java服务器性能监控 编辑:程序博客网 时间:2024/04/30 16:22
自己实现网络通讯
1.1 普通Socket用法
Java中的网络通讯是通过Socket实现的,Socket分为ServerSocket和Socket两大类,ServerSocket用于服务端,可以通过accept方法监听请求,监听到请求后返回Socket,Socket用于具体完整数据传输,客户端直接使用Socket发起请求并传输数据。
1.1.1 ServerSocket
ServerSocket的使用分为三步:
- 创建ServerSocket。
- 调用创建出来的ServerSocket的accept方法进行监听。
- 使用accept方法返回的Socket与客户端进行通信。
package com.jangz.deepinspringmvc.socket;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class Server {public static void main(String[] args) {ServerSocket server;try {server = new ServerSocket(8080);Socket socket = server.accept();BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = in.readLine();System.out.println("received from client: " + line);PrintWriter out = new PrintWriter(socket.getOutputStream());out.write("received data: " + line);out.flush();out.close();in.close();socket.close();server.close();} catch (IOException e) {e.printStackTrace();}}}
1.1.2 客户端Socket
package com.jangz.deepinspringmvc.socket;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;public class Client {public static void main(String[] args) {String msg = "Hello, server!";try {Socket socket = new Socket("127.0.0.1", 8080);PrintWriter writer = new PrintWriter(socket.getOutputStream());BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));writer.println(msg);writer.flush();String line = reader.readLine();System.out.println("received from server: " + line);writer.close();reader.close();socket.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}
先启动Server,然后启动Client就可以完成一次通信。
实现效果如下:
1.1.3 NioSocket的用法
从JDK1.4开始,Java增加了新的io模式——nio(new IO),nio在底层采用了新的处理方式,极大地提高了IO的效率。我们使用的Socket也属于IO的一种,nio提供了相应的工具:ServerSocketChannel和SocketChannel,它们分别对应原来的ServerSocket和Socket。
NioSocket三个概念:Buffer、Channel和Selector。
针对NioSocket的处理模式:Buffer就是所要送的货物,Channel就是送货员(或者开往某个区域的配货车),Selector就是中转站的分拣员。
NioSocket中服务端的处理过程可以分为5步:
- 创建ServerSocketChannel并设置相应参数
- 创建Selector并注册到ServerSocketChannel上
- 调用Selector的select方法等待请求
- Selector接收到请求后使用selectedKeys返回SelectionKey集合
- 使用SelectionKey获取到Channel、Selector和操作类型并进行具体操作
NioServer代码实现:
package com.jangz.deepinspringmvc.socket;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;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 NIOServer {public static void main(String[] args) {try {ServerSocketChannel channel = ServerSocketChannel.open();channel.socket().bind(new InetSocketAddress(8080));channel.configureBlocking(false);Selector selector = Selector.open();channel.register(selector, SelectionKey.OP_ACCEPT);// 创建处理器Handler handler = new Handler(1024);while (true) {if (selector.select(3000) == 0) {System.out.println("等待请求超时......");continue;}System.out.println("处理请求......");Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();while (keyIter.hasNext()) {SelectionKey key = keyIter.next();try {if (key.isAcceptable()) {handler.handleAccept(key);}if (key.isReadable()) {handler.handleRead(key);}} catch (IOException ex) {keyIter.remove();continue;}keyIter.remove();}}} catch (IOException e) {e.printStackTrace();}}private static class Handler {private int bufferSize = 1024;private String localCharset = "UTF-8";public Handler() {}public Handler(int bufferSize) {this(bufferSize, null);}public Handler(String localCharset) {this(-1, localCharset);}public Handler(int bufferSize, String localCharset) {if (bufferSize > 0) {this.bufferSize = bufferSize;}if (localCharset != null) {this.localCharset = localCharset;}}public void handleAccept(SelectionKey key) throws IOException {SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();channel.configureBlocking(false);channel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));}public void handleRead(SelectionKey key) throws IOException {SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment();buffer.clear();if (channel.read(buffer) == -1) {channel.close();} else {buffer.flip();String receivedStr = Charset.forName(localCharset).newDecoder().decode(buffer).toString();System.out.println("received frm client: " + receivedStr);String respStr = "received data: " + receivedStr;buffer = ByteBuffer.wrap(respStr.getBytes(localCharset));channel.write(buffer);channel.close();}}}}
自己动手实现HTTP协议
我们知道HTTP协议是应用层解析内容的,只需要按照它的报文的格式封装和解析数据就可以了,具体的传输还是使用的Socket。
因为HTTP协议是在接收到数据之后才会用到的,所以我们只需要修改NioServer中的Handler就可以了,在修改后的HttpServer中首先获取到请求报文并打印出报文的头部(包含首行)、请求的方法类型、Url和Http版本,最后将接收到的请求报文信息封装到响应报文的主体中返回给客户端。这里的HttpHandler使用了单独的线程来执行,而且把SelectionKey中操作类型的选择也放在了HttpHandler中,不过具体处理过程和前面的NioServer没有太大区别。
代码实现:
package com.jangz.deepinspringmvc.http;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;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) {try {ServerSocketChannel channel = ServerSocketChannel.open();channel.socket().bind(new InetSocketAddress(80));channel.configureBlocking(false);Selector selector = Selector.open();channel.register(selector, SelectionKey.OP_ACCEPT);// 创建处理器while (true) {if (selector.select(3000) == 0) {continue;}// 获取待处理的SelectionKeyIterator<SelectionKey> keyIter = selector.selectedKeys().iterator();while (keyIter.hasNext()) {SelectionKey key = keyIter.next();new Thread(new HttpHandler(key)).start();// 处理完成后,从待处理的SelectionKey迭代器中移除当前所使用的keykeyIter.remove();}}} catch (IOException e) {e.printStackTrace();}}private static class HttpHandler implements Runnable {private int bufferSize = 1024;private String localCharset = "UTF-8";private SelectionKey key;public HttpHandler(SelectionKey key) {super();this.key = key;}public void handleAccept() throws IOException {SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();clientChannel.configureBlocking(false);clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));}public void handleRead() throws IOException {// 获取channelSocketChannel socketChannel = (SocketChannel) key.channel();// 获取buffer并重置ByteBuffer buffer = (ByteBuffer) key.attachment();buffer.clear();// 没有读到内容则关闭if (socketChannel.read(buffer) == -1) {socketChannel.close();} else {// 接受请求数据buffer.flip();String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();// 控制台打印请求报文头String[] requestMessage = receivedString.split("\r\n");for (String s : requestMessage) {System.out.println(s);// 遇到空行说明报文头已经打印完if (s.isEmpty()) {break;}}// 控制台打印首行信息String[] firstLine = requestMessage[0].split(" ");System.out.println();System.out.println("Method:\t" + firstLine[0]);System.out.println("url:\t" + firstLine[1]);System.out.println("HTTP Version:\t" + firstLine[2]);System.out.println();// 返回客户端StringBuilder sendString = new StringBuilder();sendString.append("HTTP/1.1 200 OK\r\n"); // 响应报文首行,200表示处理成功sendString.append("Content-Type:text/html;charset=" + localCharset + "\r\n");sendString.append("\r\n"); // 报文头结束后加一个空行sendString.append("<html><head><title>显示报文</title></head><body>");sendString.append("接收到请求报文是:<br/>");for (String s : requestMessage) {sendString.append(s + "<br/>");}sendString.append("</body></html>");buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));socketChannel.write(buffer);socketChannel.close();}}@Overridepublic void run() {try {// 接收到连接请求时if (key.isAcceptable()) {handleAccept();}if (key.isReadable()) {handleRead();}} catch (IOException ex) {ex.printStackTrace();}}}}
README.md
#### 启动代码后,在浏览器中输入http://localhostGET /favicon.ico HTTP/1.1<br/>Host: localhost<br/>Connection: keep-alive<br/>User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36<br/>Accept: image/webp,image/apng,image/*,*/*;q=0.8<br/>Referer: http://localhost/<br/>Accept-Encoding: gzip, deflate, br<br/>Accept-Language: zh-CN,zh;q=0.8<br/><br/><br/>Method:GET<br/>url:/favicon.ico<br/>HTTP Version:HTTP/1.1<br/>
Come on & Regards!
- Spring MVC源码分析—基于Java中Socket实现HTTP协议
- 使用Java socket 实现 HTTP 协议
- Webbench源码分析之socket及http协议(二)
- socket实现http协议
- Java中Socket的用法--Spring MVC
- 利用socket自己实现基于HTTP协议的Web服务器
- 利用socket自己实现基于HTTP协议的Web客户端
- Spring MVC源码分析—Tomcat分析
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- Java中基于HTTP协议网络编程
- Java中基于HTTP协议网络编程
- Java中基于HTTP协议网络编程
- Spring MVC源码分析
- Spring MVC源码分析
- Spring MVC源码分析
- Spring MVC源码分析
- 自己动手实现http协议(看透spring mvc笔记)
- [BZOJ1072][SCOI2007]排列perm(状压dp)
- 《C和指针》读书笔记(五)
- static和const关键字
- 《C和指针》读书笔记(六)
- 《C和指针》读书笔记(七)
- Spring MVC源码分析—基于Java中Socket实现HTTP协议
- 《C和指针》读书笔记(八)
- xml的增删改查,使用dom解析
- 《C和指针》读书笔记(九)
- java数据类型转换新手易错
- bzoj1567: [JSOI2008]Blue Mary的战役地图
- [LeetCode] 561.Array Partition I
- windows安装numpy简单安装方法
- swift笔记--基础(二)