0
  • 作为一项任务,我只能使用 ServerSocket 和 Socket 类。它也应该是单线程的。

我在 Java 中实现了一个 HTTP 代理服务器,首先它从客户端获取请求,然后推送到服务器,然后将响应推送回客户端。

问题

问题是,我已成功获取请求,将其发送到终端服务器并获得正确的 HTTP 响应。我也可以在控制台中打印出响应但是当我将响应发送到 clientServer.outputstream 时它卡住了。Firefox(请求使用,HTTP 1.0,没有请求保持活动状态)似乎永远加载并且没有任何显示,并且 Firefox 也没有从我的程序收到响应。

我在调试时检查的内容

每次页面开始加载(FF 请求)时,总是有 2 个客户端套接字。第一个套接字包含空请求,第二个套接字包含正确的请求。我所期望的是只有一个来自 Firefox 的正确 HTTP 请求。这是一种奇怪的行为吗?

例子:

/0:0:0:0:0:0:0:1:65194
[null request]

/0:0:0:0:0:0:0:1:65195
GET http://www.microsoft.com/ HTTP/1.0
Host: www.microsoft.com
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Proxy-Connection: close
Cookie: viewkey=lightweight; WT_FPC=id=269eb0e7618962f93a81347585923074:lv=1349229942007:ss=1349229580158; WT_NVR_RU=0=technet|msdn:1=:2=; omniID=c736269c_f430_4e9b_a42a_23a0c965c60a; MUID=212A1766CFE761423CD014BDCBE76158&TUID=1; MC1=GUID=08600fba7f5c5f409e67980d8a027593&HASH=ba0f&LV=20129&V=4&LU=1347643534618; A=I&I=AxUFAAAAAADGBwAA8ezRtqBBHjk3++mP1Bwj9w!!&V=4&CS=119EQ5002j10100; msdn=L=en-US

代码

ServerSocket serverSocket;
try {
    serverSocket = new ServerSocket(60000);
    while (true) {
            clientSocket = serverSocket.accept();
            [...]
            // Extract request, and push to end-server
            // Fetch response from end-server to client, using flush() already
            // Close all input, output
            // Close all sockets
} catch {[...]}

欢迎任何帮助,谢谢!

按要求提供完整代码,我使用 PrintWriter,但在此之前使用 Byte 没有区别(不关心效率)

import java.io.*;
import java.net.*;
import java.util.*;

public class Proxy {

    static String separator = System.getProperty("line.separator");

    public static void main(String args[]) {
        //int port = Integer.parseInt(args[0]); 
        start(60000);
    }

    public static void start(int port) {
        ServerSocket serverSocket;
        try {
            serverSocket = new ServerSocket(port);
            Socket clientSocket = null;
            while (true) {
                clientSocket = serverSocket.accept();

                System.out.println(clientSocket.getRemoteSocketAddress() + "\n" + clientSocket.getLocalSocketAddress() + "\n" + clientSocket.getInetAddress());

                BufferedReader inStreamFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

                String inLine;
                Vector<String> clientRequestHeader = new Vector<String>();
                String rawRequest = "";

                while ((inLine = inStreamFromClient.readLine()) != null) {
                    if (!inLine.isEmpty()) {
                        clientRequestHeader.add(inLine);
                        rawRequest = rawRequest.concat(inLine + separator);
                    } else break;
                }

                while ((inLine = inStreamFromClient.readLine()) != null) 
                    rawRequest = rawRequest.concat(inLine + separator);

                System.out.println(rawRequest);

                if (!rawRequest.isEmpty()) {
                    handleRequest(clientSocket, clientRequestHeader, rawRequest);
                } else {
                    //clientSocket.close();
                                    // Not sure how to handle null request
                }

            }
        } catch (Exception e) {e.printStackTrace();}

    }

    public static void handleRequest(Socket clientSocket, Vector<String> clientRequestHeader, String rawRequest) {
        HTTPRequest request = new HTTPRequest(clientRequestHeader, rawRequest);
        try {
            //System.out.println(rawRequest);

            // Send request to end-server
            Socket endServerSocket = new Socket(request.getHost(), 80);

            PrintWriter outStreamToEndServer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(endServerSocket.getOutputStream())));
            BufferedReader stringReader = new BufferedReader(new StringReader(rawRequest));
            String inLine;
            while ((inLine = stringReader.readLine())!= null) {
                outStreamToEndServer.println(inLine);
            }
            outStreamToEndServer.println();
            outStreamToEndServer.flush();


            // Read response header from end-server
            String responseHeader = "";
            BufferedReader inStreamFromEndServer = new BufferedReader(new InputStreamReader(endServerSocket.getInputStream())); 
            while (!(inLine = inStreamFromEndServer.readLine()).isEmpty()) {
                responseHeader = responseHeader.concat(inLine + separator);
            }

            // Send response header to client
            PrintWriter outStreamToClient = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
            outStreamToClient.println(responseHeader);
            outStreamToClient.flush();

            // Send response body to client
            String responseBody = "";
            while ((inLine = inStreamFromEndServer.readLine()) != null) {
                responseBody = responseBody.concat(inLine + separator);
            }
            outStreamToClient.println(responseBody);
            outStreamToClient.flush();

            endServerSocket.shutdownInput();
            clientSocket.shutdownOutput();
            clientSocket.close();
            endServerSocket.close();

            //endServerSocket = null;
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

}
4

1 回答 1

0

首先,您不应该使用 PrintWriter 来传输数据,因为 HTTP 协议不是纯文本协议,主体可以包含一些原始数据,例如图像。

将您的响应传输代码替换为以下代码。

InputStream in = endServerSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1)
{
    out.write(buffer, 0, bytesRead);
}

in.close();
out.close();

第二点,你总是添加换行符

 static String separator = System.getProperty("line.separator");

这是系统特定的行分隔符。HTTP 为 HTTP 标头以及 http 标头和正文分隔定义了 ctrl 换行符,因此请更改此设置。

 static String separator = "\r\n";

通过此更改,您将获得对浏览器的响应。

最后一点,您还应该更改您的客户端请求读取代码,因为如果您想要发布一些数据,它并不总是有效。有时,这些数据将作为原始数据传输,例如文件上传。

祝你好运

于 2014-01-24T09:28:17.267 回答