概括: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)