最近研究一方socket编程,由于想动手写关于socket方面的东西。然而我们知道通过URL去访问某网址,其实其底层用的就是socket,于是我就写了一个很简单的tomcat服务器,主要目地在于学习,在此分享给大家。同时提供下载源工程。
我写的工程用Maven管理的,但是我没有引入其它的JAR包,为此我就不列出pom.xml文件了。
在此简要地说明每个类的作用:
Server.java
该类的作用就是将服务提起来的,并且利用线程池。
package com.cloud.tomcat.server;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
private static ServerSocket serverSocket;
private static ExecutorService executorService;
private final static int POOL_SIZE = 15;
public static void main(String[] args) throws Exception {
serverSocket = new ServerSocket(8080);
Socket socket = null;
executorService = Executors.newFixedThreadPool(POOL_SIZE);
while (true) {
socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
writer.println("HTTP/1.1 200 OK");
writer.println("Content-Type: text/html;charset=UTF-8");
writer.println();
executorService.execute(new Handler(socket, writer));
}
}
}
Handler.java
该类的作用是根据浏览器传过来信息做出相应的处理,同时实现Runnable接口。
package com.cloud.tomcat.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import com.cloud.tomcat.servlet.HttpServlet;
public class Handler implements Runnable {
private Socket socket;
private PrintWriter writer;
public Handler(Socket socket, PrintWriter writer) {
this.socket = socket;
this.writer = writer;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String path = "";
String method = "";
while (true) {
String msg = reader.readLine();
if (null == msg || "".equals(msg.trim())) {
break;
}
String[] msgs = msg.split(" ");
if (3 == msgs.length && "HTTP/1.1".equalsIgnoreCase(msgs[2])) {
method = msgs[0];
path = msgs[1];
break;
}
}
if (path.endsWith("ico")) {
return;
}
HttpServlet httpServlet = ServletContainer.getHttpServlet(path);
String html = "";
if ("GET".equals(method)) {
html = httpServlet.doGet();
} else if ("POST".equals(method)) {
html = httpServlet.doGet();
}
writer.write(html);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
ServletContainer.java
该类首先会解析web.xml文件,然后根据url的信息,拿到相应的servlet。
package com.cloud.tomcat.server;
import java.util.HashMap;
import java.util.Map;
import com.cloud.tomcat.model.Servlet;
import com.cloud.tomcat.model.ServletMapping;
import com.cloud.tomcat.servlet.HttpServlet;
import com.cloud.tomcat.util.XMLUtil;
public class ServletContainer {
private static Map<String, Object> servletMaps = new HashMap<String, Object>();
private static Map<String, Object> servletMappingMaps = new HashMap<String, Object>();
private static Map<String, HttpServlet> servletContainer = new HashMap<String, HttpServlet>();
static {
try {
Map<Integer, Map<String, Object>> maps = XMLUtil.parseWebXML();
if (null != maps && 2 == maps.size()) {
servletMaps = maps.get(0);
servletMappingMaps = maps.get(1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static HttpServlet getHttpServlet(String path) {
if (null == path || "".equals(path.trim()) || "/".equals(path)) {
path = "/index";
}
if (servletContainer.containsKey(path)) {
return servletContainer.get(path);
}
if (!servletMappingMaps.containsKey(path)) {
return null;
}
ServletMapping servletMapping = (ServletMapping) servletMappingMaps.get(path);
String name = servletMapping.getName();
if (!servletMaps.containsKey(name)) {
return null;
}
Servlet servlet = (Servlet) servletMaps.get(name);
String clazz = servlet.getClazz();
if (null == clazz || "".equals(clazz.trim())) {
return null;
}
HttpServlet httpServlet = null;
try {
httpServlet = (HttpServlet) Class.forName(clazz).newInstance();
servletContainer.put(path, httpServlet);
} catch (Exception e) {
e.printStackTrace();
}
return httpServlet;
}
}
HttpServlet.java
为了实现起来简单方便,我自己定义了一个HttpServlet。
package com.cloud.tomcat.servlet;
public interface HttpServlet {
public String doGet();
public String doPost();
}
CloudServlet.java
HttpServlet的具体实现类。
package com.cloud.tomcat.servlet;
public class CloudServlet implements HttpServlet {
@Override
public String doGet() {
return this.doPost();
}
@Override
public String doPost() {
return "<h1>Chicago at Cloud!!!</h1>";
}
}
下面一一列出解析web.xml用到的类,由于我没有引入第三JAR包,可能这部分有点麻烦。
Servlet.java
package com.cloud.tomcat.model;
public class Servlet {
private String name;
private String clazz;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
ServletMapping.java
package com.cloud.tomcat.model;
public class ServletMapping {
private String name;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
XMLUtil.java
package com.cloud.tomcat.util;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.cloud.tomcat.model.Servlet;
import com.cloud.tomcat.model.ServletMapping;
public class XMLUtil {
public static Map<Integer, Map<String, Object>> parseWebXML() throws Exception {
Map<Integer, Map<String, Object>> result = new HashMap<Integer, Map<String,Object>>();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputStream in = XMLUtil.class.getClassLoader().getResourceAsStream("web.xml");
Document document = db.parse(in);
Element root = document.getDocumentElement();
NodeList xmlNodes = root.getChildNodes();
for (int i = 0; i < xmlNodes.getLength(); i++) {
Node config = xmlNodes.item(i);
if (null != config && config.getNodeType() == Node.ELEMENT_NODE) {
String nodeName1 = config.getNodeName();
if ("servlet".equals(nodeName1)) {
Map<String, Object> servletMaps = null;
if (result.containsKey(0)) {
servletMaps = result.get(0);
} else {
servletMaps = new HashMap<String, Object>();
}
NodeList childNodes = config.getChildNodes();
Servlet servlet = new Servlet();
for (int j = 0; j < childNodes.getLength(); j++) {
Node node = childNodes.item(j);
if (null != node && node.getNodeType() == Node.ELEMENT_NODE) {
String nodeName2 = node.getNodeName();
String textContent = node.getTextContent();
if ("servlet-name".equals(nodeName2)) {
servlet.setName(textContent);
} else if ("servlet-class".equals(nodeName2)) {
servlet.setClazz(textContent);
}
}
}
servletMaps.put(servlet.getName(), servlet);
result.put(0, servletMaps);
} else if ("servlet-mapping".equals(nodeName1)) {
Map<String, Object> servletMappingMaps = null;
if (result.containsKey(1)) {
servletMappingMaps = result.get(1);
} else {
servletMappingMaps = new HashMap<String, Object>();
}
NodeList childNodes = config.getChildNodes();
ServletMapping servletMapping = new ServletMapping();
for (int j = 0; j < childNodes.getLength(); j++) {
Node node = childNodes.item(j);
if (null != node && node.getNodeType() == Node.ELEMENT_NODE) {
String nodeName2 = node.getNodeName();
String textContent = node.getTextContent();
if ("servlet-name".equals(nodeName2)) {
servletMapping.setName(textContent);
} else if ("url-pattern".equals(nodeName2)) {
servletMapping.setUrl(textContent);
}
}
}
servletMappingMaps.put(servletMapping.getUrl(), servletMapping);
result.put(1, servletMappingMaps);
}
}
}
return result;
}
public static void main(String[] args) throws Exception {
System.out.println(parseWebXML());
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>cloud</servlet-name> <servlet-class>com.cloud.tomcat.servlet.CloudServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cloud</servlet-name> <url-pattern>/index</url-pattern> </servlet-mapping> </web-app>
运行结果:
将Server类运行起来,然后用浏览器输入:
http://localhost:8080/index或http://localhost:8080
得到如下结果: