You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Socket流转发异常:转发HTTP请求后未收到Web服务器响应

HTTP代理收不到服务器响应的问题修复

我做了一个HTTP请求监听程序,需求是:从客户端Socket(Socket1)的InputStream提取数据后,转发到连接Web服务器的Socket(Socket2)的OutputStream;再把Web服务器返回的Socket2的InputStream回传给Socket1的OutputStream。目前Web服务器能成功收到请求,但我这边收不到响应。查资料说要关闭Socket输出流后再读输入流,我试了调用socket.shutdownOutput(),但问题还是没解决。相关代码如下:

原监听方法代码

private static void runHttpListener(Boolean simulate){
    try {
        ServerSocket server = new ServerSocket(PORT_HTTP_REQUEST);
        System.out.println("HTTP listener active on Port "+PORT_HTTP_REQUEST);
        boolean keepAlive = true;
        while (keepAlive) {               
            //Empfangen und auslesen
            Socket socketReceivingProxy = server.accept();  
            System.out.println("HTTP-Listener: Accepted client connection");              
            InputStream proxyInputStream = socketReceivingProxy.getInputStream();
            OutputStream proxyOutputStream = socketReceivingProxy.getOutputStream();
            //TODO: extract information from stream
            
            //forward
            InputStream result = sendHttpRequestToDestination(proxyInputStream, simulate);
            result.transferTo(proxyOutputStream);
            

            result.close();
            proxyOutputStream.close();
            socketReceivingProxy.close();
        }
        server.close();  
        System.out.println("HTTP listener closed");    
    } catch (Exception e) {
        e.printStackTrace();
    }
}

原转发方法代码

private static InputStream sendHttpRequestToDestination(InputStream incomingRequest, Boolean simulate){
    try{
        
        Socket socketForwardingWebapp = new Socket(simulate?URL_WEB_SERVER_SIMULATION:URL_WEB_SERVER, 
            simulate?PORT_WEB_SERVER_SIMULATION:PORT_WEB_SERVER);
        System.out.println("HTTP-Forwarding: Created socket "+socketForwardingWebapp.getInetAddress()+":"+socketForwardingWebapp.getPort());  
        InputStream webappInputStream = socketForwardingWebapp.getInputStream();
        OutputStream outputStream = socketForwardingWebapp.getOutputStream();
        if(incomingRequest.available()>0){
            System.out.println("Incoming Request can be forwarded");
            long bytesTransfered = incomingRequest.transferTo(outputStream);
            System.out.print("stream copied");
            socketForwardingWebapp.shutdownOutput();
            System.out.println("Bytes forwareded: "+bytesTransfered);
        }
        return webappInputStream;
    }catch(Exception exc){
        exc.printStackTrace();
    }
    return null;
}

问题分析

你的代码存在几个关键问题导致收不到响应:

  • 错误依赖available()判断请求数据:incomingRequest.available()>0仅返回当前缓冲区可读取的字节数,并非请求总长度,若请求数据未完全到达,会跳过转发(你这里服务器已收到请求,说明此场景未触发,但逻辑本身存在隐患)。
  • 未确保请求数据完全发送:调用transferTo后未手动flush输出流,部分数据可能滞留在缓冲区,导致服务器未收到完整请求。
  • HTTP Keep-Alive阻塞:HTTP/1.1默认启用Keep-Alive,服务器发送响应后不会立即关闭Socket,transferTo会一直阻塞直到输入流遇到EOF,导致客户端永远收不到响应。
  • 资源管理不完整:转发方法中创建的socketForwardingWebapp未被正确关闭,仅关闭返回的InputStream无法保证Socket被释放,影响服务器响应传输。

修复后的代码

封装资源类(用于传递Socket和InputStream)

private static class SocketInputStreamPair {
    private final Socket socket;
    private final InputStream inputStream;

    public SocketInputStreamPair(Socket socket, InputStream inputStream) {
        this.socket = socket;
        this.inputStream = inputStream;
    }

    public Socket getSocket() {
        return socket;
    }

    public InputStream getInputStream() {
        return inputStream;
    }
}

修改后的转发方法

private static SocketInputStreamPair sendHttpRequestToDestination(InputStream incomingRequest, Boolean simulate){
    try{
        Socket socketForwardingWebapp = new Socket(
            simulate ? URL_WEB_SERVER_SIMULATION : URL_WEB_SERVER, 
            simulate ? PORT_WEB_SERVER_SIMULATION : PORT_WEB_SERVER
        );
        System.out.println("HTTP-Forwarding: Created socket "+socketForwardingWebapp.getInetAddress()+":"+socketForwardingWebapp.getPort());  
        InputStream webappInputStream = socketForwardingWebapp.getInputStream();
        OutputStream outputStream = socketForwardingWebapp.getOutputStream();

        // 直接转发所有请求数据,直到输入流结束,不再依赖available()
        long bytesTransfered = incomingRequest.transferTo(outputStream);
        // 手动flush确保所有数据发送到服务器
        outputStream.flush();
        socketForwardingWebapp.shutdownOutput();
        System.out.println("Bytes forwarded: "+bytesTransfered);

        return new SocketInputStreamPair(socketForwardingWebapp, webappInputStream);
    }catch(Exception exc){
        exc.printStackTrace();
    }
    return null;
}

修改后的监听方法

private static void runHttpListener(Boolean simulate){
    try (ServerSocket server = new ServerSocket(PORT_HTTP_REQUEST)) {
        System.out.println("HTTP listener active on Port "+PORT_HTTP_REQUEST);
        boolean keepAlive = true;
        while (keepAlive) {               
            try (Socket socketReceivingProxy = server.accept()) {  
                System.out.println("HTTP-Listener: Accepted client connection");              
                InputStream proxyInputStream = socketReceivingProxy.getInputStream();
                OutputStream proxyOutputStream = socketReceivingProxy.getOutputStream();

                // 转发请求并获取服务器响应流和对应的Socket
                SocketInputStreamPair pair = sendHttpRequestToDestination(proxyInputStream, simulate);
                if (pair == null) {
                    continue;
                }

                try (Socket serverSocket = pair.getSocket();
                     InputStream serverInputStream = pair.getInputStream()) {
                    // 转发服务器响应到客户端
                    serverInputStream.transferTo(proxyOutputStream);
                    // flush确保响应发送到客户端
                    proxyOutputStream.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("HTTP listener closed");    
    } catch (Exception e) {
        e.printStackTrace();
    }
}

额外说明

如果需要支持HTTP/1.1的Keep-Alive,需解析请求和响应的Connection头:

  • 若头值为keep-alive,则保留Socket等待下一次请求
  • 若为close,则关闭Socket
    另外,对于chunked编码的响应,需正确解析分块数据,不能仅依赖InputStream的EOF。

内容的提问来源于stack exchange,提问作者Jones

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:06:32