在日常的开发中,我们用过非常多开源的webserver,比如tomcat、apache等等。如今我们自己实现一个简单的webserver,主要的功能就是用户点击要訪问的资源,server将资源发送到client的浏览器。为了简化操作。这里不考虑资源不存在等异常情况。web服务基于的是HTTP协议。用户在浏览器的地址栏输入要訪问的地址,server怎样得到该地址是个关键。先看下一般的HTTP请求和响应报文的一般格式:
HTTP 请求报文
HTTP 响应报文
webserver获取一个用户的连接时,会初始化一个线程和用户通信,代码例如以下:
import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintStream; import java.net.Socket; //每有一个连接建立时,server分出一个通信的线程 public class CommunicateThread extends Thread{ //与客户端通信的套接字 Socket client; public CommunicateThread(Socket s) { client = s; } //获取浏览器请求资源的路径 public String getResourcePath(String s){ // 一般的HTTP请求报文的第一行是“GET /index.html HTTP/1.1” // 我们要获取的就是中间的"/indext.apsx" //获取资源的位置 String s1 = s.substring(s.indexOf(' ')+1); s1 = s1.substring(1,s1.indexOf(' ')); //默认资源为index.html if(s1.equals("")) s1 = "index.html"; return s1; } public void sendFile(PrintStream out,File file){ try{ DataInputStream in = new DataInputStream(new FileInputStream(file)); int len = (int)file.length(); byte buf[] = new byte[len]; in.readFully(buf);//读取文内容到buf数组中 out.write(buf,0,len); out.flush(); in.close(); } catch(Exception e){ System.out.println(e.getMessage()); System.exit(1); } } public void run(){ try{ //获取用户的IP地址和端口号 String clientIP = client.getInetAddress().toString(); int clientPort = client.getPort(); //创建输出流对象 PrintStream out = new PrintStream(client.getOutputStream()); //创建输入流对象 DataInputStream in = new DataInputStream(client.getInputStream()); //读取浏览器提交的请求 String msg = in.readLine(); //获取文件路径 String fileName = getResourcePath(msg); System.out.println("The user asked for resource: "+fileName); File file = new File(fileName); if(file.exists()){ //依据响应报文格式设置 System.out.println(fileName+" start send"); out.println("HTTP/1.0 200 OK"); out.println("MIME_version:1.0"); out.println("Content_Type:text/html"); int len = (int) file.length(); out.println("Content_Length:"+len); out.println("");//报文头和信息之间要空一行 //发送文件 sendFile(out,file); out.flush(); } client.close(); } catch(Exception e){ System.out.println(e.getMessage()); } } }
server主要负责初始化套接字和线程。代码例如以下:
import java.net.ServerSocket; import java.net.Socket; public class WebServer { public static void main(String[] args) { int Port = 12345;//端口号,因为这里是測试,所以不要使用经常使用端口 //创建两个套接字 ServerSocket server = null; Socket client = null; try{ server = new ServerSocket(Port); //服务器開始监听 System.out.println("The WebServer is listening on port "+server.getLocalPort()); while(true){ client = server.accept(); //多线程执行 new CommunicateThread(client).start(); } }catch(Exception e){ System.out.println(e.getMessage()); } } }
执行測试:
编写一个index.html文件
<html> <head></head> <body> <h1>This is the index of my WebServer</h1><hr></body> </html>放到项目文件的根文件夹,然后在浏览器地址栏输入:“localhost:12345/index.html”,就能够看到位于server端的html文件了。注意因为server是死循环,重新启动server会发现指定的port已被绑定,仅仅须要进入任务管理器,关闭"Java(TM) Platfrom SE binary"进程就可以。最后结果例如以下所看到的:
这个server程序非常简陋,还有非常大的改进余地。
大家能够自己尝试改进。这里能够尝试一下訪问其它的文件,发现时成功的。说明这server非常不安全呀。