Cyh的博客

Email:kissyan4916@163.com
posts - 26, comments - 19, trackbacks - 0, articles - 220

网络编程>>HTTP服务器

Posted on 2009-12-12 16:22 啥都写点 阅读(310) 评论(0)  编辑  收藏 所属分类: J2SE
     与一般的Socket编程一样,服务器端需要在某个端口上开启一个ServerSocket,以等待、接受客户端的请求。
     采用多线程技术,支持多用户同时访问,对于每一个请求,都用一个线程专门去处理。
     客户端发送给服务器端的HTTP请求遵循一定的格式,服务器端需要根据HTTP请求确定请求的类型以及请求的文件名,这些信息都存储在请求消息的第一行中。比如,用户浏览器地址栏中输入:" http://localhost:80/test/hehe.htm ",那么产生的请求的第一行便是"GET/test/hehe.htm  HTTP/1.1"         Socket的getInetAddress方法能获得客户端的网络地址信息,包括IP和端口号。
     在给客户端发送响应消息时,也要按照HTTP协议,给响应消息添加头信息,然后将被请求文件的字节数组添加到头信息的后面,头信息的第一行为为"HTTP/1.1 200 0K" 表示"HTTP协议的版本为1.1,处理请求成功",头信息的第一行为"HTTP/1.1 400"表示"HTTP协议的版本为1.1,处理请求失败,文件没找到"。


/**
 * HTTP的服务器,接收来自客户端的HTTP请求。
 * 将要发布的HTML文件放置在工程的根目录下,
 * 然后在浏览器中输入类似"
http://localhost:80/"的网址,
 * 将能够显示网页的内容。
 
*/

public class HttpServer {
    
// 服务器名和端口
    String serverName;
    
int serverPort;

    
// 定义server的名字、版本号、端口 
    public HttpServer(String name, int port) {
        
this.serverName = name;
        
this.serverPort = port;
    }


    
public void run() {
        
// 显示名字和端口号 
        System.out.println("HttpServer: " + serverName + "" + serverPort);
        
try {
            
// 得到服务器监听端口 
            ServerSocket server = new ServerSocket(serverPort);
            
do {
                
// 等待连接请求 
                Socket client = server.accept();
                
// 为连接请求分配一个线程 
                (new HTTPServerThread(client)).start();
            }
 while (true);
        }
 catch (Exception e) {
            e.printStackTrace();
            System.exit(
1);
        }

    }

    
// 构造一个server,并运行 
    public static void main(String args[]) {
        HttpServer server 
= new HttpServer("MyHTTPServer"80);
        server.run();
    }

}


/**
 * 处理HTTP请求的线程,一个HTTP请求对应一个线程
 
*/

class HTTPServerThread extends Thread {
    
// 服务器与客户端之间的socket
    Socket client;
    
public HTTPServerThread(Socket client) {
        
this.client = client;
    }


    
public void run() {
        
try {
            
// 显示连接信息
            describeConnectionInfo(client);
            
// 获取流向到客户端的输出流
            BufferedOutputStream outStream = new BufferedOutputStream(client
                    .getOutputStream());
            
// 获取来自客户端的输入流 
            HTTPInputStream inStream = new HTTPInputStream(client
                    .getInputStream());
            
// 得到客户端的请求头(自定义类) 
            HTTPRequest request = inStream.getRequest();
            
// 显示头信息 
            request.log();
            
// 目前只处理GET请求 
            if (request.isGetRequest()){
                processGetRequest(request, outStream);
            }

            System.out.println(
"Request completed. Closing connection.");
            
//关闭socket 
            client.close();
        }
 catch (IOException e) {
            System.out.println(
"IOException occurred .");
            e.printStackTrace();
        }

    }


    
// 显示socket连接信息
    void describeConnectionInfo(Socket client) {
        
//客户端的主机名 
        String destName = client.getInetAddress().getHostName();
        
//客户端的IP地址 
        String destAddr = client.getInetAddress().getHostAddress();
        
//客户端的端口 
        int destPort = client.getPort();
        
//打印信息,表示客户端已经连接到本服务器上 
        System.out.println("Accepted connection to " + destName + " ("
                
+ destAddr + ")" + " on port " + destPort + ".");
    }


    
// 处理GET请求 
    void processGetRequest(HTTPRequest request, BufferedOutputStream outStream)
            
throws IOException {
        
// 获得客户端要get的文件名
        String fileName = request.getFileName();
        File file 
= new File(fileName);
        
// 如果文件存在,则将文件内容发送到socket的输出流,即客户端。 
        if (file.exists()){
            sendFile(outStream, file);
        }
 else {
            System.out.println(
"File " + file.getCanonicalPath()
                    
+ " does not exist.");
        }

    }


    
//  发送文件内容到客户端,这里以HTTP 1.1的协议实现的
    void sendFile(BufferedOutputStream out, File file) {
        
try {
            
// 将文件内容全部读入到一个字节数组中
            DataInputStream in = new DataInputStream(new FileInputStream(file));
            
int len = (int) file.length();
            
byte buffer[] = new byte[len];
            
// 完全读取,然后关闭文件流
            in.readFully(buffer);
            in.close();
            
            
// 写到socket的输出流中
            out.write("HTTP/1.1 200 OK\r\n".getBytes());
            out.write((
"Content-Length: " + buffer.length + "\r\n").getBytes());
            out.write(
"Content-Type: text/HTML\r\n\r\n".getBytes());
            out.write(buffer);
            out.flush();
            out.close();
            
// 写文件内容结束,log信息
            System.out.println("File sent: " + file.getCanonicalPath());
            System.out.println(
"Number of bytes: " + len);
        }
 catch (Exception e) {
            
try {
                
// 发送失败
                out.write(("HTTP/1.1 400 " + "No can do" + "\r\n").getBytes());
                out.write(
"Content-Type: text/HTML\r\n\r\n".getBytes());
            }
 catch (IOException ioe) {
            }

            System.out.println(
"Error retrieving " + file);
        }

    }

}


/**
 * 实现读客户端请求的帮助类
 
*/

class HTTPInputStream extends FilterInputStream {
    
public HTTPInputStream(InputStream in) {
        
super(in);
    }


    
// 读一行,当输入流中没有数据,或者读到"\r\n"时,一行结束。 
    public String readLine() throws IOException {
        StringBuffer result 
= new StringBuffer();
        
boolean finished = false;
        
//'\r'为回车符,值为13,'\n'为换行符,值为10
        
// cr变量表示是否已经读到换行符
        boolean cr = false;
        
do {
            
int ch = -1;
            
// 读一个字节
            ch = read();
            
if (ch == -1){
                
return result.toString();
            }

            result.append((
char) ch);
            
//去掉最后的'\r\n' 
            if (cr && (ch == 10)) {
                result.setLength(result.length() 
- 2);
                
return result.toString();
            }

            
//读到回车符,设置标识 
            if (ch == 13){
                cr 
= true;
            }
 else {
                cr 
= false;
            }

        }
 while (!finished);
        
return result.toString();
    }


    
// 得到所有的请求 
    public HTTPRequest getRequest() throws IOException {
        HTTPRequest request 
= new HTTPRequest();
        String line;
        
do {
            
// 依次读取
            line = readLine();
            
//将请求填入容器 
            if (line.length() > 0){
                request.addLine(line);
            }
 else {
                
break;
            }

        }
 while (true);
        
// 返回
        return request;
    }

}


// 客户端请求的封装类 
class HTTPRequest {
    
// 请求的数据,按行存储
    Vector lines = new Vector();
    
public HTTPRequest() {
    }


    
public void addLine(String line) {
        lines.addElement(line);
    }


    
// 判断是否是Get请求 
    boolean isGetRequest() {
        
if (lines.size() > 0{
            
// 获取请求内容的第一行,如果头三个字符是"GET",则为Get请求
            String firstLine = (String) lines.elementAt(0);
            
if (firstLine.length() > 0){
                
if (firstLine.substring(03).equalsIgnoreCase("GET")){
                    
return true;
                }

            }

        }

        
return false;
    }


    
// 从请求中解析到文件名 
    
// 一般第一行的消息如此类格式:"GET /hehe.htm HTTP/1.1"
    /**
     * 从请求中解析到文件名,只需要处理第一行即可。
     * 第一行的格式有如下几种:
     * (1)如果请求的URL为"
http://localhost:80/test/hehe.htm",
     * 则第一行的内容为"GET /test/hehe.htm HTTP/1.1"。
     * (2)如果请求的URL为"
http://localhost:80",
     * 则第一行的内容为"GET HTTP/1.1",此时应该找默认的html文件,如index.htm
     * (3)如果请求的URL为"
http://localhost:80/test",
     * 则第一行的内容为"GET /test/ HTTP/1.1",此时应该找test目录下默认的html文件
     
*/

    String getFileName() 
{
        
if (lines.size() > 0{
            
//得到vector中第一个元素 
            String firstLine = (String) lines.elementAt(0);

            System.out.println(
"firstLine:  " + firstLine);
            
//根据http消息格式得到文件名
            String fileName = firstLine.substring(firstLine.indexOf(" "+ 1);
            
int n = fileName.indexOf(" ");
            
//URL在两个空格之间 
            if (n != -1){
                fileName 
= fileName.substring(0, n);
            }


            
//去掉第一个'/' 
            try {
                
if (fileName.charAt(0== '/'){
                    fileName 
= fileName.substring(1);
                }

            }
 catch (StringIndexOutOfBoundsException ex) {
            }


            
//默认首页,这里认为index.htm为默认的首页
            
//类似于'http://localhost:80'的情况 
            if (fileName.equals("")){
                fileName 
= "index.htm";
            }

            
//类似于'http://localhost:80/download/'的情况 
            if (fileName.charAt(fileName.length() - 1== '/'){
                fileName 
+= "index.htm";
            }

            System.out.println(
"fileName:  " + fileName);
            
return fileName;
        }
 else {
            
return "";
        }

    }


    
// 显示请求信息 
    void log() {
        System.out.println(
"Received the following request:");
        
for (int i = 0; i < lines.size(); ++i){
            System.out.println((String) lines.elementAt(i));
        }

    }

}


                                                                                                       --    学海无涯