服务端代码:
public class WebServer {
private ServerSocket server;
/*
* 线程池,管理用于处理客户端请求的线程
*/
private ExecutorService threadPool;
/**
* 构造方法,用于初始化服务端程序
* @throws Exception
*/
public WebServer() throws Exception{
try{
server=new ServerSocket(8080);
threadPool=Executors.newFixedThreadPool(100);
}catch(Exception e){
throw e;
}
}
public void start(){
try{
while(true){
System.out.println("等待客户端连接");
Socket socket=server.accept();
System.out.println("客户端已连接");
/*
* 将处理该客户端请求的任务交给线程池
*
*/
threadPool.execute(new ClientHandler(socket));
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
WebServer server=new WebServer();
server.start();
}catch(Exception e){
e.printStackTrace();
System.out.println("服务端启动失败!");
}
}
}
HttpContent:
/**
* 定义HTTP协议中的相关信息
* @author live
*
*/
public class HttpContent {
/*
* 回车
*/
public static final int CR=13;
/*
* 换行
*/
public static final int LF=10;
}
ClientHandler:
/**
* 处理客户端请求
* @author live
*
*/
public class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket){
this.socket=socket;
}
public void run(){
try{
/*
* HTTP协议中的请求信息格式:
* 请求行 资源 协议 换行
* GET/index.html HTTP1.1 CRLF(请求方式) POST
* 消息报头
* 消息正文
*
* 以行为单位发送至服务端 每行结尾以(CR LF)
* CR:回车 LF:换行
* http://localhost:8080
*/
//获取输入流,读取客户端发送过来的数据
InputStream in=socket.getInputStream();
//读取一行字符串,请求行信息
//Method Request-URI Http-version(CRLF)
//如:
//GET/index.html HTTP/1.1CRLF
//生成HttpRequest表示请求信息
HttpRequest request=new HttpRequest(in);
if(request.getUri()!=null){
// String line=readline(in);
// System.out.println("line:"+line);
// if(line.length()>0){
// /*
// * 解析Request-URI
// * 1:按照空格拆分请求行
// * 2:取出第二部分
// */
// String[] data=line.split("\\s");//空格字符/s
// String uri=data[1];
// System.out.println("uri:"+uri);
// int start=line.indexOf("/");
// int end=line.indexOf("H");
// String str=line.substring(start+1, end);
// System.out.println(str.trim());
/*
* 回应客户端对应的资源
* HTTP中响应的格式:
* 1:状态行
* 2:响应头
* 3:响应正文
* 状态行格式:
* HTTP-Version Status_code Reason_phrase CRLF
* HTTP协议 状态代码 状态描述CRLF
* 例如:
* HTTP/1.1 200 OK CRLF
* 2:响应头
* 响应头注明很多返回的信息,按行输出
* 常见:
* Content-Type:用来指明发送给接收者的媒体类型
* 常见的Content-Type:
* text/html:HTML格式文件(网页)
* text/xml:XML格式文件
* image/gif:gif格式图片
* image/jpeg:jpeg格式图片
* image/png:png格式图片
* Content-Length,用来指明发送给接受者实体正文的长度
* 简单的说就是发送过去的数据的字节量
*
* HTTP协议要求实际响应客户端时的数据格式:
* HTTP/1.1 200 OK CRLF 状态行
* Content-Type:text/html CRLF 响应头信息
* Content-Length:100CRLF 响应头信息
* CRLF 单独发送CRLF指明响应头全部发送完毕
* DATA 实际数据
* TLD type length data
*/
OutputStream out=socket.getOutputStream();
//响应状态行
println("HTTP/1.1 200 OK",out);
//发送响应头信息
println("Content-Type:text/html",out);
//加载客户端需要访问的文件资源
File file=new File("webapps"+request.getUri());
println("Content-Length:"+file.length(),out);
println("",out);//单独发送空行表示响应头发送完毕
/*
* 将该文件中所有字节读取出来并发送给客户端
*/
BufferedInputStream bis=new BufferedInputStream(
new FileInputStream(file));
int d=-1;
while((d=bis.read())!=-1){
out.write(d);
}
bis.close();
System.out.println("响应客户端完毕!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
socket.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
/*
* 将给定的一行字符串以HTTP协议格式要求按行写出
*/
private void println(String str,OutputStream out) throws Exception{
try {
out.write(str.getBytes("ISO8859-1"));
out.write(HttpContent.CR);
out.write(HttpContent.LF);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.out.println("不受支持的编码");
throw e;
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
}
HttpRequest:
public class HttpRequest {
//请求方法
private String method;
//请求资源
private String uri;
//请求协议
private String protocol;
/*
* 构造方法,用于创建HttpRequest实例
*
* @param in 对应客户端输入流,通过该流读取客
* 户端发送过来的请求信息并封装到当前HttpRequest对象中
*/
public HttpRequest(InputStream in){
/*
* HTTP协议中的请求信息格式:
* 请求行 资源 协议 换行
* GET/index.html HTTP1.1 CRLF(请求方式) POST
* 消息报头
* 消息正文
*
* 以行为单位发送至服务端 每行结尾以(CR LF)
* CR:回车 LF:换行
* http://localhost:8080
*/
try{
//读取一行字符串,请求行信息
//Method Request-URI Http-version(CRLF)
//如:
//GET/index.html HTTP/1.1CRLF
String line=readline(in);
if(line.length()>0){
String[] data=line.split("\\s");
method=data[0];
uri=data[1];
protocol=data[2];
}
}catch(IOException e){
e.printStackTrace();
}
}
/**
* 从给定的输入流中读取一行字符窜并返回。
* 当读取到CR LF时认为一行结束
* 13 10
*
* @param in
* @return
* @throws IOException
*/
private String readline(InputStream in)throws IOException{
/*
* 顺序的从流中读取每个字节并转换为对应的字符
* 然后拼接在一起,直到连续读取了CR LF时停止
* 并将拼接的字符串返回
*/
StringBuilder builder=new StringBuilder();
try{
int ch1=-1,ch2=-1;
while((ch2=in.read())!=-1){
if(ch1==HttpContent.CR&&ch2==HttpContent.LF){
break;
}
builder.append((char)ch2);
ch1=ch2;
}
return builder.toString().trim();
}catch(IOException e){
throw e;
}
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
}