Socket实现的HTTP协议查看器(GET/POST/UPLOAD)

本文通过一个简单的Socket实现的HTTP响应服务器,介绍了GET、POST和文件上传三种表单提交方式的数据格式,帮助初学者更好地理解HTTP协议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本文用于初学者熟悉Http协议的一些细节,特别是常见的GET/POST(Upload)三种表单提交方式的数据格式。
原文地址: http://www.java2000.net/p9925

代码如下
  1. package test.io;
  2. import java.net.*;
  3. import java.io.*;
  4. /**
  5.  * 一个简单的Socket实现的HTTP响应服务器。<br>
  6.  * 只用于熟悉HTTP协议的目的,可以看到浏览器发过来的数据格式。
  7.  * 
  8.  * @author 赵学庆 www.java2000.net
  9.  */
  10. public class MyWebServer {
  11.   public static void main(String[] args) {
  12.     Socket socket = null;
  13.     try {
  14.       // 创建一个监听8000端口的服务器Socket
  15.       ServerSocket s = new ServerSocket(80003);
  16.       System.out.println("MyWebServer等待来自浏览器的连接\n");
  17.       while (true) {
  18.         socket = s.accept();
  19.         System.out.println("连接已建立。端口号:" + socket.getPort());
  20.         new MyWebServerThread(socket).start();
  21.       }
  22.     } catch (IOException e) {
  23.       e.printStackTrace();
  24.     }
  25.   }
  26. }
  27. class MyWebServerThread extends Thread {
  28.   private Socket socket;
  29.   MyWebServerThread(Socket socket) {
  30.     this.socket = socket;
  31.   }
  32.   @Override
  33.   public void run() {
  34.     try {
  35.       InputStreamReader is = new InputStreamReader(socket.getInputStream());
  36.       char[] bs = new char[2048];
  37.       PrintStream out;
  38.       out = new PrintStream(socket.getOutputStream());
  39.       StringBuilder msg = new StringBuilder();
  40.       // 如果10毫秒还没有数据,则视同没有新的数据了。
  41.       // 因为有Keep-Alive的缘故,浏览器可能不主动断开连接的。
  42.       // 实际应用,会根据协议第一行是GET还是 POST确定。
  43.       socket.setSoTimeout(10);
  44.       //
  45.       // 此处读入请求数据并做相应的处理
  46.       //
  47.       int len = -1;
  48.       try {
  49.         while ((len = is.read(bs)) != -1) {
  50.           msg.append(bs, 0, len);
  51.           msg.append("\n");
  52.         }
  53.       } catch (Exception ex) {
  54.         // ex.printStackTrace();
  55.       }
  56.       // 下面是由服务器直接生成的主页内容
  57.       // 1、首先向浏览器输出响应头信息
  58.       out.println("HTTP/1.1 200 OK");
  59.       out.println("Content-Type:text/html;charset:GBK");
  60.       out.println();
  61.       // 2、输出主页信息
  62.       out
  63.           .println("<HTML><BODY>"
  64.               + "<center>"
  65.               + "<H1>HTTP协议测试服务器,当前时间:"
  66.               + new java.util.Date()
  67.               + "</h1>"
  68.               + "<form method='get'>username:<input type='text' name='username'/>password:<input type='text' name='password'/><input type='submit' value='GET测试'/></form><br/>"
  69.               + "<form method='post'>username:<input type='text' name='username'/>password:<input type='text' name='password'/><input type='submit' value='POST测试'/></form><br/>"
  70.               + "<form method='post'  enctype='multipart/form-data'>phototitle:<input type='text' name='phototitle'/>photo:<input type='file' name='photo'/><input type='submit' value='Upload测试'/></form>"
  71.               + "</center>您提交的数据如下:<pre>" + msg.toString() + "</pre></BODY></HTML>");
  72.       out.flush();
  73.       out.close();
  74.       is.close();
  75.       System.out.println("close");
  76.       // 关闭连接
  77.       socket.close();
  78.     } catch (IOException e) {
  79.       e.printStackTrace();
  80.     }
  81.   }
  82. }

GET的运行结果演示
  1. GET /?username=111&password=222 HTTP/1.1
  2. Host: 127.0.0.1:8000
  3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
  4. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  5. Accept-Language: zh-cn,zh;q=0.5
  6. Accept-Encoding: gzip,deflate
  7. Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
  8. Keep-Alive: 300
  9. Connection: keep-alive
  10. Referer: http://127.0.0.1:8000/?username=1&password=2 

普通POST的结果演示
  1. POST /?username=111&password=222 HTTP/1.1
  2. Host: 127.0.0.1:8000
  3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
  4. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  5. Accept-Language: zh-cn,zh;q=0.5
  6. Accept-Encoding: gzip,deflate
  7. Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
  8. Keep-Alive: 300
  9. Connection: keep-alive
  10. Referer: http://127.0.0.1:8000/?username=111&password=222
  11. Content-Type: application/x-www-form-urlencoded
  12. Content-Length: 25
  13. username=111&password=222

POST文件上传的结果演示
  1. POST /?username=111&password=222 HTTP/1.1
  2. Host: 127.0.0.1:8000
  3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
  4. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  5. Accept-Language: zh-cn,zh;q=0.5
  6. Accept-Encoding: gzip,deflate
  7. Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
  8. Keep-Alive: 300
  9. Connection: keep-alive
  10. Referer: http://127.0.0.1:8000/?username=111&password=222
  11. Content-Type: multipart/form-data; boundary=---------------------------23757983230932
  12. Content-Length: 1704
  13. -----------------------------23757983230932
  14. Content-Disposition: form-data; name="phototitle"
  15. 12
  16. -----------------------------23757983230932
  17. Content-Disposition: form-data; name="photo"filename="技术考核题.txt"
  18. Content-Type: text/plain
  19. 技术考核
  20. 本次是个编程题,题目选Java/语言进行回答。
  21. 问题描述:
  22. 假设你的爱好是集邮。目前总共有N种不同的邮票,编号为从0到N-1.每种邮票的价钱被定义在数组int[] prices中(序号从0开始,数组的第i个元素表示第i种邮票的价格)。
  23. 你的目标是收集尽可能多种的邮票。你当前拥有的邮票存储在int[] have这个数组中。初始时,你没有钱,但是你可以卖掉已有邮票来买不同的邮票。返回你能收集到的不同种邮票的最大数量。
  24. 定义:
  25. class: PostmarksCollection
  26. method: numberOfDistinctPostmarks
  27. Parameters: int[], int[]
  28. Returns: int
  29. Method signature: public int numberOfDistinctPostmarks(int[] prices, int[] have)
  30. //为了进行测试,必须保证是public
  31. 约束:
  32. 1) N从1到50
  33. 2)prices中的元素数量刚好是N个元素
  34. 3)prices中的元素的值,从1到1,000,000
  35. 4)have中的元素数量是0个到N个
  36. 5)have中的每个元素是不同的
  37. 6)have中的每个元素的值是从0到N-1.
  38. 例如:
  39. 1)
  40. {13,10,14,20}
  41. {3,0,2,1}
  42. Returns: 4
  43. 你已经拥有所有种类的邮票。
  44. 2)
  45. {7,5,9,7}
  46. {}
  47. Returns: 0
  48. 你开始没有任何邮票,所以你也不能组任何事情。
  49. 3)
  50. {4,13,9,1,5}
  51. {1,3,2}
  52. Returns: 4
  53. 卖掉邮票2,买入邮票0和4,(邮票2的价钱是9,邮票0的价钱是4,邮票4的价钱是5,卖掉2刚好买入0和4)
  54. 4)
  55. {16,32,13,2,17,10,8,8,20,17}
  56. {7,0,4,1,6,8}
  57. Returns: 8
  58. --------------------------------------------------------------------------------
  59. 使用Java请在此处答题(请使用JDK5.0编译,编译不能通过者不计算分数)
  60. -----------------------------23757983230932--

总结:
  初学者要提高自己的编程水平,熟悉一下常用的协议是必须的。

<think>好的,用户现在遇到了ESP01返回404 NotFound的错误,需要帮助解决。首先,我得回忆一下404错误的基本原因。404通常意味着客户端能连接到服务器,但请求的资源不存在。那可能的原因包括URL路径错误、服务器端未正确配置路由、文件缺失或者权限问题等。接下来,结合用户提供的引用内容,引用[3]提到了ESPAsyncWebServer库的使用,这可能涉及到服务器端路由的设置。如果用户在使用这个库时没有正确注册处理特定路径的处理器,就会导致404错误。比如,如果客户端请求的是“/data”,但服务器只处理了“/”路径,这时候就会返回404。另外,引用[2]中提到了访问ESP8266的IP地址时,需要正确使用路径,比如点击Write toSPIFFS和Read fromSPIFFS链接。如果用户尝试访问的路径在服务器代码中没有定义,或者文件没有正确上传到SPIFFS中,同样会导致404错误。比如,用户可能上传了文件到错误的位置,或者文件名拼写错误。然后,考虑硬件或网络问题。虽然404更多是服务器端的响应,但有时候网络问题也可能导致请求被错误处理。比如,引用[1]中的Socket错误,如KErrHostUnreach,但用户的问题已经能连接到服务器,所以可能排除网络不可达的情况,但需要确认ESP01确实连接到了正确的服务器IP和端口。还需要检查固件或库的版本问题。引用[4]提到ESP8266的PWM范围在不同版本中的差异,可能类似的,某些库的版本更新可能导致API变化,如果用户使用的库版本较旧,可能路由注册方式不同,从而引发404错误。另外,用户是否在代码中正确设置了服务器路由?比如,在AsyncWebServer中,是否有类似server.on("/path", handler)的语句,且路径与客户端请求的完全匹配?比如,客户端请求的是“/index.html”,但服务器只处理了“/index”就会导致404。 还有,SPIFFS文件系统中是否存在请求的文件?如果用户希望通过服务器发送静态文件,比如HTML或CSS文件,这些文件需要预先上传到ESP01的SPIFFS中,并且路径正确。例如,如果文件存放在SPIFFS的“/www”目录下,但服务器配置的路径是“/”,则无法找到文件。另外,权限问题也可能导致404,虽然较少见,但需要确认文件是否有读取权限。比如,SPIFFS中的文件是否设置了正确的访问权限。还需要考虑客户端请求的HTTP方法是否正确。比如,服务器端可能只处理GET请求,但客户端发送了POST请求,导致404。不过这种情况可能返回其他错误,但也不排除。最后,用户可能需要检查客户端发送的具体请求,比如使用抓包工具或服务器日志来查看接收到的请求路径,从而确定是否是客户端的问题,比如拼写错误或多余的斜杠。总结下来,可能的原因包括:URL路径错误、服务器路由未配置、SPIFFS文件缺失、库版本问题、权限问题等。解决方法需要逐步排查这些可能性,检查代码中的路由设置,确认文件上传正确,验证客户端请求的URL是否正确,以及检查网络连接是否稳定。</think>### ESP01 404 Not Found 错误原因及解决方法 #### 一、错误原因分析 1. **URL路径错误** 客户端请求的路径与服务器端定义的路由不匹配,例如请求`/data`但服务器仅注册了`/`路径[^3]。 2. **SPIFFS文件缺失** 若通过SPIFFS提供静态文件(如HTML/CSS),但文件未正确上传到ESP01的存储系统中,会导致404错误[^2]。 3. **服务器路由未配置** 使用`ESPAsyncWebServer`时未正确绑定处理器函数,例如缺少`server.on("/path", handler)`代码段[^3]。 4. **网络配置问题** 客户端与ESP01的IP地址或端口不匹配,例如客户端访问`192.168.1.100:80`但ESP01实际运行在`192.168.1.101:8080`。 5. **固件/库版本兼容性** 旧版库可能对路由处理方式不同(如路径大小写敏感性问题),需检查库版本与代码兼容性[^4]。 --- #### 二、解决方法步骤 1. **验证URL路径** - 确保客户端请求的路径与服务器代码中定义的完全一致(包括大小写和斜杠)。 - 示例代码检查: ```cpp server.on("/api", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/plain", "OK"); }); ``` 2. **检查SPIFFS文件上传** - 使用Arduino IDE的`ESP8266 Sketch Data Upload`工具上传文件到SPIFFS。 - 确认文件路径与代码中设置的静态资源目录一致,例如: ```cpp server.serveStatic("/", SPIFFS, "/www/"); ``` 3. **服务器路由调试** - 添加默认404处理器以捕获未定义路径的请求: ```cpp server.onNotFound([](AsyncWebServerRequest *request){ request->send(404, "text/plain", "Path not found"); }); ``` 4. **网络连接验证** - 通过串口监视器打印ESP01的IP地址和端口号: ```cpp Serial.print("HTTP Server started on: http://"); Serial.println(WiFi.localIP()); ``` 5. **库版本更新** - 在Arduino库管理中升级`ESPAsyncWebServer`和`AsyncTCP`至最新版本。 --- #### 三、常见问题案例 - **案例1:路径多写斜杠** 请求`http://192.168.1.100//index`可能导致404,需统一路径格式。 - **案例2:SPIFFS文件未更新** 修改HTML文件后未重新上传,导致客户端请求旧文件[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值