如何让Java Socket通信被Wireshark识别为HTTP协议?
问题
我使用Java编写了Socket通信代码,通过Wireshark抓包时,工具显示该通信使用TCP协议而非HTTP协议。我是否未遵循HTTP头格式?请告知如何修改代码,使Wireshark抓包时识别为HTTP协议。
原代码
MyServer.java
import java.net.ServerSocket; import java.net.Socket; import java.io.InputStream; import java.io.DataInputStream; import java.io.OutputStream; import java.io.DataOutputStream; import java.util.Date; import java.text.SimpleDateFormat; import java.util.Locale; public class MyServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream inputStream = null; DataInputStream dataInputStream = null; OutputStream outputStream = null; DataOutputStream dataOutputStream = null; String blank = ""; try { serverSocket = new ServerSocket(9000); System.out.println("已准备好接收客户端数据"); socket = serverSocket.accept(); System.out.println("客户端连接完成"); System.out.println("socket: " + socket); inputStream = socket.getInputStream(); dataInputStream = new DataInputStream(inputStream); outputStream = socket.getOutputStream(); dataOutputStream = new DataOutputStream(outputStream); while (true) { String clientMessage = dataInputStream.readUTF(); String[] clientMessages = clientMessage.split("\\r\\n"); String slot1 = clientMessages[0]; // 示例) "GET / HTTP/1.1" String[] slot2 = slot1.split(" "); String request_method = slot2[0]; // 示例) GET String request_version = slot2[2]; // 示例) HTTP/1.1 String server_name = "Socket Server"; String strDate = ""; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); strDate = sdf.format(date); String response_data = ""; if (request_method.equals("GET") && request_version.equals("HTTP/1.1")){ System.out.printf("%s %s 200\n", request_method, request_version); response_data = String.format("%s 200 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } else if (request_method.equals("POST") && request_version.equals("HTTP/1.1")){ System.out.printf("%s %s 201", request_method, request_version); response_data = String.format("%s 201 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } else if (request_method.equals("HEAD") && request_version.equals("HTTP/1.1")){ System.out.printf("%s %s 203", request_method, request_version); response_data = String.format("%s 203 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } else if (request_method.equals("PUT") && request_version.equals("HTTP/1.1")){ System.out.printf("%s %s 202", request_method, request_version); response_data = String.format("%s 202 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } else if (request_method.equals("DELETE") && request_version.equals("HTTP/1.1")){ System.out.printf("%s %s 204", request_method, request_version); response_data = String.format("%s 204 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } else { System.out.printf("%s %s 405", request_method, request_version); response_data = String.format("%s 405 OK\nServer: %s\nDate: %s\n", request_version, server_name, strDate); } dataOutputStream.writeUTF(response_data); dataOutputStream.flush(); if (clientMessage.equals("STOP")) break; } } catch (Exception e) { e.printStackTrace(); } finally { try { if (dataOutputStream != null) dataOutputStream.close(); if (outputStream != null) outputStream.close(); if (dataInputStream != null) dataInputStream.close(); if (inputStream != null) inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } } }
MyClient.java
import java.net.Socket; import java.io.OutputStream; import java.io.DataOutputStream; import java.io.InputStream; import java.io.DataInputStream; import java.util.Scanner; public class MyClient { public static void main(String[] args) { Socket socket = null; OutputStream outputStream = null; DataOutputStream dataOutputStream = null; InputStream inputStream = null; DataInputStream dataInputStream = null; String inputMessage = ""; String outputMessage = ""; Scanner scanner = null; try{ socket = new Socket("localhost", 9000); System.out.println("服务器连接完成"); outputStream = socket.getOutputStream(); dataOutputStream = new DataOutputStream(outputStream); inputStream = socket.getInputStream(); dataInputStream = new DataInputStream(inputStream); scanner = new Scanner(System.in); while(true){ System.out.print("请输入消息:"); inputMessage = scanner.next(); if (inputMessage.equals("GET")) { outputMessage = "GET / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: Mozilla/5.0\\r\\nAccept: */*\\r\\n\\r\\n"; } else if (inputMessage.equals("POST")) { outputMessage = "POST / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: insomnia/2022.2.1\\r\\nAccept: */*\\r\\nContent-Length: 0\\r\\n\\r\\n"; } else if (inputMessage.equals("HEAD")) { outputMessage = "HEAD / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: insomnia/2022.2.1\\r\\nAccept: */*\\r\\n\\r\\n"; } else if (inputMessage.equals("PUT")) { outputMessage = "PUT / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: insomnia/2022.2.1\\r\\nAccept: */*\\r\\nContent-Length: 0\\r\\n\\r\\n"; } else if (inputMessage.equals("DELETE")) { outputMessage = "DELETE / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: insomnia/2022.2.1\\r\\nAccept: */*\\r\\n\\r\\n"; } else { outputMessage = "NotAllowed / HTTP/1.1\\r\\nHost: 127.0.0.1:9000\\r\\nUser-Agent: insomnia/2022.2.1\\r\\nAccept: */*\\r\\n\\r\\n"; } dataOutputStream.writeUTF(outputMessage); dataOutputStream.flush(); String receivedMessage = dataInputStream.readUTF(); System.out.println(receivedMessage); if (inputMessage.equals("STOP")) break; } } catch (Exception e){ e.printStackTrace(); } finally { try{ if (dataOutputStream != null) dataOutputStream.close(); if (outputStream != null) outputStream.close(); if (dataInputStream != null) dataInputStream.close(); if (inputStream != null) inputStream.close(); } catch (Exception e){ e.printStackTrace(); } } } }
问题原因与修改方案
核心问题是你使用了DataInputStream.readUTF()和DataOutputStream.writeUTF()方法,这两个方法会在传输的文本前添加2字节的长度标识,破坏了HTTP协议要求的纯文本格式,导致Wireshark无法识别为HTTP流量。
要解决这个问题,需要替换为纯文本字符流进行读写:
- 服务器端:用
BufferedReader读取HTTP请求,用PrintWriter发送HTTP响应 - 客户端:用
PrintWriter发送HTTP请求,用BufferedReader接收HTTP响应
同时注意两个关键细节:
- HTTP协议的换行必须是
\r\n,不能使用Java字符串中的转义写法\\r\\n - HTTP响应需要以空行结尾,用于分隔响应头和响应体(即使响应体为空)
修改后的代码
MyServer.java
import java.net.ServerSocket; import java.net.Socket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Date; import java.text.SimpleDateFormat; import java.util.Locale; public class MyServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; BufferedReader reader = null; PrintWriter writer = null; try { serverSocket = new ServerSocket(9000); System.out.println("已准备好接收客户端数据"); socket = serverSocket.accept(); System.out.println("客户端连接完成"); System.out.println("socket: " + socket); // 用BufferedReader读取纯文本请求 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 用PrintWriter发送纯文本响应,自动刷新缓冲区 writer = new PrintWriter(socket.getOutputStream(), true); String line; String requestLine = ""; // 读取请求的第一行(请求行),直到空行结束请求头 while ((line = reader.readLine()) != null) { if (line.isEmpty()) break; if (requestLine.isEmpty()) { requestLine = line; } } if (!requestLine.isEmpty()) { String[] slot2 = requestLine.split(" "); String request_method = slot2[0]; String request_version = slot2[2]; String server_name = "Socket Server"; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); String strDate = sdf.format(date); String responseStatus = ""; switch (request_method) { case "GET": responseStatus = "200 OK"; System.out.printf("%s %s 200\n", request_method, request_version); break; case "POST": responseStatus = "201 Created"; System.out.printf("%s %s 201\n", request_method, request_version); break; case "HEAD": responseStatus = "203 Non-Authoritative Information"; System.out.printf("%s %s 203\n", request_method, request_version); break; case "PUT": responseStatus = "202 Accepted"; System.out.printf("%s %s 202\n", request_method, request_version); break; case "DELETE": responseStatus = "204 No Content"; System.out.printf("%s %s 204\n", request_method, request_version); break; default: responseStatus = "405 Method Not Allowed"; System.out.printf("%s %s 405\n", request_method, request_version); break; } // 构造符合HTTP规范的响应,结尾加空行分隔头和体 String response = String.format("%s %s\r\nServer: %s\r\nDate: %s\r\n\r\n", request_version, responseStatus, server_name, strDate); writer.print(response); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (writer != null) writer.close(); if (reader != null) reader.close(); if (socket != null) socket.close(); if (serverSocket != null) serverSocket.close(); } catch (Exception e) { e.printStackTrace(); } } } }
MyClient.java
import java.net.Socket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Scanner; public class MyClient { public static void main(String[] args) { Socket socket = null; PrintWriter writer = null; BufferedReader reader = null; Scanner scanner = null; try { socket = new Socket("localhost", 9000); System.out.println("服务器连接完成"); // 用PrintWriter发送纯文本请求 writer = new PrintWriter(socket.getOutputStream(), true); // 用BufferedReader接收纯文本响应 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); scanner = new Scanner(System.in); while (true) { System.out.print("请输入消息:"); String inputMessage = scanner.next(); if (inputMessage.equals("STOP")) break; String outputMessage; // 直接使用\r\n作为HTTP换行符 switch (inputMessage) { case "GET": outputMessage = "GET / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: Mozilla/5.0\r\nAccept: */*\r\n\r\n"; break; case "POST": outputMessage = "POST / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: insomnia/2022.2.1\r\nAccept: */*\r\nContent-Length: 0\r\n\r\n"; break; case "HEAD": outputMessage = "HEAD / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: insomnia/2022.2.1\r\nAccept: */*\r\n\r\n"; break; case "PUT": outputMessage = "PUT / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: insomnia/2022.2.1\r\nAccept: */*\r\nContent-Length: 0\r\n\r\n"; break; case "DELETE": outputMessage = "DELETE / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: insomnia/2022.2.1\r\nAccept: */*\r\n\r\n"; break; default: outputMessage = "NotAllowed / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nUser-Agent: insomnia/2022.2.1\r\nAccept: */*\r\n\r\n"; break; } writer.print(outputMessage); writer.flush(); // 读取并打印服务器响应 System.out.println("服务器响应:"); String responseLine; while ((responseLine = reader.readLine()) != null) { if (responseLine.isEmpty()) break; System.out.println(responseLine); } System.out.println(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (writer != null) writer.close(); if (reader != null) reader.close(); if (scanner != null) scanner.close(); if (socket != null) socket.close(); } catch (Exception e) { e.printStackTrace(); } } } }
内容的提问来源于stack exchange,提问作者Issac Kim
相关产品推荐
相关产品推荐

