JAVA-手写服务器

花了3天时间搞了这么个服务器程序,基于Socket与Thread等JAVA基本东西.

基本知识.

HTTP协议,这个是浏览器与服务器交互的"暗语".一般常见的交互方式是Get/Post

下面是请求文件信息Request:

GET /index.html?uname=11111111111111111&pwd=22222222222222 HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8


POST /index.html HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 41
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8

uname=11111111111111111&pwd=2222222222222

get传输信息比较少,直接跟在文件名后,post 传输信息比较多,在文件头后面.每一行都有一个分隔符'\r\n'

服务器响应格式Response:

HTTP/1.1 200 OK
Content-Length	47
Content-Type	text/html;charset=GBK
Date	31 Mar 2015 06:12:22 GMT
Server	MyServer Server/0.0.1
接下来是一个CRLF分隔符,再就是文本信息

二.系统框架

简单来说就是服务器接受信息,看请求的是谁,则转给那个servlet处理.

servlet处理就是根据响应类型写好头信息,+CRLF+正文,发送.

为了服务多个,则引入多线程,为了提供不同的服务则要先将接收到的头信息做处理,看是哪种类型的然后分别处理.

下面是图示:

使用表单交互:

JAVA-手写服务器_第1张图片

Server:

package MyServer;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Date;

/*
 * 响应
 */
public class Server {
	public final static int port = 8888;
	public final static String CRLF = "\r\n";
	private ServerSocket serversocket;
	private boolean isRunning=true;
	public static void main(String[] args) {
		new Server().start();
	}
	public void start(){
		start(8888);//没有参数则调用有参的,还有另一种就是有参的this()其他
	}
	public void start(int port){
		try {
			serversocket = new ServerSocket(port);
			receive();//启动ServerSocket服务,并接收
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			stop();
		}
		
	}
	private void receive() {
		// TODO Auto-generated method stub
		try {
			while(true){
				Socket client = serversocket.accept();
				new Thread(new Dispatcher(client)).start();// Dispatcher启动多线程处理
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			stop();
		}
	}
	private void stop(){
		isRunning = false;
		CloseUtil.closeServerSocket(serversocket);
	}
}


Dispatcher类:

package MyServer;
import java.net.Socket;


public class Dispatcher implements Runnable{
	private Request  req ;
	private Response rep ;
	private Servlet  servlet;
	private Socket socket;
	private int code;//Request接收请求,Response发送响应.Servlet处理请求 code 200 正常 404 没有页面 500 服务器异常
	public Dispatcher(Socket client){
		this.socket = client;
		req = new Request(client);
		rep = new Response(client);
		code = 200;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			servlet = new WebApp().getServlet(req.getUrl());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			code = 500;
		}
		try {
			servlet.doGet(req, rep, code);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
Request;类:

package MyServer;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


public class Request {
	private String method;//请求的方式
	private String url;//放请求的URL
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	private Map> parameterMapValue;//键值对 eg复选框  可能有多个
	public final static int port = 8888;
	public final static String CRLF = "\r\n";
	private String requestInfo;
	private BufferedInputStream bis;
	private Socket socket;
	public Request(){
		method = "";
		url = "";
		parameterMapValue = new HashMap>();
		requestInfo = "";
	}
	public Request(Socket socket){
		this();//
		this.socket = socket;
		try {
			bis = new BufferedInputStream(socket.getInputStream());
			byte [] data = new byte[20480];
			int len = bis.read(data);
			requestInfo = new String(data,0,len);//读入所有请求,然后分离URL.参数等(parseRequestInfo处理)
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		parseRequestInfo();
	}
	/*
	 * 请求参数
	 * 请求路径(get/post/other)
	 * 
	 */
	private void parseRequestInfo(){
		requestInfo = requestInfo.trim();
		if(null == requestInfo || requestInfo.equals(""))
			return;
		String paramString = "";
		String fitstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF));//读入第一句话
		String url = fitstLine.substring(fitstLine.indexOf("/"),fitstLine.indexOf("HTTP/")).trim();//按格式划分
		this.method = fitstLine.substring(0, fitstLine.indexOf("/")).trim();
		if(this.method.equalsIgnoreCase("post")){
			paramString = requestInfo.substring(requestInfo.indexOf(CRLF));
			this.url = url;
		}else if(this.method.equalsIgnoreCase("get")){
			//是否存在参数
			if(requestInfo.contains("?")){
				String [] urlArrary = url.split("\\?");//划分参数与网址
				this.url = urlArrary[0];
				paramString = urlArrary[1];
			}else{
				this.url = url;
			}
		}
		if(paramString.equals(""))
			return;
		parseParms(paramString);//接收参数
		
	}
	private void parseParms(String paramString){
		String [] param = paramString.split("&");
		for(String t:param){
			String key = t.substring(0,t.indexOf("=")).trim();
			String value = decode(t.substring(t.indexOf("=")+1).trim(),"gbk");//URL编码问题,解码放入
			if(!parameterMapValue.containsKey(key))
				parameterMapValue.put(key, new LinkedList());
			List temp = parameterMapValue.get(key);
			temp.add(value);//如果这里为空放入
			//System.out.println(value);
		}
	}
	
	public String[] getParmeterValues(String name){//Servlet应用获取参数
		List value = parameterMapValue.get(name);
		if(value == null)
			return null;
		return value.toArray(new String [0]);
	}
	public String getParmeter(String name){
		String [] value = getParmeterValues(name);
		if(null == value)
			return null;
		return value[0];
	}
	private static String decode(String value,String code){
		try {
			return java.net.URLDecoder.decode(value, code);//
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
		}
		return null;
	}
//	public static void main(String[] args) {
//		String str = " uname=11111111111111111&pwd=&bike=on&bike=on ";
//		new Request().parseParms(str);
//	}	
}

Response类:

package MyServer;


import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.sql.Date;

public class Response {
	public final static int port = 8888;
	public final static String CRLF = "\r\n";
	private StringBuilder headInfo;
	private StringBuilder content;
	private Socket socket;
	private PrintWriter pw;
	private int len;
	public Response(){
		headInfo = new StringBuilder();
		content  = new StringBuilder();
		len = 0;
	}
	public Response(Socket socket){
		this();
		try {
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public Response print(String str){
		content.append(str);
		len += str.getBytes().length;
		return this;
	}
	private void createHeadInfo(int code){
		headInfo = new StringBuilder();
		headInfo.append("HTTP/1.1 "+code+" ");
		switch(code){
			case 200:headInfo.append("OK");
				break;
			case 404:headInfo.append("NOT FOUND");
				break;
			case 500:headInfo.append("SERVER ERROR");
				break;
		}
		headInfo.append(CRLF);
		headInfo.append("Server:MyServer Server/0.0.1"+CRLF);
		headInfo.append("Date:"+(new Date(System.currentTimeMillis()).toGMTString())+CRLF);
		headInfo.append("Content-Type:text/html;charset=GBK"+CRLF);
		headInfo.append("Content-Length:"+len+CRLF);
		headInfo.append(CRLF);//这个特别注意
	}
	public void pustToClient(int code){
		createHeadInfo(code);
		pw.print(headInfo.toString()+content.toString());
		pw.flush();
	}
	public void close(){
		CloseUtil.closeAll(pw);
	}
}


Servlet类:

package MyServer;

public abstract class Servlet {
	public void service(Request req,Response rep,int code) throws Exception{
		this.doGet(req,rep,code);
	}
	public abstract void doGet(Request req,Response rep,int code) throws Exception;
	public abstract void doPost(Request req,Response rep,int code) throws Exception;
}
LoginServlet类:

package MyServer;

public class LoginServlet extends Servlet{

	@Override
	public void doGet(Request req, Response rep, int code) throws Exception {
		// TODO Auto-generated method stub
		rep.print("welcome ");
		rep.print(req.getParmeter("uname")+" back
"); rep.print(""); rep.pustToClient(code); } @Override public void doPost(Request req, Response rep, int code) throws Exception { // TODO Auto-generated method stub } }

CloseUtil类:

package MyServer;

import java.io.Closeable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class CloseUtil implements Closeable{

	@Override
	public void close() throws IOException {
		// TODO Auto-generated method stub
		
	}
	public static void closeAll(Closeable...io){
		for(Closeable t:io){
			if(null!=t)
				try {
					t.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
	public static void closeSocket(Socket socket){
		if(socket == null)
			return;
		try {
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void closeServerSocket(ServerSocket serversocket){
		if(serversocket == null)
			return;
		try {
			serversocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

然后就是用XML文件去配置Servlet与健壮性的问题了.

学习到的一些特性:

1.可变参,在CloseUtil中使用,要放在最后一位.继承Closeable各种流随便关.

2.反射技术在JAVA执行过程中动态生成类

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) throws Exception {
		Main x = new Child();
		Child c =(Child)x;
		System.out.println(x==c);
		String str = "aaa";
		Constructor con = str.getClass().getDeclaredConstructor(new Class[] {String.class});
		String test1;
		test1 = (String)con.newInstance(str);
		String test2;
		Class clz;
		clz = str.getClass();
		test2 = (String)clz.newInstance();
		String test3;
		clz = String.class;
		test3 = (String)clz.newInstance();
		String test4;
		clz = Class.forName("java.lang.String");
		test4 = (String)clz.newInstance();
		System.out.println(test1);
		System.out.println(test2 instanceof String);
		System.out.println(test3 instanceof String);
		System.out.println(test4 instanceof String);
	}
}
/*
 * Result:
 * true
 * aaa
 * true
 * true
 * true
 */
class Child extends Main {
	
}


3.画图真方便:点击打开链接

4.list.toArray() 

不带参数的方法,构造并返回一个Object数组对象,这时候向下转型为String数组对象,导致类型不兼容,报错。(本身就是Object,而不是String转到Object)

而带参数的方法,构造的数组对象类型和参数的类型一致,故不存在转型.

String[] array = new String[] {"A", "B"}; List list = Arrays.asList(array); 
List list = new ArrayList(); list.add("A");list.add("B");final int size =  list.size(); 
String[] array = (String[])list.toArray(new String[size]); 
5. Servlet的实现

双Map即 A->B  B->C  中间这个B满足了多对一,工厂模式

你可能感兴趣的:(JAVA)