一、首先来看一下http的报文结构
1、请求报文
一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。
(1)请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。这里介绍最常用的GET方法和POST方法。
GET:当客户端要从服务器中读取文档时,使用GET方法。GET方法要求服务器将URL定位的资源放在响应报文的数据部分,回送给客户端。使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始,传递参数长度受限制。例如,/index.jsp?id=100&op=bind。
POST:当客户端给服务器提供信息较多时可以使用POST方法。POST方法将请求参数封装在HTTP请求数据中,以名称/值的形式出现,可以传输大量数据。
(2)请求头部
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
(3)空行
最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
(4)请求数据
请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。
2、响应报文
响应报文的格式大体上和请求报文类似,只是第一行有所不同,读者可以自己在网上查找这方面的介绍,这里不再赘述。
二、程序的实现
程序的实现步骤如下:
1、接收客户端浏览器的请求;
2、创建一个新的线程,用于处理该次请求;
3、读取报文数据,判断报文正确与否,分析报文内容;
4、创建响应报文,发送到客户端;
5、结束该处理线程,处理其他客户请求;
程序代码如下:
view plaincopy to clipboardprint?
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class WebServer {
public static void main(String [] args){
int port;
ServerSocket server_socket;
try{
port=Integer.parseInt(args[0]);
}
catch (Exception e){
port=8080;
}
try{
server_socket=new ServerSocket(port);
System.out.println("WebServer running on port"+server_socket.getLocalPort());
while(true){
Socket socket=server_socket.accept();
System.out.println("New connection accepted"+socket.getInetAddress()+":"+socket.getPort());
//针对特定的请求创建处理该请求的线程
try{
httpRequestHandler request=new httpRequestHandler(socket);
Thread thread=new Thread(request);
thread.start();
}
catch(Exception e){
System.out.println(e);
}
}
}
catch(IOException e){
System.out.println(e);
}
}
}
//处理请求的线程类
class httpRequestHandler implements Runnable{
final static String CRLF="\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
//判断请求的文件类型是否正确
boolean fileType=true;
//初始化参数
public httpRequestHandler(Socket socket) throws Exception{
this.socket=socket;
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
//启动该线程
public void run(){
try{
processRequest();
}
catch(Exception e){
System.out.println(e);
}
}
//处理请求的核心函数
private void processRequest() throws Exception{
while(true){
String headerLine=br.readLine();
System.out.println("the client request is"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))
break;
StringTokenizer s=new StringTokenizer(headerLine);
String temp=s.nextToken();
if(temp.equals("GET")){
String fileName=s.nextToken();
fileName="."+fileName;
FileInputStream fis=null;
boolean fileExists=true;
if(!(fileName.endsWith(".htm")||fileName.endsWith(".html")))
{
this.fileType=false;
try{
fis=new FileInputStream("error.html");
}
catch(FileNotFoundException e){
fileExists=false;
}
}
else{
try{
fis=new FileInputStream(fileName);
}
catch(FileNotFoundException e){
fileExists=false;
}
}
String serverLine="Server:a simple java WebServer";
String statusLine=null;
String contentTypeLine=null;
String entityBody=null;
String contentLengthLine="error";
if(fileExists&&this.fileType){
statusLine="HTTP/1.0 200 OK"+CRLF;
contentTypeLine="Content-type:"+this.contentType(fileName)+CRLF;
contentLengthLine="Content-Length:"+(new Integer(fis.available())).toString()+CRLF;
}
else{
if(fileExists&&this.fileType==false){
statusLine="HTTP/1.0 400 BadRequest"+CRLF;
contentTypeLine="text/html";
entityBody="<HTML>400 Not BadRequest</TITLE></HEAD>"+
"<BODY>400 BadRequest"+
"<br>usage:http://yourHostName:port/"+
"fileName.html</BODY></HTML>";
}
else if(fileExists==false){
statusLine="HTTP/1.0 404 Not Found"+CRLF;
contentTypeLine="text/html";
entityBody="<HTML>404 Not Found</TITLE></HEAD>"+
"<BODY>404 Not Found"+
"<br>usage:http://yourHostName:port/"+
"fileName.html</BODY></HTML>";
}
}
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
output.write(CRLF.getBytes());
if(fileExists&&this.fileType){
sendBytes(fis,output);
fis.close();
}
else{
output.write(entityBody.getBytes());
}
}
}
try{
output.close();
br.close();
socket.close();
}
catch(Exception e){}
}
//将客户端请求的页面发送出去
private static void sendBytes(FileInputStream fis,OutputStream os) throws Exception{
byte[] buffer=new byte[1024];
int bytes=0;
while((bytes=fis.read(buffer))!=-1){
os.write(buffer,0,bytes);
}
}
//设置contentType的内容
private static String contentType(String fileName){
if(fileName.endsWith(".htm")||fileName.endsWith(".html")){
return "text/html";
}
return "fileName";
}
}