前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Tomcat NIO(10)-IO线程-关键类

Tomcat NIO(10)-IO线程-关键类

作者头像
TA码字
发布于 2020-09-30 03:01:43
发布于 2020-09-30 03:01:43
1.3K00
代码可运行
举报
文章被收录于专栏:TA码字TA码字
运行总次数:0
代码可运行

上一篇文章里我们主要介绍了 tomcat io 线程的 overall 调用流程以及关键类SocketProcessor 和 ConnectionHandler 的核心逻辑总结,这里我们主要来介绍剩余其它的核心类 AbstractProcessorLight,Http11Processor,CoyoteAdapter。

AbstractProcessorLight核心逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException {
    SocketState state = SocketState.CLOSED;
    Iterator<DispatchType> dispatches = null;
    do {
        if (dispatches != null) {
            DispatchType nextDispatch = dispatches.next();
            state = dispatch(nextDispatch.getSocketStatus());
        } else if (status == SocketEvent.DISCONNECT) {
            // Do nothing here, just wait for it to get recycled
        } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
            state = dispatch(status);
            if (state == SocketState.OPEN) {
                // There may be pipe-lined data to read. If the data isn't
                // processed now, execution will exit this loop and call
                // release() which will recycle the processor (and input
                // buffer) deleting any pipe-lined data. To avoid this,
                // process it now.
                state = service(socketWrapper);
            }
        } else if (status == SocketEvent.OPEN_WRITE) {
            // Extra write event likely after async, ignore
            state = SocketState.LONG;
        } else if (status == SocketEvent.OPEN_READ){
            state = service(socketWrapper);
        } else {
            // Default to closing the socket if the SocketEvent passed in
            // is not consistent with the current state of the Processor
            state = SocketState.CLOSED;
        }

        if (getLog().isDebugEnabled()) {
            getLog().debug("Socket: [" + socketWrapper + "], Status in: [" + status + "], State out: [" + state + "]");
        }

        if (state != SocketState.CLOSED && isAsync()) {
            state = asyncPostProcess();
            if (getLog().isDebugEnabled()) {
                getLog().debug("Socket: [" + socketWrapper + "], State after async post processing: [" + state + "]");
            }
        }

        if (dispatches == null || !dispatches.hasNext()) {
            dispatches = getIteratorAndClearDispatches();
        }
    } while (state == SocketState.ASYNC_END || dispatches != null && state != SocketState.CLOSED);
    return state;
}
  • 该类的核心方法为 process() ,会被在上一篇文章之中介绍的 ConnectionHandler 对象实例的 process() 方法调所用。
  • 该方法根据不同的 socket 事件和是否采用异步处理来进行不同的调用,返回期望的 SocketState 状态,这里我们只对非异步的正常调用介绍。
  • 对于非异步的正常调用下,SocketEvent 为 OPEN_READ ,进入Http11Processor 实例的 service() 方法。
  • 对于其他未知的 SocketEvent 事件来说,返回给 ConnectionHandler 实例的SocketState 为 CLOSED 。根据以前文章,这样的结果会被给 SocketProcessor 关闭原始 socket 。

Http11Processor的核心代码逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public AbstractProcessor(Adapter adapter) {
     this(adapter, new Request(), new Response());
}
//Http11Processor
public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
    super(adapter);
    this.protocol = protocol;

    httpParser = new HttpParser(protocol.getRelaxedPathChars(),
            protocol.getRelaxedQueryChars());

    inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
            protocol.getRejectIllegalHeaderName(), httpParser);
    request.setInputBuffer(inputBuffer);

    outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
    response.setOutputBuffer(outputBuffer);

    // Create and add the identity filters.
    inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new IdentityOutputFilter());

    // Create and add the chunked filters.
    inputBuffer.addFilter(new ChunkedInputFilter(protocol.getMaxTrailerSize(),
            protocol.getAllowedTrailerHeadersInternal(), protocol.getMaxExtensionSize(),
            protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new ChunkedOutputFilter());

    // Create and add the void filters.
    inputBuffer.addFilter(new VoidInputFilter());
    outputBuffer.addFilter(new VoidOutputFilter());

    // Create and add buffered input filter
    inputBuffer.addFilter(new BufferedInputFilter());

    // Create and add the chunked filters.
    //inputBuffer.addFilter(new GzipInputFilter());
    outputBuffer.addFilter(new GzipOutputFilter());

    pluggableFilterIndex = inputBuffer.getFilters().length;
}
public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        setSocketWrapper(socketWrapper);
        inputBuffer.init(socketWrapper);
        outputBuffer.init(socketWrapper);

        keepAlive = true;
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;

        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null && sendfileState == SendfileState.DONE && !protocol.isPaused()) {
            // Parsing the request header
            try {
                if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(), protocol.getKeepAliveTimeout())) {
                    if (inputBuffer.getParsingRequestLinePhase() == -1) {
                        return SocketState.UPGRADING;
                    } else if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }

                if (protocol.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);
                    setErrorState(ErrorState.CLOSE_CLEAN, null);
                } else {
                    keptAlive = true;
                    // Set this every time in case limit has been changed via JMX
                    request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
                    if (!inputBuffer.parseHeaders()) {
                        // We've read part of the request, don't recycle it
                        // instead associate it with the socket
                        openSocket = true;
                        readComplete = false;
                        break;
                    }
                    if (!protocol.getDisableUploadTimeout()) {
                        socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
                    }
                }
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("http11processor.header.parse"), e);
                }
                setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                UserDataHelper.Mode logMode = userDataHelper.getNextMode();
                if (logMode != null) {
                    String message = sm.getString("http11processor.header.parse");
                    switch (logMode) {
                        case INFO_THEN_DEBUG:
                            message += sm.getString("http11processor.fallToDebug");
                        case INFO:
                            log.info(message, t);
                            break;
                        case DEBUG:
                            log.debug(message, t);
                    }
                }
                // 400 - Bad Request
                response.setStatus(400);
                setErrorState(ErrorState.CLOSE_CLEAN, t);
            }

            // Has an upgrade been requested?
            Enumeration<String> connectionValues = request.getMimeHeaders().values("Connection");
            boolean foundUpgrade = false;
            while (connectionValues.hasMoreElements() && !foundUpgrade) {
                foundUpgrade = connectionValues.nextElement().toLowerCase(
                        Locale.ENGLISH).contains("upgrade");
            }

            if (foundUpgrade) {
                // Check the protocol
                String requestedProtocol = request.getHeader("Upgrade");

                UpgradeProtocol upgradeProtocol = protocol.getUpgradeProtocol(requestedProtocol);
                if (upgradeProtocol != null) {
                    if (upgradeProtocol.accept(request)) {
                        response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
                        response.setHeader("Connection", "Upgrade");
                        response.setHeader("Upgrade", requestedProtocol);
                        action(ActionCode.CLOSE,  null);
                        getAdapter().log(request, response, 0);

                        InternalHttpUpgradeHandler upgradeHandler =
                                upgradeProtocol.getInternalUpgradeHandler(
                                        socketWrapper, getAdapter(), cloneRequest(request));
                        UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
                        action(ActionCode.UPGRADE, upgradeToken);
                        return SocketState.UPGRADING;
                    }
                }
            }

            if (getErrorState().isIoAllowed()) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                }
            }

            int maxKeepAliveRequests = protocol.getMaxKeepAliveRequests();
            if (maxKeepAliveRequests == 1) {
                keepAlive = false;
            } else if (maxKeepAliveRequests > 0 &&
                    socketWrapper.decrementKeepAlive() <= 0) {
                keepAlive = false;
            }

            // Process the request in the adapter
            if (getErrorState().isIoAllowed()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    getAdapter().service(request, response);
                    // Handle when the response was committed before a serious
                    // error occurred.  Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && !getErrorState().isError() && !isAsync() && statusDropsConnection(response.getStatus())) {
                        setErrorState(ErrorState.CLOSE_CLEAN, null);
                    }
                } catch (InterruptedIOException e) {
                    setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                } catch (HeadersTooLargeException e) {
                    log.error(sm.getString("http11processor.request.process"), e);
                    // The response should not have been committed but check it
                    // anyway to be safe
                    if (response.isCommitted()) {
                        setErrorState(ErrorState.CLOSE_NOW, e);
                    } else {
                        response.reset();
                        response.setStatus(500);
                        setErrorState(ErrorState.CLOSE_CLEAN, e);
                        response.setHeader("Connection", "close"); // TODO: Remove
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                    getAdapter().log(request, response, 0);
                }
            }

            // Finish the handling of the request
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
            if (!isAsync()) {
                // If this is an async request then the request ends when it has
                // been completed. The AsyncContext is responsible for calling
                // endRequest() in that case.
                endRequest();
            }
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            if (getErrorState().isError()) {
                response.setStatus(500);
            }

            if (!isAsync() || getErrorState().isError()) {
                request.updateCounters();
                if (getErrorState().isIoAllowed()) {
                    inputBuffer.nextRequest();
                    outputBuffer.nextRequest();
                }
            }

            if (!protocol.getDisableUploadTimeout()) {
                int connectionTimeout = protocol.getConnectionTimeout();
                if(connectionTimeout > 0) {
                    socketWrapper.setReadTimeout(connectionTimeout);
                } else {
                    socketWrapper.setReadTimeout(0);
                }
            }

            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            sendfileState = processSendfile(socketWrapper);
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        if (getErrorState().isError() || protocol.isPaused()) {
            return SocketState.CLOSED;
        } else if (isAsync()) {
            return SocketState.LONG;
        } else if (isUpgrade()) {
            return SocketState.UPGRADING;
        } else {
            if (sendfileState == SendfileState.PENDING) {
                return SocketState.SENDFILE;
            } else {
                if (openSocket) {
                    if (readComplete) {
                        return SocketState.OPEN;
                    } else {
                        return SocketState.LONG;
                    }
                } else {
                    return SocketState.CLOSED;
                }
            }
        }
    }
  • Http11Processor 是 AbstractProcessor 的实现子类,构造函数会创建以前文章中介绍的 TomcatRequest/TomcatResponse/Http11InputBuffer/Http11OutBuffer 对象,设置它们的关联关系,以及关联 CoyoteAdapter 对象。
  • Http11Processor 的核心方法是 service 方法,整体代码比较长,我们着重分析重点。
  • Service 方法会去初始化以前文章中介绍的 Http11InputBuffer 和 Http11OutputBuffer 对象实例,用来解析请求行,请求头,写入数据等等。
  • 首先会去利用 Http11InputBuffer 对象实例的 parseRequestLine() 方法解析请求行,如果请求行没有解析完(例如client没有发完数据),那么返回 SocketState.LONG 的状态。根据上一篇文章, ConnectionHanlder 如果发现返回 LONG 状态,会对 socket 包装对象去注册 OP_READ 事件,并添加到 poller 线程的事件队列里,让 poller 线程继续监听 client 端可读事件发送,从而等待 client 继续发送数据。同时并不会移除原始 socket 和处理类 Http11Processor 的关联关系,也不去回收 Http11Processor 实例,以便保持现有状态(已经解析的数据),当 client 再次发送数据的时候可以继续处理。
  • 再利用 Http11InputBuffer.parseHeaders() 方法解析请求头,如果请求头没有没有解析完(client没有发完数据),则处理方式和上一步解析请求行一样。
  • 该方法中会有一些协议 upgrade 的处理(例如websocket),我们不在这里详细展开。
  • 当请求头和请求行完全解析完毕时,会调用 CoyoteAdapter.service() 方法,该方法会通过 servlet container 调用标准 servlet API
  • Servlet API 正常调用完毕,对于非异步请求回去调用 endRequest() 方法表示结束。在其内部用 Http11InputBuffer.endRequest() 结束请求,用 Http11OutputBuffer.end() 将剩余 response 数据发送到 client 端。
  • 同时对于非异步模式下的 servlet 请求,还会去调用 Http11InputBuffer.nextRequest() 方法和 Http11OutputBuffer.nextRequest() 方法来回收两个实例,以便后续重用,可以提高效率。
  • 对于非异步请求正常结束后,返回的 socket 状态是 SocketState.OPEN 。根据上一篇文章介绍的 ConnectionHandler 对象, OPEN 则表示该连接为长连接,不关闭原始 socket 。所以在关联的 Map 中移除 socket 和 Http11Processor 的对应关系,释放当前 Http11Processor 实例以便后续重用。由于是长连接,所以和异步处理方式一样,对 socket 包装对象注册 OP_READ 事件,并添加到 poller 线程事件队列中,让 poller 线程继续去监听 client 端可读事件,从而结束当前请求,为下一个请求做准备。

CoyoteAdapter的核心代码逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    if (request == null) {
        request = connector.createRequest();
        request.setCoyoteRequest(req);
        response = connector.createResponse();
        response.setCoyoteResponse(res);

        request.setResponse(response);
        response.setRequest(request);

        req.setNote(ADAPTER_NOTES, request);
        res.setNote(ADAPTER_NOTES, response);

        req.getParameters().setQueryStringCharset(connector.getURICharset());
    }

    if (connector.getXpoweredBy()) {
        response.addHeader("X-Powered-By", POWERED_BY);
    }

    boolean async = false;
    boolean postParseSuccess = false;

    req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());

    try {
        // Parse and set Catalina and configuration specific
        // request parameters
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            //check valves if we support async
            request.setAsyncSupported(
                    connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
        }
        if (request.isAsync()) {
            async = true;
            ReadListener readListener = req.getReadListener();
            if (readListener != null && request.isFinished()) {
                // Possible the all data may have been read during service()
                // method so this needs to be checked here
                ClassLoader oldCL = null;
                try {
                    oldCL = request.getContext().bind(false, null);
                    if (req.sendAllDataReadEvent()) {
                        req.getReadListener().onAllDataRead();
                    }
                } finally {
                    request.getContext().unbind(false, oldCL);
                }
            }
            Throwable throwable =
                    (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
            // If an async request was started, is not going to end once
            // this container thread finishes and an error occurred, trigger
            // the async error process
            if (!request.isAsyncCompleting() && throwable != null) {
                request.getAsyncContextInternal().setErrorState(throwable, true);
            }
        } else {
            request.finishRequest();
            response.finishResponse();
        }
    } catch (IOException e) {
        // Ignore
    } finally {
        AtomicBoolean error = new AtomicBoolean(false);
        res.action(ActionCode.IS_ERROR, error);
        if (request.isAsyncCompleting() && error.get()) {
            // Connection will be forcibly closed which will prevent
            // completion happening at the usual point. Need to trigger
            // call to onComplete() here.
            res.action(ActionCode.ASYNC_POST_PROCESS,  null);
            async = false;
        }
        // Access log
        if (!async && postParseSuccess) {
            // Log only if processing was invoked.
            // If postParseRequest() failed, it has already logged it.
            Context context = request.getContext();
            Host host = request.getHost();
            // If the context is null, it is likely that the endpoint was
            // shutdown, this connection closed and the request recycled in
            // a different thread. That thread will have updated the access
            // log so it is OK not to update the access log here in that
            // case.
            // The other possibility is that an error occurred early in
            // processing and the request could not be mapped to a Context.
            // Log via the host or engine in that case.
            long time = System.currentTimeMillis() - req.getStartTime();
            if (context != null) {
                context.logAccess(request, response, time, false);
            } else if (response.isError()) {
                if (host != null) {
                    host.logAccess(request, response, time, false);
                } else {
                    connector.getService().getContainer().logAccess(
                            request, response, time, false);
                }
            }
        }

        req.getRequestProcessor().setWorkerThreadName(null);
        // Recycle the wrapper request and response
        if (!async) {
            updateWrapperErrorCount(request, response);
            request.recycle();
            response.recycle();
        }
    }
}
  • CoyoteAdapter 的核心方法是 service 方法,整体代码比较长,我们着重分析重点。
  • 该方法会去用 tomcat 的 request 和 response 创建 servlet 的标准 request 和 response ,并设置其关联关系,即把 tomcat request 关联到 servlet request ,把 tomcat response 关联到 servlet response 。
  • 通过 servlet container 调用标准 servlet API,connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)
  • 如果不是异步请求,完成servlet API后,通过HttpServletRequest.finishRequest() 方法调用和HttpServletResponse.finishResponse() 方法调用结束当前请求和响应。
  • 最后通过 HttpServletRequest.recycle() 调用和 HttpServletResponse.recycle() 调用来回收请求和响应,以便后面可以重用提高效率。

目前先写到这里,下一篇文章里我们继续介绍 tomcat io 线程中的读写。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TA码字 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
最后那个服务端运行成功,但是未能成功与客户端通信,应该咋检查客户端运行状况呀。。。我看好像都设置好的呀。。。
最后那个服务端运行成功,但是未能成功与客户端通信,应该咋检查客户端运行状况呀。。。我看好像都设置好的呀。。。
回复回复点赞举报
推荐阅读
使用FRP内网穿透工具实现"安全访问"家中群晖NAS
有时出差或者外出需要访问家中的群晖NAS,但由于该群晖NAS没有quickconnect服务(原因你懂的),基于安全考虑也不愿意将NAS通过内网穿透的方式直接暴露到公网上
yuanfan2012
2023/09/06
4.3K0
使用FRP内网穿透工具实现"安全访问"家中群晖NAS
【内网穿透】用frps实现内网穿透
在当今互联网时代,远程访问内网资源已成为一种常见需求。无论是在家访问办公室的电脑,还是远程管理家庭NAS,内网映射都是一种强大的解决方案。
云帆沧海
2024/10/15
1.2K0
群晖NAS使用Docker部署frpc客户端实现内网穿透
群晖默认访问的规则是IP+端口号,并且又有在内网访问比较麻烦。我们经常会有外出使用的需求,需要在外网访问群晖nas进行操作。接下来给大家讲解如何通过群晖docker插件安装frpc客户端,并且通过宝塔nginx配置域名
cnlixs
2022/11/01
10.2K1
群晖NAS使用Docker部署frpc客户端实现内网穿透
【玩转Lighthouse】使用腾讯云轻量应用服务器运用FRP搭建内网穿透服务器并在群晖上面配置使用内网穿透
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
青阳
2022/04/18
4.5K2
linux、centos7 安装 frp 搭建高性能内网穿透服务
frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。
用户2235302
2018/12/27
7.4K0
frp服务端和客户端的配置和使用
frp是一个开源、简洁易用、高性能的内网穿透和反向代理软件,支持 tcp, udp, http, https等协议。
Past
2022/07/12
6K1
搭建frp内网穿透
frp脚本下载:https://github.com/fatedier/frp/releases/
R0A1NG
2022/02/19
1.8K0
搭建frp内网穿透
树莓派 + frp + 公网服务器,实现自定义域名内网穿透,暴露内网服务在公网(多 ssh, 多 web)
FRP 是 Fast Reverse Proxy 的缩写,一款支持 TCP/UDP 快速反向代理的开源软件,可以很方便的内网穿透。和花生壳、Ngrok 等不同,FRP 客户端和服务端的控制权都在自己手中,这也意味着你需要准备一台有公网IP 的 VPS 运行服务端程序。
卓越笔记
2023/02/18
1.7K0
利用frp工具实现内网穿透、随时随地访问内网服务
之前分享过一次《ZeroTier实现内网穿透、异地组网》,其基本工作原理是组建一个虚拟局域网,各个设备(NAS、Linux、Windows、Mac、iOS、Android)安装了客户端、加入到这个虚拟局域网后,就会自动分配一个IP,从而实现局域网内各个设备及服务的相互访问。
大刚测试开发实战
2023/01/18
3K0
内网穿墙利器frp,实现无公网IP穿透(支持windows+linux)
开源项目下载地址→https://github.com/fatedier/frp/releases frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。
Lcry
2022/11/29
4.1K0
内网穿透 - 反向代理 - FRP 使用指南
下面示例将本地服务 http://127.0.0.1:8080/ 反向代理到公网 http://<公网IP>:8080/
轻量级云原生架构实验室
2022/12/13
8250
frp使用说明(转)
frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp 协议,为 http 和 https 应用协议提供了额外的能力,且尝试性支持了点对点穿透。GitHub地址
yaodo
2022/08/26
1.5K0
关于内网穿透:FRP神器
frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。
Khan安全团队
2020/10/10
1.4K0
关于内网穿透:FRP神器
frp实现外网访问群晖synology
    下载Linux(centos7)版本的frp_0.21.0,所有版本frp点这里
用户1086810
2018/09/27
7.3K0
群晖NAS配置之自有服务器frp实现内网穿透
frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议,且支持 P2P 通信。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。今天跟大家分享一下frp实现内网穿透
星哥玩云
2023/11/28
3.6K0
群晖NAS配置之自有服务器frp实现内网穿透
搭建FRP服务进行内网穿透
frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
zuantou
2022/04/25
1.4K0
搭建FRP服务进行内网穿透
【玩转Lighthouse】小白也能FRP内网穿透配置和使用
有时候在想互联网互联网,为什么在异地没有办法通过网络连接家里的NAS,远程控制家里的电脑呢 ? 网上一顿恶补学习,原来是没有分配到 基于 IPV4 的公网 IP,而动态公网 IP 申请 还不一定能成功。 解决的办法可以 使用 基于 IPV6 的公网IP 远程连接 或 使用本文所介绍的FRP进行内网穿透。
用户6795856
2022/04/15
1.4K0
Frp内网穿透
​ 内网穿透从本质上来讲也是端口映射,两者都是将内网地址映射到公网可访问的地址,而区别是端口映射直接在路由器中配置即可,而内网穿透配置的端口映射则需要客户端和服务端进行绑定后实现,相当于客户端和服务端之间建立了一条隧道,然后访问服务端的请求会通过隧道转发给内网主机,该情况多用于没有公网 IP 的情况下使用;
全栈程序员站长
2022/09/30
1.4K0
Frp内网穿透
内网隐藏通信隧道技术——FRP隧道
frp是一个专注于内网穿透的高性能的反向代理应用,支持TCP、UDP、HTTP、HTTPS等多种协议。可以将内网服务以安全、便捷的方式通过具有公网IP节点的中转暴露到公网。frp的好处是利用内网或防火墙后的机器,对外网环境提供http或https服务。对于http和https服务支持基于域名的虚拟主机,支持自定义域名绑定,使多个域名可以共用一个80端口。利用处于内网或防火墙后的机器,对外网环境提供tcp和udp服务,例如在家里通过ssh或者web访问公司内网环境内的主机或者业务进行办公。frp采用Golang编写,支持跨平台,除了二进制文件,没有额外依赖
释然IT杂谈
2022/10/27
3.8K0
内网隐藏通信隧道技术——FRP隧道
使用frps和frpc实现内网穿透
内网穿透的作用包括跨网段访问一个局域网中的一台主机。 如上图,假设我们想要通过主机 A 访问主机 C,但是主机 A 和主机 C 绑定的都是私有 ip 地址,所以它们之间是无法直接进行通信的。要想使得 A 和 C 能够进行通信,就需要用到内网穿透的技术。 我们可以借助 frps(服务端)和 frpc(客户端)来实现主机 A 对主机 C 的访问。 需要做的是: 在绑定了公网 ip 的主机 B 中配置 frps(服务端) 在主机 C 中配置 frpc(客户端) frps/frpc 的工具包的 github 地
入门笔记
2022/06/03
5.1K0
使用frps和frpc实现内网穿透
推荐阅读
相关推荐
使用FRP内网穿透工具实现"安全访问"家中群晖NAS
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档