通过Java中的Socket处理POST请求

我正在尝试使用Socket处理Java中的简单POST请求。 我可以收到请求标头并回答请求没有任何问题,但我当然无法得到请求的正文。

我在某处读到了我需要打开第二个InputStream来实现这个目标,但这对我来说并没有多大意义。 您有关于如何获取请求正文的任何​​提示吗?

这是我基本上用来获取标题:

BufferedReader in = new BufferedReader(new InputStreamReader( clientSocket.getInputStream())); char[] inputBuffer = new char[INPUT_BUFFER_LENGTH]; int inputMessageLength = in.read(inputBuffer, 0, INPUT_BUFFER_LENGTH); String inputMessage = new String(inputBuffer, 0, inputMessageLength); 

所以,我得到的信息是这样的:

 POST / HTTP/1.1 User-Agent: Java/1.8.0_45 Host: localhost:5555 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 

但我无法获取POST请求的参数。

编辑:

所以事实certificate我只有INPUT_BUFFER_LENGTH足够高(我知道,对我感到羞耻)。 因此,当我工作时,我将我的ServerSocket更改为SSLServerSocket并再次尝试从Java发送带有HttpsUrlConnection的请求,现在我又遇到了同样的问题(已经检查过缓冲区),得到这样的结果:

 POST / HTTP/1.1 User-Agent: Java/1.8.0_45 Host: localhost:5555 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Content-type: application/x-www-form-urlencoded Content-Length: 128 *Missing Body* 

原来我只是在用我的Java客户端发送请求时才得到这个 – 来自Chrome等的发送请求工作正常 – 所以我认为我的代码中出了问题。 这是我用来发送请求的内容:

 System.setProperty("javax.net.ssl.trustStore", ...); System.setProperty("javax.net.ssl.trustStorePassword", ...); SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory .getDefault(); String url = "https://..."; URL obj = new URL(url); HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); con.setRequestMethod("POST"); con.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream()); writer.write(*Some String*); writer.flush(); writer.close(); 

关于我的代码可能出错的任何提示?

您显示的代码不是读取HTTP请求的正确方法。

首先,Java有自己的HttpServerHttpsServer类。 你应该考虑使用它们。

否则,您必须手动实施HTTP协议。 您需要逐行读取输入,直到到达指示请求标头结束的空行,然后查看您已阅读的标头,特别是Transfer-EncodingContent-Length标头,以了解如何根据RFC 2616第4.4节读取请求的剩余字节:

4.4消息长度

消息的传输长度是消息中出现的消息体的长度; 也就是说,在应用任何转移编码之后。 当消息包含在消息中时,该主体的传输长度由以下之一确定(按优先顺序排列):

  1. 任何“绝不”包含消息体的响应消息(例如1xx,204和304响应以及对HEAD请求的任何响应)始终由头字段之后的第一个空行终止,而不管实体是什么 – 邮件中存在标题字段。

  2. 如果存在Transfer-Encoding头字段(第14.41节)并且具有除“identity”之外的任何值,则使用“chunked”传输编码(第3.6节)定义传输长度,除非消息终止通过关闭连接。

  3. 如果存在Content-Length头字段(第14.13节),则其在OCTET中的十进制值表示实体长度和传输长度。 如果这两个长度不同(即,如果存在Transfer-Encoding头字段),则不得发送Content-Length头字段。 如果收到带有Transfer-Encoding标头字段和Content-Length标头字段的消息,则必须忽略后者。

  4. 如果消息使用媒体类型“multipart / byteranges”,并且未指定ransfer-length,则此自我修饰媒体类型定义传输长度。 除非发件人知道收件人可以自行处理,否则不得使用此媒体类型UST; 在具有来自1.1客户端的多个字节范围说明符的Range头的请求中的存在意味着lient可以解析多部分/字节范围响应。

    范围标头可能由1.0代理转发,该代理不理解multipart / byteranges; 在这种情况下,服务器必须使用本节第1,3或5项中定义的方法来分隔消息。

  5. 由服务器关闭连接。 (关闭连接不能用于指示请求正文的结束,因为这样就不会让服务器发回响应。)

    为了与HTTP / 1.0应用程序兼容,包含消息体的HTTP / 1.1请求必须包含有效的Content-Length头字段,除非已知服务器符合HTTP / 1.1。 如果请求包含消息体并且未给出Content-Length,则服务器应该响应400(错误请求),如果它无法确定消息的长度,或者如果它希望坚持,则应回复411(需要长度)收到有效的内容长度。

    接收实体的所有HTTP / 1.1应用程序必须接受“分块”传输编码(第3.6节),从而允许在无法事先确定消息长度时将此机制用于消息。

    消息不得包含Content-Length头字段和非身份传输编码。 如果消息确实包含非身份传输编码,则必须忽略Content-Length。

    当在允许消息体的消息中给出Content-Length时,其字段值必须与消息体中的OCTET数完全匹配。 HTTP / 1.1用户代理必须在收到并检测到无效长度时通知用户。

尝试更像这样的东西(半伪代码):

 String readLine(BufferedInputStream in) { // HTTP carries both textual and binary elements. // Not using BufferedReader.readLine() so it does // not "steal" bytes from BufferedInputStream... // HTTP itself only allows 7bit ASCII characters // in headers, but some header values may be // further encoded using RFC 2231 or 5987 to // carry Unicode characters ... InputStreamReader r = new InputStreamReader(in, StandardCharsets.US_ASCII); StringBuilder sb = new StringBuilder(); char c; while ((c = r.read()) >= 0) { if (c == '\n') break; if (c == '\r') { c = r.read(); if ((c < 0) || (c == '\n')) break; sb.append('\r'); } sb.append(c); } return sb.toString(); } ... BufferedInputStream in = new BufferedInputStream(clientSocket.getInputStream()); String request = readLine(in); // extract method, resource, and version... String line; do { line = readLine(in); if (line.isEmpty()) break; // store line in headers list... } while (true); // parse headers list... if (request method has a message-body) // POST, etc { if ((request version >= 1.1) && (Transfer-Encoding header is present) && (Transfer-Encoding != "identity")) { // read chunks... do { line = readLine(in); // read chunk header int size = extract value from line; if (size == 0) break; // use in.read() to read the specified // number of bytes into message-body... readLine(in); // skip trailing line break } while (true); // read trailing headers... line = readLine(in); while (!line.isEmpty()) { // store line in headers list, updating // any existing header as needed... } // parse headers list again ... } else if (Content-Length header is present) { // use in.read() to read the specified // number of bytes into message-body... } else if (Content-Type is "multipart/...") { // use readLine(in) and in.read() as needed // to read/parse/decode MIME encoded data into // message-body until terminating MIME boundary // is reached... } else { // fail the request... } } // process request and message-body as needed..