编译java类文件后,javac启动服务器能正常访问servlet容器(基于socket和servlet编写)

最近学习tomcat的结构,书是比较早的《how tomcat works》,上一章的示例是一个简单的web服务器,这一章是在之前的基础上扩展,增加了servlet的内容。整个编码完成后用java / javac 命令运行,可以访问servlet容器。

本示例总共有6个类

  • HttpServer(一个ServerSocket服务器用来接收请求)
  • Request (请求类,用来解释socket请求信息,实现了ServletRequest)
  • Response (响应类,根据请求寻找资源并返回)
  • StaticResourceProcessor (处理静态资源的请求)
  • ServletProcessor (处理servlet请求)
  • Constants (常量类)
  • PrimitiveServlet (简单的servlet实现类)

1.HttpServer.java

package com.chl.webserver.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


public class HttpServer {

	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
	
	private boolean shutdown = false;
	
	public static void main(String[] args) {
		HttpServer hs = new HttpServer();
		hs.await();
	}
	
	//循环等待请求
	public void await() {
		ServerSocket ss = null;
		int port = 8080;
		try {
			ss = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
		}catch(IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		
		while(!shutdown) {
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			try {
				
				socket = ss.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();
				
				Request req = new Request(input);
				req.parse();
				
				Response res = new Response(output);
				res.setRequest(req);
				
				if(req.getUri().startsWith("/servlet/")) {
					ServletProcessor sp = new ServletProcessor();
					sp.process(req, res);
				}else {
					StaticResourceProcessor sr = new StaticResourceProcessor();
					sr.process(req, res);
				}
				
				socket.close();
				shutdown = req.getUri().equals(SHUTDOWN_COMMAND);
				
			}catch(Exception e) {
				e.printStackTrace();
				System.exit(1);
			}
		}
		
		
	}
	
}

2.Request.java

package com.chl.webserver.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Request implements ServletRequest {
	// 输入流
	private InputStream input;
	// 地址
	private String uri;

	public Request(InputStream input) {
		this.input = input;
	}
	
	public String getUri() {
		return uri;
	}

	//解析http请求中的原始数据。
	void parse() {
		 //Read a set of characters from the socket
		 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.println(request.toString());
		 uri = parseUri(request.toString());
	 }

	//从 url中返回uri
	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;
	}

	@Override
	public Object getAttribute(String name) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Enumeration<String> getAttributeNames() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getCharacterEncoding() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getContentLength() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long getContentLengthLong() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getContentType() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ServletInputStream getInputStream() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getParameter(String name) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Enumeration<String> getParameterNames() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String[] getParameterValues(String name) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, String[]> getParameterMap() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getProtocol() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getScheme() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getServerName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getServerPort() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public BufferedReader getReader() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getRemoteAddr() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getRemoteHost() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setAttribute(String name, Object o) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removeAttribute(String name) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Locale getLocale() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Enumeration<Locale> getLocales() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isSecure() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public RequestDispatcher getRequestDispatcher(String path) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getRealPath(String path) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getRemotePort() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getLocalName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getLocalAddr() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getLocalPort() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public ServletContext getServletContext() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync() throws IllegalStateException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
			throws IllegalStateException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isAsyncStarted() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAsyncSupported() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public AsyncContext getAsyncContext() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public DispatcherType getDispatcherType() {
		// TODO Auto-generated method stub
		return null;
	}
}

3.Response.java

package com.chl.webserver.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;

public class Response implements ServletResponse {
	
	private static final int BUFFER_SIZE = 1024;
	
	Request request;
	OutputStream output;
	PrintWriter writer;
	
	//构造函数创建output对象
	public Response(OutputStream output) {
		this.output = output;
	}
	
	//传递request对象给reponse
	public void setRequest(Request request) {
		this.request = request;
	}
	
	// 输出一个静态资源
	public void sendStaticResource() throws IOException{
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file = new File(Constants.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,BUFFER_SIZE);
					ch = fis.read(bytes,0,BUFFER_SIZE);
				}
			}else { //如果不存在,则发送一个异常信息
				String errorMessage = "HTTP/1.1 404 File Not Found \r\n"+
					"Content-Type : text/html\r\n"+
					"Content-Length:23\r\n"+
					"\r\n"+
					"<h1>File Not Found</h1>";
				System.out.println(errorMessage);
				output.write(errorMessage.getBytes());
			}
			
			
		}catch(Exception e) {
			System.out.println(e.toString());
		}finally {
			if(fis != null)
				fis.close();
		}
	}

	@Override
	public String getCharacterEncoding() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getContentType() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public PrintWriter getWriter() throws IOException {
		
		writer = new PrintWriter(output,true);
		
		return writer;
	}

	@Override
	public void setCharacterEncoding(String charset) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setContentLength(int len) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setContentLengthLong(long len) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setContentType(String type) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setBufferSize(int size) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getBufferSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void flushBuffer() throws IOException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void resetBuffer() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean isCommitted() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void reset() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setLocale(Locale loc) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Locale getLocale() {
		// TODO Auto-generated method stub
		return null;
	}
}

4.StaticResourceProcessor.java

package com.chl.webserver.servlet;

import java.io.IOException;

/**
 * 处理对静态资源的请求
 * @author chenhailong
 *
 */
public class StaticResourceProcessor {

	public void process(Request request, Response response) {
		
		try {
			response.sendStaticResource();
		}catch(IOException e) {
			e.printStackTrace();
		}
		
	}
}

5.ServletProcessor.java

package com.chl.webserver.servlet;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;

import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletProcessor {

	
	public void process(Request request, Response response) {
		
		String uri = request.getUri();
		String servletName = uri.substring(uri.lastIndexOf("/") + 1);
		URLClassLoader loader = null;
		
		try {
			URL[] urls = new URL[1];
			URLStreamHandler streamHandler = null;
			File classPath = new File(Constants.WEB_ROOT);
			System.out.println("classPath.getCanonicalPath()--" + classPath.getCanonicalPath());
			String repository = (new URL("file",null,classPath.getCanonicalPath() + File.separator)).toString();
			urls[0] = new URL(null, repository,streamHandler);
			loader = new URLClassLoader(urls);
			
		}catch(IOException e) {
			System.out.println(e.toString());
		}
		
		Class myClass = null;
		try {
			String curPackageName = ServletProcessor.class.getPackage().getName();
			servletName = curPackageName + "." + servletName;
			myClass = loader.loadClass(servletName);
		}catch(ClassNotFoundException e) {
		}
		
		Servlet servlet = null;
		try {
			servlet = (Servlet)myClass.newInstance();
			
			servlet.service((ServletRequest)request, (ServletResponse)response);
			
		}catch(Exception e) {
			System.out.println("+++"+e.toString());
		}catch(Throwable e) {
			System.out.println("###"+e.toString());
		}
		
		
	}
	
	
}

6.Constants.java

package com.chl.webserver.servlet;

import java.io.File;

public class Constants {

	public static final String WEB_ROOT = 
			System.getProperty("user.dir") + File.separator + "com/chl/webserver/servlet/";
}

Servlet编程是通过javax.servlet和javax.servlet.http这两个包的类和接口来实现的。所有的servlet必须实现或继承实现该接口的类。

7.PrimitiveServlet.java


package com.chl.webserver.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.*;

/**
 * Servlet implementation class PrimitiveServlet
 * 所有的servlet程序都必须实现该接口或继承自实现了该接口的类。
 */
public class PrimitiveServlet implements Servlet {

	
	@Override
	public void init(ServletConfig config) throws ServletException {
		//生命周期方法,可以通过覆盖该方法来初始化对象,如加载数据库,值初始化
		
	}

	@Override
	public ServletConfig getServletConfig() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		//req包含HTTP请求信息,res可以封装servlet的响应。
		System.out.println("are you ok !");
		PrintWriter out = res.getWriter();
		out.println("Hello , Roses are red.");
		
	}

	@Override
	public String getServletInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void destroy() {
		//通常在servlet容器正在关闭正在被关闭或者servlet容器需要一些空闲内存的时候调用,尽在所有servlet线程的service方法已经退出或者超时淘汰的时候。            
		System.out.println("des !");
	}

    

}

先执行编译类文件

javac -classpath /Users/chenhailong/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar -g ./com/chl/webserver/servlet/*.java


./com/chl/webserver/servlet/Request.java:11: 错误: 找不到符号
import javax.servlet.AsyncContext;
                    ^
  符号:   类 AsyncContext
  位置: 程序包 javax.servlet
./com/chl/webserver/servlet/Request.java:12: 错误: 找不到符号
import javax.servlet.DispatcherType;
                    ^
  符号:   类 DispatcherType
  位置: 程序包 javax.servlet
./com/chl/webserver/servlet/Request.java:252: 错误: 找不到符号
	public AsyncContext startAsync() throws IllegalStateException {
	       ^
...
...
...

./com/chl/webserver/servlet/Response.java:98: 错误: 方法不会覆盖或实现超类型的方法
	@Override
	^
注: ./com/chl/webserver/servlet/Request.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
15 个错误

处理:原因是我引入了 servlet-api-2.5.jar 和 javax.servlet-api-4.0.1.jar应该使用第二个包

修改jar包名称后可以看到编译成功

chenhailongdeMacBook-Pro:src chenhailong$ javac -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar -g ./com/chl/webserver/servlet/*.java
注: ./com/chl/webserver/servlet/Request.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。

然后运行项目

chenhailongdeMacBook-Pro:servlet chenhailong$ java -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar HttpServer
错误: 找不到或无法加载主类 HttpServer

是因为类中有写包名,运行的时候需要带上包名(从包最外层执行)

## 执行
chenhailongdeMacBook-Pro:src chenhailong$ java com.chl.webserver.servlet.HttpServer

## 查看运行状态

chenhailongdeMacBook-Pro:Java chenhailong$ jps -v
63738 HttpServer
63739 Jps -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home -Xms8m

可以看到 HttpServer已经正常运行了

然后在地址栏访问 http://127.0.0.1:8080/servlet/PrimitiveServlet会报错如下

chenhailongdeMacBook-Pro:src chenhailong$ java com.chl.webserver.servlet.HttpServer
Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletRequest
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at com.chl.webserver.servlet.HttpServer.await(HttpServer.java:43)
	at com.chl.webserver.servlet.HttpServer.main(HttpServer.java:19)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletRequest
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 14 more

这是由于启动时没有指定 classpath的servlet-api.jar包,重新运行如下:

java -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar:./ com.chl.webserver.servlet.HttpServer

重新访问http://127.0.0.1:8080/servlet/PrimitiveServlet

java.lang.ClassNotFoundException: Primitiveservlet
java.lang.NullPointerException
GET /servlet/PrimitiveServlet HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

或者报错

Exception in thread "main" java.lang.NoClassDefFoundError: PrimitiveServlet (wrong name: com/chl/webserver/servlet/PrimitiveServlet)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at com.chl.webserver.servlet.ServletProcessor.process(ServletProcessor.java:39)
	at com.chl.webserver.servlet.HttpServer.await(HttpServer.java:51)
	at com.chl.webserver.servlet.HttpServer.main(HttpServer.java:19)

是因为 myClass = loader.loadClass(servletName);
这里的类名需要加 包名。
修改之后正常运行并输出内容

are you ok !
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值