注:本段内容来源于《JAVA 实现 简单的 HTTP服务器》
1、 HTTP所有状态码
状态码 |
状态码英文名称 |
中文描述 |
---|---|---|
100 |
Continue |
继续。客户端应继续其请求 |
101 |
Switching
Protocols |
切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
200 |
OK |
请求成功。一般用于GET与POST请求 |
201 |
Created |
已创建。成功请求并创建了新的资源 |
202 |
Accepted |
已接受。已经接受请求,但未处理完成 |
203 |
Non-Authoritative
Information |
非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
204 |
No Content |
无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
205 |
Reset Content |
重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
206 |
Partial
Content |
部分内容。服务器成功处理了部分GET请求 |
300 |
Multiple
Choices |
多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
301 |
Moved
Permanently |
永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 |
Found |
临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
303 |
See Other |
查看其它地址。与301类似。使用GET和POST请求查看 |
304 |
Not Modified |
未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
305 |
Use Proxy |
使用代理。所请求的资源必须通过代理访问 |
306 |
Unused |
已经被废弃的HTTP状态码 |
307 |
Temporary
Redirect |
临时重定向。与302类似。使用GET请求重定向 |
400 |
Bad Request |
客户端请求的语法错误,服务器无法理解 |
401 |
Unauthorized |
请求要求用户的身份认证 |
402 |
Payment
Required |
保留,将来使用 |
403 |
Forbidden |
服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 |
Not Found |
服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
405 |
Method Not
Allowed |
客户端请求中的方法被禁止 |
406 |
Not
Acceptable |
服务器无法根据客户端请求的内容特性完成请求 |
407 |
Proxy Authentication
Required |
请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
408 |
Request
Time-out |
服务器等待客户端发送的请求时间过长,超时 |
409 |
Conflict |
服务器完成客户端的PUT请求时可能返回此代码,服务器处理请求时发生了冲突 |
410 |
Gone |
客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
411 |
Length
Required |
服务器无法处理客户端发送的不带Content-Length的请求信息 |
412 |
Precondition
Failed |
客户端请求信息的先决条件错误 |
413 |
Request Entity Too
Large |
由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
414 |
Request-URI Too
Large |
请求的URI过长(URI通常为网址),服务器无法处理 |
415 |
Unsupported Media
Type |
服务器无法处理请求附带的媒体格式 |
416 |
Requested range not
satisfiable |
客户端请求的范围无效 |
417 |
Expectation
Failed |
服务器无法满足Expect的请求头信息 |
500 |
Internal Server
Error |
服务器内部错误,无法完成请求 |
501 |
Not
Implemented |
服务器不支持请求的功能,无法完成请求 |
502 |
Bad Gateway |
充当网关或代理的服务器,从远端服务器接收到了一个无效的请求 |
503 |
Service
Unavailable |
由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
504 |
Gateway
Time-out |
充当网关或代理的服务器,未及时从远端服务器获取请求 |
505 |
HTTP Version not
supported |
服务器不支持请求的HTTP协议的版本,无法完成处理 |
2、 关于HTTP响应头:
响应头
客户端向服务器发送一个请求,服务器以一个状态行作为响应,响应的内容包括:消息协议的版本、成功或者错误编码、服务器信息、实体元信息以及必要的实体内容。根据响应类别的类别,服务器响应里可以含实体内容,但不是所有的响应都有实体内容。本节仅简述响应头[13] 。
响应头第一行
响应头第一行也称为状态行,格式如下:
HTTP-Version 空格 Status-Code 空格 Reason-Phrase CRLF
HTTP- Version表示HTTP版本,例如为HTTP/1.1。Status- Code是结果代码,用三个数字表示。Reason-Phrase是个简单的文本描述,解释Status-Code的具体原因。Status-Code用于机器自动识别,Reason-Phrase用于人工理解。Status-Code的第一个数字代表响应类别,可能取5个不同的值。后两个数字没有分类作用。Status-Code的第一个数字代表响应的类别,后续两位描述在该类响应下发生的具体状况,具体请参见:HTTP状态码 。
3、关于HTTP的请求方法:
请求方法
HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式:
OPTIONS - 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。
HEAD- 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
GET - 向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
POST - 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT - 向指定资源位置上传其最新内容。
DELETE - 请求服务器删除Request-URI所标识的资源。
TRACE- 回显服务器收到的请求,主要用于测试或诊断。
CONNECT - HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
PATCH - 用来将局部修改应用于某一资源,添加于规范RFC5789。
方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed);当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。
HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。当然,所有的方法支持的实现都应当符合下述的方法各自的语义定义。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。
一个简单的Java web服务器实现
注:本段内容来源于《一个简单的Java web服务器实现》
前言
一个简单的Java web服务器实现,比较简单,基于java.net.Socket和java.net.ServerSocket实现;
程序执行步骤
- 创建一个ServerSocket对象;
- 调用ServerSocket对象的accept方法,等待连接,连接成功会返回一个Socket对象,否则一直阻塞等待;
- 从Socket对象中获取InputStream和OutputStream字节流,这两个流分别对应request请求和response响应;
- 处理请求:读取InputStream字节流信息,转成字符串形式,并解析,这里的解析比较简单,仅仅获取uri(统一资源标识符)信息;
- 处理响应:根据解析出来的uri信息,从WEB_ROOT目录中寻找请求的资源资源文件, 读取资源文件,并将其写入到OutputStream字节流中;
- 关闭Socket对象;
- 转到步骤2,继续等待连接请求;
代码实现
服务器实现:
package ex01.pyrmont; import java.net.Socket; import java.net.ServerSocket; import java.net.InetAddress; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import java.io.File; public class HttpServer { /** * WEB_ROOT是HTML和其它文件存放的目录. 这里的WEB_ROOT为工作目录下的webroot目录 */ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; // 关闭服务命令 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; public static void main(String[] args) { HttpServer server = new HttpServer(); //等待连接请求 server.await(); } public void await() { ServerSocket serverSocket = null; int port = 8080; try { //服务器套接字对象 serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } // 循环等待一个请求 while (true) { Socket socket = null; InputStream input = null; OutputStream output = null; try { //等待连接,连接成功后,返回一个Socket对象 socket = serverSocket.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); // 创建Request对象并解析 Request request = new Request(input); request.parse(); // 检查是否是关闭服务命令 if (request.getUri().equals(SHUTDOWN_COMMAND)) { break; } // 创建 Response 对象 Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); // 关闭 socket 对象 socket.close(); } catch (Exception e) { e.printStackTrace(); continue; } } } }
Request类:
package ex01.pyrmont; import java.io.InputStream; import java.io.IOException; public class Request { private InputStream input; private String uri; public Request(InputStream input) { this.input = input; } //从InputStream中读取request信息,并从request中获取uri值 public void parse() { StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try { i = input.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j = 0; j < i; j++) { request.append((char) buffer[j]); } System.out.print(request.toString()); uri = parseUri(request.toString()); } /** * * requestString形式如下: * GET /index.html HTTP/1.1 * Host: localhost:8080 * Connection: keep-alive * Cache-Control: max-age=0 * ... * 该函数目的就是为了获取/index.html字符串 */ private String parseUri(String requestString) { int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) return requestString.substring(index1 + 1, index2); } return null; } public String getUri() { return uri; } }
Response类:
package ex01.pyrmont; import java.io.OutputStream; import java.io.IOException; import java.io.FileInputStream; import java.io.File; /* HTTP Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ] Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ public class Response { private static final int BUFFER_SIZE = 1024; Request request; OutputStream output; public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } public void sendStaticResource() throws IOException { byte[] bytes = new byte[BUFFER_SIZE]; FileInputStream fis = null; try { //将web文件写入到OutputStream字节流中 File file = new File(HttpServer.WEB_ROOT, request.getUri()); if (file.exists()) { fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SIZE); while (ch != -1) { output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); } } else { // file not found String errorMessage = "HTTP/1.1 404 File Not Found " + "Content-Type: text/html " + "Content-Length: 23 " + " " + "<h1>File Not Found</h1>"; output.write(errorMessage.getBytes()); } } catch (Exception e) { // thrown if cannot instantiate a File object System.out.println(e.toString()); } finally { if (fis != null) fis.close(); } } }
现在在webroot中创建一个html页面,命名为index.html,源码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>Hello World!</h1> </body> </html>
现在启动该WEB服务器,并请求index.html静态页面。
所对应的控制台的输出:
如此,一个简单的http服务器便完成了。