Java Socket现实简单的HTTP服务


Java Socket 现实简单的 HTTP 服务 一个简单的用 Java Socket 编写的 HTTP 服务器应用, 演示了请求和应答的协 议通信内容以及给客户端返回 HTML 文本和二进制数据文件(一个图片), 同时 展示了 404, 200 等状态码. 首先运行这个程序,然后打开 Web 浏览器,键入 http://localhost,则这个程序能 够显示出浏览器发送了那些信息并且向浏览器返回一个网页和一副图片, 并测试 同浏览器对话. 当浏览器看到 HTML 中带有图片地址时, 则会发出第二次连接 来请求图片等资源. 这个例子可以帮您理解 Java 的 HTTP 服务器软件是基于 J2SE 的 Socket 等软件编写的概念, 并熟悉 HTTP 协议. 相反的用 Telnet 连接 到已有的服务器则可以帮忙理解浏览器的运行过程和服务器端的返回内容. 当用户在 Web 浏览器地址栏中输入一个带有 http://前缀的 URL 并按下 Enter 后,或者在 Web 页面中某个以 http://开头的超链接上单击鼠标,HTTP 事务处理 的第一个阶段--建立连接阶段就开始了.HTTP 的默认端口是 80. 随着连接的建 立,HTTP 就进入了客户向服务器发送请求的阶段.客户向服务器发送的请求是一 个有特定格式的 ASCII 消息,其语法规则为: < Method > < URL > < HTTP Version > <\r\n> {
: <\r\n>} <\r\n> { Entity Body } 请求消息的顶端是请求行,用于指定方法,URL 和 HTTP 协议的版本,请求行的最 后是回车换行.方法 GET,POST,HEAD,PUT,DELETE 等. 在请求行之后是若干个 报头(Header)行.每个报头行都是由一个报头和一个取值构成的二元对,报头和 取值之间以":"分隔;报头行的最后是回车换行.常见的报头有 Accept(指定 MIME 媒体类型),Accept_Charset(响应消息的编码方式),Accept_Encoding(响应消息 的字符集),User_Agent(用户的浏览器信息)等. 在请求消息的报头行之后是一个 回车换行,表明请求消息的报头部分结束.在这个之后是请求消息的消息实体 (Entity Body).具体的例子参看 httpRequest.txt. Web 服务器在收到客户请求并作出处理之后,要向客户发送应答消息.与请求消 息一样,应答消息的语法规则为: < HTTP Version> []<\r\n> {
: <\r\n> } <\r\n> { Entity Body } 应答消息的第一行为状态行,其中包括了 HTTP 版本号,状态码和对状态码进行简 短解释的消息;状态行的最后是回车换行.状态码由 3 位数字组成,有 5 类: 参看:HTTP 应答码及其意义 1XX 保留 2XX 表示成功 3XX 表示 URL 已经被移走 4XX 表示客户错误 5XX 表示服务器错误 例如:415,表示不支持改媒体类型;503,表示服务器不能访问.最常见的是 200,表 示成功.常见的报头有:Last_Modified(最后修改时间),Content_Type(消息内容 的 MIME 类型),Content_Length(内容长度)等. 在报头行之后也是一个回车换行,用以表示应答消息的报头部分的结束,以及应答 消息实体的开始. 下面是一个应答消息的例子: HTTP/1.0 200 OK Date: Moday,07-Apr-97 21:13:02 GMT Server:NCSA/1.1 MIME_Version:1.0 Content_Type:text/html Last_Modified:Thu Dec 5 09:28:01 1996 Coentent_Length:3107 </HTML> 在用 Java 语言实现 HTTP 服务器时,首先启动一个 java.net.ServerSocket 在提 供服务的端口上监听连接.向客户返回文本时,可以用 PrintWriter,但是如果返回 二进制数据,则必须使用 OutputStream.write(byte[])方法,返回的应答消息字符 串可以使用 String.getBytes()方法转换为字节数组返回,或者使用 PrintStream 的 print()方法写入文本,用 write(byte[])方法写入二进制数据. 源程序来自 http://blog.csdn.net/myeclipse_java,本程序是在原有 基础上进行了一些调整,源程序在 POST 请求时会阻塞,本程序解决 了此问题,另外,本程序在原有的基础上增加了文件上传与下载模拟 功能: Java 代码 import java.io.File; import java.io.FileInputStream; 1. import java.io.IOException; 2. import java.io.InputStream; 3. import java.io.PrintStream; 4. import java.io.PrintWriter; 5. import java.net.ServerSocket; 6. import java.net.Socket; 7. import java.net.URLDecoder; 8. import java.util.ArrayList; 9. import java.util.StringTokenizer; 10. 11./** 12. * 13. * @author 刘长炯 14. * modi by jzj 15. * 16. */ 17.public class SimpleHttpServer implements Runnable { 18. 19. ServerSocket serverSocket;//服务器 Socket 20. 21. public static int PORT = 80;//标准 HTTP 端口 22. 23. public String encoding = "GBK"; 24. 25. public SimpleHttpServer() { 26. try { 27. serverSocket = new ServerSocket(PORT); 28. } catch (Exception e) { 29. e.printStackTrace(); 30. System.exit(1); 31. } new Thread(this).start(); System.out.println("HTTP 服务器正在运行,端口:" + PORT); 32. } 33. 34. public void run() { 35. while (true) { 36. try { 37. Socket client = serverSocket.accept();//客户机(这里是 IE 等 浏览器)已经连接到当前服务器 38. if (client != null) { 39. System.out.println("连接到服务器的用户:" + client); 40. try { 41. // 第一阶段: 打开输入流 42. InputStream is = client.getInputStream(); 43. 44. System.out.println("客户端发送的请求信息: >>>>>>>> >>>>>>>>>>>>>>>>>"); 45. // 读取第一行, 请求地址 46. String line = readLine(is, 0); 47. //打印请求行 48. System.out.print(line); 49. // < Method > < URL > < HTTP Version > <\r\n> 取 的是 URL 部分 50. String resource = line.substring(line.indexOf('/'), line 51. .lastIndexOf('/') - 5); 52. //获得请求的资源的地址 53. resource = URLDecoder.decode(resource, encoding);// 反编码 URL 地址 54. String method = new StringTokenizer(line).nextElemen t() 55. .toString();// 获取请求方法, GET 或者 POST 56. int contentLength = 0;//如果为 POST 方法,则会有消息 体长度 57. 58. // 读取所有浏览器发送过来的请求参数头部信息 59. do { 60. line = readLine(is, 0); 61. //如果有 Content-Length 消息头时取出 62. if (line.startsWith("Content-Length")) { 63. contentLength = Integer.parseInt(line.split(":")[1] 64. .trim()); 65. } 66. //打印请求部信息 67. System.out.print(line); 68. //如果遇到了一个单独的回车换行,则表示请求头结束 69. } while (!line.equals("\r\n")); 70. //如果是 POST 请求,则有请求体 71. if ("POST".equalsIgnoreCase(method)) { 72. //注,这里只是简单的处理表单提交的参数,而对于上 传文件这里是不能这样处理的, 73. //因为上传的文件时消息体不只是一行,会有多行消息 体 74. System.out.print(readLine(is, contentLength)); 75. System.out.println(); 76. } 77. 78. System.out.println("客户端发送的请求信息结束 <<<<< <<<<<<<<<<<<<<<<<<<<<"); 79. System.out.println("用户请求的资源是:" + resource); 80. System.out.println("请求的类型是: " + method); 81. System.out.println(); 82. 83. //如果是下载文件 84. if (resource.startsWith("/download")) { 85. fileDownload("test.txt", client); 86. closeSocket(client); 87. continue; 88. } 89. 90. // GIF 图片就读取一个真实的图片数据并返回给客户端 91. if (resource.endsWith(".gif")) { 92. imgDownload("test.gif", client); 93. closeSocket(client); 94. continue; 95. } 96. 97. // 请求 JPG 格式就报错 404 98. if (resource.endsWith(".jpg")) { 99. PrintWriter out = new PrintWriter(client.getOutputS tream(), 100. true); 101. out.println("HTTP/1.0 404 Not found");//返回应 答消息,并结束应答 102. out.println();// 根据 HTTP 协议, 空行将结束头信息 103. out.close(); 104. closeSocket(client); 105. continue; 106. } else { 107. // 用 writer 对客户端 socket 输出一段 HTML 代码 108. PrintWriter out = new PrintWriter(client.getOut putStream(), 109. true); 110. out.println("HTTP/1.0 200 OK");//返回应答消息, 并结束应答 111. out.println("Content-Type:text/html;charset=" + encoding); 112. out.println();// 根据 HTTP 协议, 空行将结束头信息 113. 114. out.println("<h1> Hello Http Server</h1>"); 115. out.println("你好, 这是一个 Java HTTP 服务器 de mo 应用.<br>"); 116. out.println("您请求的路径是: " + resource + "<br >"); 117. out.println("你请求的页面含有图片:<img src='tes t.gif'><br>" 118. + "<a href='test.gif'>手动点击打开 test.gif 图片文件.</a>"); 119. out.println("<br>服务器不支持 jpg 格式图片,所 以显示 XX:" 120. + "<img src='test.jpg'><br><a href='test. jpg'>" 121. + "手动点击打开 test.jpg,会跳转另一页面, 并且服务返回为 404 错误</a><br>"); 122. out 123. .println("<form method=post action='/pat h?qryParm=POST URL 查询参数' > POST 表单 " 124. + "<input name=username value='用 户'> " 125. + "<input name=submit type=submi t value=submit></form>"); 126. out 127. .println("<form method=get action='/path ?qryParm=GET URL 查询参数' > GET 表单 " 128. + "<input name=username value='用 户'> " 129. + "<input name=submit type=submi t value=submit></form>"); 130. 131. out 132. .println("<form method=post action='/pat h?qryParm=POST URL 查询参数'" 133. + " enctype='multipart/form-data' >" 134. + "文件上传  <input type='file' name=file1 ><br>" 135. + "    &nbs p;     " 136. + "<input type='file' name=file2 ><b r>" 137. + "    &nbs p;     " 138. + "<input name=username value='用 户'> " 139. + "<input name=submit type=submi t value=submit></form>"); 140. out.println("<a href='/download'>点击此处模拟 文件下载</a>"); 141. 142. out.close(); 143. 144. closeSocket(client); 145. } 146. } catch (Exception e) { 147. System.out.println("HTTP 服务器错误:" + e.getLoca lizedMessage()); 148. } 149. } 150. //System.out.println(client+"连接到 HTTP 服务器");//如 果加入这一句,服务器响应速度会很慢 151. } catch (Exception e) { 152. System.out.println("HTTP 服务器错误:" + e.getLocalized Message()); 153. } 154. } 155. } 156. 157. /* 158. * 这里我们自己模拟读取一行,因为如果使用 API 中的 BufferedReader 时,它是读取到一个回车换行后 159. * 才返回,否则如果没有读取,则一直阻塞,这就导致如果为 POST 请求时,表单中的元素会以消息体传送, 160. * 这时,消息体最末按标准是没有回车换行的,如果此时还使用 BufferedReader 来读时,则 POST 提交 161. * 时会阻塞。如果是 POST 提交时我们按照消息体的长度 Content- Length 来截取消息体,这样就不会阻塞 162. */ 163. private String readLine(InputStream is, int contentLe) throws I OException { 164. ArrayList lineByteList = new ArrayList(); 165. byte readByte; 166. int total = 0; 167. if (contentLe != 0) { 168. do { 169. readByte = (byte) is.read(); 170. lineByteList.add(Byte.valueOf(readByte)); 171. total++; 172. } while (total < contentLe);//消息体读还未读完 173. } else { 174. do { 175. readByte = (byte) is.read(); 176. lineByteList.add(Byte.valueOf(readByte)); 177. } while (readByte != 10); 178. } 179. 180. byte[] tmpByteArr = new byte[lineByteList.size()]; 181. for (int i = 0; i < lineByteList.size(); i++) { 182. tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue(); 183. } 184. lineByteList.clear(); 185. 186. String tmpStr = new String(tmpByteArr, encoding); 187. /* http 请求的 header 中有一个 Referer 属性,这个属性的意思 就是如果当前请求是从别的页面链接过 188. * 来的,那个属性就是那个页面的 url,如果请求的 url 是直接从 浏览器地址栏输入的就没有这个值。得 189. * 到这个值可以实现很多有用的功能,例如防盗链,记录访问来 源以及记住刚才访问的链接等。另外,浏 190. * 览器发送这个 Referer 链接时好像固定用 UTF-8 编码的,所以 在 GBK 下出现乱码,我们在这里纠正一下 191. */ 192. if (tmpStr.startsWith("Referer")) {//如果有 Referer 头时,使用 UTF-8 编码 193. tmpStr = new String(tmpByteArr, "UTF-8"); 194. } 195. return tmpStr; 196. } 197. 198. /** 199. * 关闭客户端 socket 并打印一条调试信息. 200. * @param socket 客户端 socket. 201. */ 202. void closeSocket(Socket socket) { 203. try { 204. socket.close(); 205. } catch (IOException ex) { 206. ex.printStackTrace(); 207. } 208. System.out.println(socket + "离开了 HTTP 服务器"); 209. } 210. 211. /** 212. * 读取一个图像文件的内容并返回给浏览器端. 213. * @param fileName 文件名 214. * @param socket 客户端 socket. 215. */ 216. void imgDownload(String fileName, Socket socket) { 217. 218. try { 219. PrintStream out = new PrintStream(socket.getOutputStre am(), true); 220. File fileToSend = new File(fileName); 221. if (fileToSend.exists() && !fileToSend.isDirectory()) { 222. out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应 答 223. out.println("Content-Type: application/octet-stream"); 224. out.println("Content-Length: " + fileToSend.length());// 返回内容字节数 225. out.println();// 根据 HTTP 协议, 空行将结束头信息 226. 227. FileInputStream fis = new FileInputStream(fileToSend); 228. byte data[] = new byte[fis.available()]; 229. fis.read(data); 230. out.write(data); 231. //文件下载完后关闭 socket 流,但 socket 还没有关闭 232. out.close(); 233. fis.close(); 234. } 235. } catch (Exception e) { 236. e.printStackTrace(); 237. } 238. } 239. 240. /** 241. * 读取一个文件的内容并返回给浏览器端. 242. * @param fileName 文件名 243. * @param socket 客户端 socket. 244. */ 245. void fileDownload(String fileName, Socket socket) { 246. try { 247. PrintStream out = new PrintStream(socket.getOutputStre am(), true); 248. File fileToSend = new File(fileName); 249. if (fileToSend.exists() && !fileToSend.isDirectory()) { 250. out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应 答 251. out.println("Content-Type: application/octet- stream;charset=" + encoding); 252. 253. /* Content-Disposition 不是标准参数,查看一下 HTTP/1.1 的规范文档,对于这个参数的解释大意如下: 254. * Content-Disposition 参数本来是为了在客户端另存文件 时提供一个建议的文件名,但是考虑到安全的原因, 255. * 就从规范中去掉了这个参数。但是由于很多浏览器已经能 够支持这个参数,所以只是在规范文档中列出,但是要 256. * 注意这个不是 HTTP/1.1 的标准参数。其值为 “attachment”,那么无论这个文件是何类型,浏览器都会提示我 257. * 们下载此文件,因为此时它认为后面的消息体是一个“附 件”,不需要由浏览器来处理了。 258. */ 259. out.println("Content-Disposition: attachment;filename =测试下载文件.txt"); 260. // out.println("Accept-Ranges: bytes"); 261. out.println("Content-Length: " + fileToSend.length());// 返回内容字节数 262. out.println();// 根据 HTTP 协议, 空行将结束头信息 263. 264. FileInputStream fis = new FileInputStream(fileToSend); 265. byte[] tmpByteArr = new byte[10];//这里为了测试看下载 进度条,所以设置小点 266. while (fis.available() > 0) { 267. int readCount = fis.read(tmpByteArr); 268. out.write(tmpByteArr, 0, readCount); 269. } 270. 271. //文件下载完后关闭 socket 流 272. out.close(); 273. fis.close(); 274. } 275. } catch (Exception e) { 276. e.printStackTrace(); 277. } 278. } 279. 280. public static void main(String[] args) { 281. PORT = 8080; 282. new SimpleHttpServer(); 283. } 284. } 下面是我测试的过程: 在地址栏输入 http://localhost:8080/后显示如下页面: 点击“手动点击打开 test.gif 图片文件. ”连接显示以下图片: 再点击“手动点击打开 test.jpg,会跳转另一页面,并且服务返回为 404 错 误”显示以下错误页面: 再点击“点击此处模拟文件下载”显示文件下载框: 最后是服务器运行日志,仅供参考: HTTP 服务器正在运行,端口:8080 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56155,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET / HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: zh-cn User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/ 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56155,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56156,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: */* Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.gif 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56156,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56157,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: */* Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.jpg 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56157,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56158,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.gif 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56158,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56159,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.jpg 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56159,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56160,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> POST /path?qryParm=POST%20URL 查询参数 HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate Host: localhost:8080 Content-Length: 35 Connection: Keep-Alive Cache-Control: no-cache username=%D3%C3%BB%A7&submit=submit 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/path?qryParm=POST URL 查询参数 请求的类型是: POST Socket[addr=/0:0:0:0:0:0:0:1,port=56160,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56161,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.gif 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56161,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56162,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.jpg 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56162,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56163,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /path?username=%D3%C3%BB%A7&submit=submit HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/path?username=用户&submit=submit 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56163,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56165,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?username=%D3%C3%BB %A7&submit=submit Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.jpg 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56165,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56164,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?username=%D3%C3%BB %A7&submit=submit Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.gif 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56164,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56168,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> POST /path?qryParm=POST%20URL 查询参数 HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/path?username=%D3%C3%BB %A7&submit=submit Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Content-Type: multipart/form-data; boundary=--------------------------- 7d91c380444 Accept-Encoding: gzip, deflate Host: localhost:8080 Content-Length: 591 Connection: Keep-Alive Cache-Control: no-cache -----------------------------7d91c380444 Content-Disposition: form-data; name="file1"; filename="file1.txt" Content-Type: text/plain 123 -----------------------------7d91c380444 Content-Disposition: form-data; name="file2"; filename="file2.txt" Content-Type: text/plain 这是第二个测试文件的内容: 中 a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./ -----------------------------7d91c380444 Content-Disposition: form-data; name="username" 用户 -----------------------------7d91c380444 Content-Disposition: form-data; name="submit" submit -----------------------------7d91c380444-- 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/path?qryParm=POST URL 查询参数 请求的类型是: POST Socket[addr=/0:0:0:0:0:0:0:1,port=56168,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56169,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.gif 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56169,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56170,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/test.jpg 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56170,localport=8080]离开了 HTTP 服 务器 连接到服务器的用 户:Socket[addr=/0:0:0:0:0:0:0:1,port=56171,localport=8080] 客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>> GET /download HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/path?qryParm=POST URL 查询参数 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<< 用户请求的资源是:/download 请求的类型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56171,localport=8080]离开了 HTTP 服 务器 import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.URLDecoder; import java.util.ArrayList; import java.util.StringTokenizer; public class SimpleHttpServer implements Runnable { ServerSocket serverSocket;//服务器 Socket public static int PORT = 0;//标准 HTTP 端口 public String encoding = "GBK"; public SimpleHttpServer() { try { serverSocket = new ServerSocket(PORT); } catch (Exception e) { e.printStackTrace(); System.exit(1); } new Thread(this).start(); System.out.println("HTTP 服务器正在运行,端口:" + PORT); } public void run() { while (true) { try { Socket client = serverSocket.accept();//客户机(这里是 IE 等浏览器)已经 连接到当前服务器 if (client != null) { System.out.println("连接到服务器的用户:" + client); try { // 第一阶段: 打开输入流 InputStream is = client.getInputStream(); System.out.println("客户端发送的请求信息: >>>>>>>>>>>>>> >>>>>>>>>>>"); // 读取第一行, 请求地址 String line = readLine(is, 0); //打印请求行 System.out.print(line); // < Method > < URL > < HTTP Version > <\r\n> 取的是 URL 部分 String resource = line.substring(line.indexOf('/'), line .lastIndexOf('/') - 5); //获得请求的资源的地址 resource = URLDecoder.decode(resource, encoding);//反编码 URL 地址 String method = new StringTokenizer(line).nextElement() .toString();// 获取请求方法, GET 或者 POST int contentLength = 0;//如果为 POST 方法,则会有消息体长度 // 读取所有浏览器发送过来的请求参数头部信息 do { line = readLine(is, 0); //如果有 Content-Length 消息头时取出 if (line.startsWith("Content-Length")) { contentLength = Integer.parseInt(line.split(":")[1] .trim()); } //打印请求部信息 System.out.print(line); //如果遇到了一个单独的回车换行,则表示请求头结束 } while (!line.equals("\r\n")); //如果是 POST 请求,则有请求体 if ("POST".equalsIgnoreCase(method)) { //注,这里只是简单的处理表单提交的参数,而对于上传文件这里 是不能这样处理的, //因为上传的文件时消息体不只是一行,会有多行消息体 System.out.print(readLine(is, contentLength)); System.out.println(); } System.out.println("客户端发送的请求信息结束 <<<<<<<<<<< <<<<<<<<<<<<<<<"); System.out.println("用户请求的资源是:" + resource); System.out.println("请求的类型是: " + method); System.out.println(); //如果是下载文件 if (resource.startsWith("/download")) { fileDownload("test.txt", client); closeSocket(client); continue; } // GIF 图片就读取一个真实的图片数据并返回给客户端 if (resource.endsWith(".gif")) { imgDownload("test.gif", client); closeSocket(client); continue; } // 请求 JPG 格式就报错 404 if (resource.endsWith(".jpg")) { PrintWriter out = new PrintWriter(client.getOutputStream(), true); out.println("HTTP/1.0 404 Not found");//返回应答消息,并结束 应答 out.println();// 根据 HTTP 协议, 空行将结束头信息 out.close(); closeSocket(client); continue; } else { // 用 writer 对客户端 socket 输出一段 HTML 代码 PrintWriter out = new PrintWriter(client.getOutputStream(), true); out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应答 out.println("Content-Type:text/html;charset=" + encoding); out.println();// 根据 HTTP 协议, 空行将结束头信息 out.println("<h1> Hello Http Server</h1>"); out.println(" 你好, 这是一个 Java HTTP 服务器 demo 应 用.<br>"); out.println("您请求的路径是: " + resource + "<br>"); out.println("你请求的页面含有图片:<img src='test.gif'><br>" + "<a href='test.gif'> 手动点击打开 test.gif 图片文 件.</a>"); out.println("<br>服务器不支持 jpg 格式图片,所以显示 XX:" + "<img src='test.jpg'><br><a href='test.jpg'>" + "手动点击打开 test.jpg,会跳转另一页面,并且服务返回 为 404 错误</a><br>"); out .println("<form method=post action='/path? qryParm=POST URL 查询参数' > POST 表单 " + "<input name=username value='用户'> " + "<input name=submit type=submit value=submit></form>"); out .println("<form method=get action='/path? qryParm=GET URL 查询参数' > GET 表单 " + "<input name=username value='用户'> " + "<input name=submit type=submit value=submit></form>"); out .println("<form method=post action='/path? qryParm=POST URL 查询参数'" + " enctype='multipart/form-data' >" + " 文件上传  <input type='file' name=file1 ><br>" + "          " + "<input type='file' name=file2 ><br>" + "          " + "<input name=username value='用户'> " + "<input name=submit type=submit value=submit></form>"); out.println("<a href='/download'>点击此处模拟文件下载 </a>"); out.close(); closeSocket(client); } } catch (Exception e) { System.out.println("HTTP 服 务 器 错 误 :" + e.getLocalizedMessage()); } } //System.out.println(client+"连接到 HTTP 服务器");//如果加入这一句,服 务器响应速度会很慢 } catch (Exception e) { System.out.println("HTTP 服务器错误:" + e.getLocalizedMessage()); } } } /* 162. * 这里我们自己模拟读取一行,因为如果使用 API 中的 BufferedReader 时, 它是读取到一个回车换行后 163. * 才返回,否则如果没有读取,则一直阻塞,这就导致如果为 POST 请求时, 表单中的元素会以消息体传送, 164. * 这 时 , 消 息 体 最 末 按 标 准 是 没 有 回 车 换 行 的 , 如 果 此 时 还 使 用 BufferedReader 来读时,则 POST 提交 165. * 时会阻塞。如果是 POST 提交时我们按照消息体的长度 Content-Length 来 截取消息体,这样就不会阻塞 166. */ private String readLine(InputStream is, int contentLe) throws IOException { ArrayList lineByteList = new ArrayList(); byte readByte; int total = 0; if (contentLe != 0) { do { readByte = (byte) is.read(); lineByteList.add(Byte.valueOf(readByte)); total++; } while (total < contentLe);//消息体读还未读完 } else { do { readByte = (byte) is.read(); lineByteList.add(Byte.valueOf(readByte)); } while (readByte != 10); } byte[] tmpByteArr = new byte[lineByteList.size()]; for (int i = 0; i < lineByteList.size(); i++) { tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue(); } lineByteList.clear(); String tmpStr = new String(tmpByteArr, encoding); /* http 请求的 header 中有一个 Referer 属性,这个属性的意思就是如果当前请 求是从别的页面链接过 * 来的,那个属性就是那个页面的 url,如果请求的 url 是直接从浏览器地址栏输 入的就没有这个值。得 * 到这个值可以实现很多有用的功能,例如防盗链,记录访问来源以及记住刚才 访问的链接等。另外,浏 * 览器发送这个 Referer 链接时好像固定用 UTF-8 编码的,所以在 GBK 下出现 乱码,我们在这里纠正一下 */ if (tmpStr.startsWith("Referer")) {//如果有 Referer 头时,使用 UTF-8 编码 tmpStr = new String(tmpByteArr, "UTF-8"); } return tmpStr; } /** * 关闭客户端 socket 并打印一条调试信息. * @param socket 客户端 socket. */ void closeSocket(Socket socket) { try { socket.close(); } catch (IOException ex) { ex.printStackTrace(); } System.out.println(socket + "离开了 HTTP 服务器"); } /** * 读取一个图像文件的内容并返回给浏览器端. * @param fileName 文件名 * @param socket 客户端 socket. 219. */ void imgDownload(String fileName, Socket socket) { try { PrintStream out = new PrintStream(socket.getOutputStream(), true); File fileToSend = new File(fileName); if (fileToSend.exists() && !fileToSend.isDirectory()) { out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应答 out.println("Content-Type: application/octet-stream"); out.println("Content-Length: " + fileToSend.length());// 返回内容字节 数 out.println();// 根据 HTTP 协议, 空行将结束头信息 FileInputStream fis = new FileInputStream(fileToSend); byte data[] = new byte[fis.available()]; fis.read(data); out.write(data); //文件下载完后关闭 socket 流,但 socket 还没有关闭 out.close(); fis.close(); } } catch (Exception e) { e.printStackTrace(); } } /** * 读取一个文件的内容并返回给浏览器端. 246. * @param fileName 文件名 247. * @param socket 客户端 socket. 248. */ void fileDownload(String fileName, Socket socket) { try { PrintStream out = new PrintStream(socket.getOutputStream(), true); File fileToSend = new File(fileName); if (fileToSend.exists() && !fileToSend.isDirectory()) { out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应答 out.println("Content-Type: application/octet-stream;charset=" + encoding); /* Content-Disposition 不是标准参数,查看一下 HTTP/1.1 的规范文档, 对于这个参数的解释大意如下: 258. * Content-Disposition 参数本来是为了在客户端另存文件时提供一 个建议的文件名,但是考虑到安全的原因, 259. * 就从规范中去掉了这个参数。但是由于很多浏览器已经能够支持这个 参数,所以只是在规范文档中列出,但是要 260. * 注意这个不是 HTTP/1.1 的标准参数。其值为“attachment”,那么 无论这个文件是何类型,浏览器都会提示我 261. * 们下载此文件,因为此时它认为后面的消息体是一个“附件”,不 需要由浏览器来处理了。 262. */ out.println("Content-Disposition: attachment;filename=测试下载文 件.txt"); // out.println("Accept-Ranges: bytes"); out.println("Content-Length: " + fileToSend.length());// 返回内容字节 数 out.println();// 根据 HTTP 协议, 空行将结束头信息 FileInputStream fis = new FileInputStream(fileToSend); byte[] tmpByteArr = new byte[10];//这里为了测试看下载进度条,所以 设置小点 while (fis.available() > 0) { int readCount = fis.read(tmpByteArr); out.write(tmpByteArr, 0, readCount); } //文件下载完后关闭 socket 流 out.close(); fis.close(); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PORT = 8080; new SimpleHttpServer(); } } </article> <div class="alert alert-warning" role="alert">...</div> <div class="thumbnail text-center"> <div class="more"> <span>还剩47页未读</span> <p class="go mt10"> <span class="btn btn-default" id="showMore" data-page="1"><i class="fa fa-chevron-down"></i> 继续阅读</span> </p> </div> </div> <div class="thumbnail box-line"> <div class="l1 line"></div> <div class="l2 line"></div> <div class="l3 line"></div> <div class="l4 line"></div> <div class="l5 line"></div> <div class="l6 line"></div> <div id="reader-more"> <p class="title">下载pdf到电脑,查找使用更方便</p> <p class="gray"> pdf的实际排版效果,会与网站的显示效果略有不同!!</p> <p class="download-info"> <span style="font-size: 14px;color: #888888">需要</span> <span style="font-size: 24px;">5</span> <span style="font-size: 14px;padding-right: 20px;color: #888888">金币</span> <a href="javascript:void(null);" onclick="JC.redirect('/pdf/create')" style="color: #cf6a07"> [ 分享pdf获得金币 ] </a> <span class="fcff">2 人已下载</span> </p> <p> <a class="btn btn-danger download buy circle80 fs30" href="javascript:void(null);" data-type="3" data-num="5" data-download="true"><i aria-hidden="true" class="fa fa-yen"> </i> 下载pdf</a> </p> </div> </div> </div> <!--right--> <div class="col-md-3"> <div class="thumbnail"> <h4>pdf贡献者</h4> <div class="ui items"> <div class="item"> <a class="ui tiny image" style="width: 50px;"> <img src="https://simg.open-open.com/show/77fbac33ccff8363b873771ec9e3ffd7.jpg" width="50"> </a> <div class="content"> <a class="header" href="https://user.open-open.com/u/111609"> cqmrzeng </a> <div class="description"> <p>贡献于2012-07-19</p> </div> </div> </div> </div> <div> 下载需要 <span style="font-size: 24px;">5</span> <span style="font-size: 14px;padding-right: 20px;color: #888888">金币</span> <a href="javascript:void(null);" onclick="JC.redirect('https://user.open-open.com/pay')" style="color: #cf6a07"> [金币充值 ] </a> <div class="kind-tip">亲,您也可以通过 <a href="javascript:void(0) " onclick="JC.redirect('/pdf/create')">分享原创pdf</a> 来获得金币奖励!</div> </div> </div> <div> <a class="btn btn-block buy btn-danger download" href="javascript:void(null);" data-type="3" data-num="5" data-download="true"><i aria-hidden="true" class="fa fa-yen"></i> 下载pdf</a> </div> <div class="box side-box mt20"> <div class="title"> <h3><i class="fa fa-tags" aria-hidden="true"></i> 关键词</h3> </div> <p class="tags mt10"> <a class="" href="/pdf/tag/web-fuwuqi.html">Web服务器</a> </p> </div> <div class="ad"> <script>(function() {var s = "_" + Math.random().toString(36).slice(2);document.write('<div id="' + s + '"></div>');(window.slotbydup=window.slotbydup || []).push({id: '4133327', container: s, size: '0,0', display: 'inlay-fix'});})();</script><script src="https://dup.baidustatic.com/js/os.js"></script> </div> <div class="box side-box mt20"> <div class="title"> <h3>相关pdf</h3> </div> <ul> <li class="ellipsis"> <a href="/pdf/441cf23b928744948596f94c787919f2.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  Java Socket 现实简单的http服务</a> </li> <li class="ellipsis"> <a href="/pdf/707938bb9cb6414db4931e2c17890d9b.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  Java Socket现实简单的HTTP服务</a> </li> <li class="ellipsis"> <a href="/pdf/bee5cc9182aa4cfd9a90a22d194b028a.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  WEBIM简单实现</a> </li> <li class="ellipsis"> <a href="/pdf/55b5af9332284fe480eac99cb5372bea.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  HTTP 代理服务器在 Windows下的实现</a> </li> <li class="ellipsis"> <a href="/pdf/3d9eb1f93a584f229eaf5f6b0c4978f3.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  linux+socket转发服务器</a> </li> <li class="ellipsis"> <a href="/pdf/39c1a94dc0aa4626b8442a87d5daf09c.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  Web环境下单点登录服务的设计与实现</a> </li> <li class="ellipsis"> <a href="/pdf/a966c8d768154c66a58f1dc28fae093d.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  基于android的socket服务器编程</a> </li> <li class="ellipsis"> <a href="/pdf/734abe021a8c4382b8c9fc596b2e6fb4.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  现代软件工程 - 简单实现</a> </li> <li class="ellipsis"> <a href="/pdf/e6ad1ce045ef48fe9cc3b52c0167c022.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  基于http的安卓与服务器交互方法的实现</a> </li> <li class="ellipsis"> <a href="/pdf/0a6e723aee064ec39bbdf292b19a726b.html"><i class="fa fa-file-word-o" aria-hidden="true"></i>  一个简单的完成端口(服务端_客户端)类</a> </li> </ul> </div> </div> </div> </div> </div> <footer > <div class="container py-5"> <div class="row"> <div class="col-md-3"> <h5>社区</h5> <div class="row"><div class="col-md-6"><a class="text-muted" href="/project/">项目</a></div><div class="col-md-6"><a class="text-muted" href="/solution/">问答</a></div><div class="col-md-6"><a class="text-muted" href="/wenku/">文库</a></div><div class="col-md-6"><a class="text-muted" href="/code/">代码</a></div><div class="col-md-6"><a class="text-muted" href="/lib/">经验</a></div><div class="col-md-6"><a class="text-muted" href="/news/">资讯</a></div></div> <ul class="list-unstyled text-small ut-mt20"><li><a class="text-muted" title=" 安卓开发专栏" target="_blank" href="http://www.open-open.com/lib/list/177">安卓开发专栏</a></li><li><a class="text-muted" href="http://www.open-open.com/lib/tag/开发者周刊" target="_blank" rel="tag">开发者周刊</a></li><li><a class="text-muted" href="http://www.open-open.com/lib/view/open1475497562965.html" target="_blank" rel="tag">Android Studio 使用推荐</a></li><li><a class="text-muted" href="http://www.open-open.com/lib/view/open1475497355674.html" target="_blank" rel="tag">Android开发推荐</a></li></ul> </div> <div class="col-md-3"> <h5>帮助中心</h5> <ul class="list-unstyled text-small"><li><a class="text-muted" href="/upload.html">文档上传须知</a></li></ul> <h5>关于我们</h5> <ul class="list-unstyled text-small"><li><a class="text-muted" href="/about.html">关于深度开源</a></li><li><a class="text-muted" href="/duty.html">免责声明</a></li><li><a class="text-muted" href="/contact.html">联系我们</a></li></ul> </div> <div class="col-md-6 text-center"><img class=center-block src="https://static.open-open.com/img/logo01.svg" width=190px alt="深度开源"><small class="d-block mb-3 text-muted ut-mt40">© 2006-2019 深度开源 —— 开源项目,开源代码,开源文档,开源新闻,开源社区  杭州精创信息技术有限公司  <br/><br/><img src="https://static.open-open.com/img/beian.png"/><a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=33010602002439">  浙公网安备 33010602002439号</a>  <a target="_blank" href="https://www.miibeian.gov.cn/">浙ICP备09019653号-31</a></small></div> </div> </div> </footer> <div id="fTools"><span id="gotop"> <i class="fa fa-arrow-up" aria-hidden="true"></i> </span><span id="feedback" title="建议反馈"> <i class="fa fa-inbox" aria-hidden="true"></i></span></div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script type="text/javascript" src="https://static.open-open.com/js/lib.js"></script> <script type="text/javascript" src="https://static.open-open.com/assets/jquery-confirm/jquery-confirm.js?v=4.7.0"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script> <script src="https://static.open-open.com/js/bootstrap.min.js"></script> <script type="text/javascript" src="https://static.open-open.com/js/base.js?v=2.002"></script> <script type="text/javascript" src="https://static.open-open.com/js/jq-plug.js?v=2.002"></script> <script> $(function () { JC.reminderPop();//弹出用户信息 $(".link-login").click(function(){ JC.lORr('login'); }); $("#topSearch").searchInit(); //用户登录状态 JC.setLogin(false); }); </script> <!-- JavaScript at the bottom for fast page loading --> <!-- end scripts --> </body> </html>