tomcat 内置版 处理 http springboot2.2.4.RELEASE

概括:java服务器传输数据是sock来完成的。tomcat也不例外。当tomcat 检测到 请求后,得到sock,从sock中取出流,得到 byte数组,然后就开始 读取请求的 url 请求的头。再根据请求头的数据类型,判断这个 这个数据是 字符窜还是 文件。
如果是 字符串 就直接读取,如果是文件 就走文件的处理类。

当传输的数据是 josn 时 是 类 IdentityInputFilter.doRead处理的
当传输文件时 是类 ChunkedInputFilter.doRead处理

serverSock创建

在类 NioEndpoint 的 initServerSocket 方法中开启了 Socket 服务,用于接送请求

            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
            serverSock.socket().bind(addr,getAcceptCount());

接受请求

在 Acceptor 的 run 方法中

//接受链接请求
socket = endpoint.serverSocketAccept();
//把得到的链接放入队列
endpoint.setSocketOptions(socket)==》poller.register(channel, socketWrapper);

从队列中取出链接来执行 NioEndpoint 的内部类 Poller 的 run 方法

//重队列中取出链接
sc = processorCache.pop();
//链接放入线程池中 运行
executor.execute(sc);

Sock运行

1、开始 在 SocketProcessorBase 类的 run 方法中,从这里开始 就开始对链接 经行处理

    @Override
    public final void run() {
        synchronized (socketWrapper) {
            // It is possible that processing may be triggered for read and
            // write at the same time. The sync above makes sure that processing
            // does not occur in parallel. The test below ensures that if the
            // first event to be processed results in the socket being closed,
            // the subsequent events are not processed.
            //有可能触发读和处理
			//同时写入。上面的同步确保处理
			//不能并行执行。下面的测试确保
			//处理的第一个事件导致套接字被关闭;
			//后续事件不处理。
            if (socketWrapper.isClosed()) {
                return;
            }
            doRun();
        }
    }

2、读取数据

从上面的 sock 可以看出 这是一个 SocketChannelImpl 直接去这个类的 读取方法打上断点。如图,他会把数据读到缓存中来
在这里插入图片描述
读取数据到缓存

inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(), protocol.getKeepAliveTimeout())

从图片中可以看出,请求方式、请求头都读到这个 byte 缓存数组里面来了
在这里插入图片描述
3、解析 从数据中解析出url,所用的协议,请求方式

请求方式 Http11InputBuffer.parseRequestLine方法 ,从缓冲队列里的第一个字符 取 值 直到找到 空格 就把前面的 字符窜作为 请求方式。
在这里插入图片描述
url 和 url中的 差数 Http11InputBuffer.parseRequestLine 接着上面的 发现空格后的地方 ,直到发现空格,这一断数据作为 url 要是 在 这中间发现了?好代表有参数 就把参数 也放到 里面。
在这里插入图片描述

协议 Http11InputBuffer.parseRequestLine方法 ,接到 上面的空格到换行,作为协议的版本。
在这里插入图片描述
4、请求头
Http11InputBuffer.parseHeader 上面三个数据都处理完了 一行,请求的数据从第二行开始一直间隙,根据 换行来区分开 为一个 头属性,根据 : 来区分 头的 键值对。直到出现连续空格。

在这里插入图片描述
在这里插入图片描述

处理请求

http发送的数据 前面是头 后面是数据

POST /rrrrr HTTP/1.1
Content-Type: application/json
Content-Length: 44
Host: 127.0.0.1:9965

{"aaa":"成功","dddd":"dfasdfasdfasdfasdf"}

接收类 Servlet 类 不知道 spring boot 怎样注册 Servlet 请参照 DispatcherServlet

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        ServletInputStream inputStream = req.getInputStream();
        byte[] bytes = new byte[1024];
        while (true) {
            int read = inputStream.read(bytes);
            if (read == -1) break;
            String s = new String(bytes, 0, read);
            System.out.print(s);
        }
    }

数据结构
RequestFacade 自己写的 Servlet
----request
--------inputBuffer
------------ coyoteRequest 这个就是 tomcat 创建的request
---------------- inputBuffer 这个就是去读 sock 的数据

RequestFacade.getInputStream 创建流
----request.getInputStream
--------new CoyoteInputStream(inputBuffer)

第一步 Request 的创建 在 AbstractProcessor 的构造方法中 创建了 Request 和 Response

在这里插入图片描述
第二步 把 inputBuffer 添加到 Request
在这里插入图片描述
第三步
读取 数据 inputBuffer 去读取 sock 中的数据 在这个读取的时候 就会去 解析 url 请求头。
在这里插入图片描述
第 四步 进入我们的代码 处理 流数据
在这里插入图片描述

在这里插入图片描述

流程图
在这里插入图片描述

tomcat 处理http

第一步 船舰serverSock   NioEndpoint.initServerSocket
ServerSocketChannel serverSock = ServerSocketChannel.open();
InetSocketAddress addr = new InetSocketAddress(9985);
serverSock.socket().bind(addr, 100);2步 监听端口得到链接 并把他加到队列 
Acceptor.ruu
	socket = endpoint.serverSocketAccept();  NioEndpoint
		serverSock.accept();
	endpoint.setSocketOptions(socket)
		channel = nioChannels.pop();
		channel = new NioChannel(bufhandler);
		channel.reset(socket, newWrapper);
			this.sc = channel;                            //socket包装到channel
			this.socketWrapper = socketWrapper;
			bufHandler.reset();
		socketProperties.setProperties(socket.socket());//设置属性
		poller.register(channel, socketWrapper); //加入队列
			r = eventCache.pop();
			r.reset(socket, OP_REGISTER);
				socket = ch;
				interestOps = intOps
			addEvent(r);
				events.offer(event);// 发起事件 触发监听 处理请求
					queue[insert++] = t;3步 在上面的队列取出 	socket 做一些事  并加入线程池 这个线程 事一直在跑的没有停过  ------------------------ 上面的 线程事 来一个 加一个 停过
NioEndpoint.Poller.run
	events()
		eventCache.push(pe);
			stack[index] = obj;
	processKey(sk, socketWrapper); 取出来处理
		!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)
			sc = processorCache.pop();
				sc.reset(socketWrapper, event);
			sc = createSocketProcessor(socketWrapper, event);
			 executor.execute(sc);4步 执行
runWorker(this);线程池开始启动
	task.run();  SocketProcessorBase.run
		doRun();
			state = getHandler().process(socketWrapper, event);
				state = processor.process(wrapper, status);
					state = service(socketWrapper);
					
						//Http11InputBuffer 处理链接
						inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(), protocol.getKeepAliveTimeout())  
							!fill(false)
								nRead = socketWrapper.read(block, byteBuffer);
									nRead = fillReadBuffer(block, to);
										NioChannel socket = getSocket();
										nRead = socket.read(to);
											sc.read(dst); SocketChannelImpl
												n = IOUtil.read(fd, buf, -1, nd); 读取数据到 缓冲池
							解析请求方法 循环处理
							byte chr = byteBuffer.get();
							if (chr == Constants.SP || chr == Constants.HT)判断出现空格 第一个空格的到请求方式 循环结束
							request.method().setBytes(byteBuffer.array(), parsingRequestLineStart, pos - parsingRequestLineStart); 设置请求方式
							
							解析url和url中的参数  循环处理 
							byte chr = byteBuffer.get();
							if (chr == Constants.SP || chr == Constants.HT)判断出现空格 第一个空格 到 第二个空格 表示 url 循环结束
							
							if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) //Constants.QUESTION='?' 出现第一个问号时候表示有url传参数
							
							request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1, end - parsingRequestLineQPos - 1); 设置参数url中的
							request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart); 设置url
							
							解析请求是那个协议
							chr == Constants.CR chr == Constants.LF		\r\n 读取到这个就代表结束
							request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart, end - parsingRequestLineStart);设置请求协议
							
							解析一些请求头
							inputBuffer.parseHeaders()
								//循环执行下面的代码 只要灭有读取完
								status = parseHeader();
									//在字符缓冲流中找 :  找到了就是 头
									headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start,  pos - headerData.start); 添加对象头 的名称
									headerData.headerValue.setBytes(byteBuffer.array(), headerData.start, headerData.lastSignificantChar - headerData.start);把对象头的数据 添加进去
							
							设置http特有的过滤 和请求头
						prepareRequest();
								检查连接头
								检查用户代理报头
								检查主机报头
								检查绝对uri是否少于已经存在的查询字符串 在解析请求行期间被删除
								验证URI中的字符。在解码时将检查%nn解码。
								解析传输编码头
								解析内容长度报头
								验证主机名并提取端口(如果存在)
								
							在适配器中处理请求 处理 过滤 器 处理请求
						getAdapter().service(request, response);
								解析 Servlet ===============================================================================================
								postParseSuccess = postParseRequest(req, request, res, response);
									connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());
										internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
											internalMapWrapper(contextVersion, uri, mappingData);
												mappingData.wrapper = contextVersion.defaultWrapper.object;  设置 DispatcherServlet
								执行
								connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); 执行过滤 StandardEngineValve.invoke
									Host host = request.getHost()
									host.getPipeline().getFirst().invoke(request, response);  ErrorReportValve.invoke
										getNext().invoke(request, response);
											Context context = request.getContext();   TomcatEmbeddedContext
											context.getPipeline().getFirst().invoke(request, response); NonLoginAuthenticator.invoke
												getNext().invoke(request, response);
													wrapper.getPipeline().getFirst().invoke(request, response);   StandardWrapperValve.invoke
													
														servlet = wrapper.allocate();
															 return instance;  这个就是DispatcherServlet
														filterChain.doFilter(request.getRequest(), response.getResponse());//开始执行过滤
															循环执行过滤器 直到结束
															filter.doFilter(request, response, this);
															执行 Servlet ===============================================================================================
															servlet.service(request, response);
																service(request, response);
																	super.service(request, response);
																		doGet(req, resp);
																			processRequest(request, response);
																				doService(request, response);
																					logRequest(request);//解析文件解析参数
																						params = (request.getParameterMap().isEmpty() ? "" : "masked");
																							return request.getParameterMap();
																								Enumeration<String> enumeration = getParameterNames();
																									parseParameters();
																										parseParts(false);解析文件
																											List<FileItem> items = upload.parseRequest(new ServletRequestContext(this));  this=request:upload=ServletFileUpload 
																												//根据request得到文件流 校验第一个发送 文件的 流
																												FileItemIterator iter = getItemIterator(ctx);
																													return new FileItemIteratorImpl(this, ctx);
																														findNextItem();
																															nextPart = multi.skipPreamble();
																																discardBodyData();
																																	return readBodyData(null);
																												//生成保存文件的的文件流 C:\Users\xiangliang\AppData\Local\Temp\tomcat.1360107831101238772.9965\work\Tomcat\localhost\ROOT\upload_322a4d8b_7a2e_41d4_9966_50d940f2e467_00000000.tmp
																												FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), fileName);
																												//把网咯流拷贝到 文件中
																												Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer);
																					doDispatch(request, response); 
																					好了  后面就是 mvc得事情了
																					
															
															
									
javax.servlet.ServletContainerInitializer   tomcat 会去找这个接口的所有实现类
	
	
数据流转过程
SocketChannelImpl网卡
	NioEndpoint.NioSocketWrapper.socketBufferHandler(类对象SocketBufferHandler).readBuffer   Request.inputBuffer.activeFilters.buffer.wrapper
			Request.inputBuffer.activeFilters.readChunk 
				MultipartStream.buffer
					new byte[8194]
				
				

Request.inputBuffer.activeFilters.buffer 



request 创建过程
AbstractHttp11Protocol.createProcessor 
		new Http11Processor(this, adapter);
				super(adapter);
						this(adapter, new Request(), new Response());// 创建Request
						request = coyoteRequest;
						response = coyoteResponse;
				inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(), protocol.getRejectIllegalHeaderName(), httpParser); 这个就是去读 sock 的数据
				request.setInputBuffer(inputBuffer);
 

connector.createRequest()
		return new Request(this);

request.setCoyoteRequest(req); req 是上面的request
	    this.coyoteRequest = coyoteRequest;
        inputBuffer.setRequest(coyoteRequest);


new RequestFacade(this)

RequestFacade 自己写的 Servlet
	request
		inputBuffer
			coyoteRequest 这个就是 tomcat 创建的request
				inputBuffer 这个就是去读 sock 的数据
				
RequestFacade.getInputStream 创建流
	request.getInputStream
		new CoyoteInputStream(inputBuffer)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值