手写服务器httpserver_封装分发器
前面所述的分装请求和响应其实都只是对单个请求有效,如果需要对多个请求有效的话,就需要加入多线程。
import java.io.IOException;
import java.net.ServerSocket;
public class MyServer {
private ServerSocket server ;
private static final String CRLF = "\r\n";
private static final String blank = " ";
private boolean flag = true;
private int i = 0;
public static void main(String[] args) {
new MyServer().start();
}
public void start(){
start(10001);
}
public void start(int port){
try {
server = new ServerSocket(port);
receive();
} catch (IOException e) {
// e.printStackTrace();
stop();
}
}
public void receive(){
try {
//请求及响应
while(flag){
new Thread(new Dispacher(server.accept())).start();
}
} catch (IOException e) {
// e.printStackTrace();
stop();
}
}
public void stop(){
try {
flag = false;
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
public class Servlet {
public void service(Request req,Response re) throws IOException{
re.println("<html><head><title>HTML响应实例</title>");
re.println("</head><body>");
re.println("欢迎").println(req.getParameterValue("uname"));
re.println("</body></html>");
}
}
import java.io.IOException;
import java.net.Socket;
/**
* 加入多线程,一个请求与响应,就一个对象
*/
public class Dispacher implements Runnable{
private Request req;
private Response re;
private Socket soc;
private int code;
public Dispacher(Socket soc) {
this.soc = soc;
try {
req = new Request(soc.getInputStream());
re = new Response(soc);
} catch (IOException e) {
// e.printStackTrace();
code = 500;
return ;
}
}
@Override
public void run() {
Servlet s = new Servlet();
try {
s.service(req, re);
re.pushToClient(code);
soc.close();
} catch (IOException e) {
// e.printStackTrace();
try {
re.pushToClient(500);
soc.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
手写服务器httpserver_多请求处理_多态
如果我们有不同的Servlet,这个时候就需要加入多态的概念,也就是根据不同的请求,用不用的Servlet服务,这里利用工厂模式:客户端发送请求至服务器,服务器启动并调用 Servlet,Servlet 根据客户端请求生成响应内容并将其传给服务器,服务器将响应返回客户端。
一、先封装接收和发送消息
package cn.feng.http_2;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
public class Request2 {
//请求方式
private String method;
//请求资源
private String url;
//请求参数
private Map<String,List<String>> parameterMapValues;
//内部
public static final String CRLF = "\r\n";
private InputStream is;
private String requestInfo;
public Request2(){
method = "";
url = "";
parameterMapValues = new HashMap<String,List<String>>();
requestInfo = "";
}
public Request2(InputStream is){
this();
this.is = is;
try {
byte[] data = new byte[204800];
int len = is.read(data);
requestInfo = new String(data,0,len);
} catch (IOException e) {
return;
}
//分析请求信息
parseRequestInfo();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
/**
* 分析请求信息
*/
private void parseRequestInfo(){
if((null==requestInfo) || (requestInfo=requestInfo.trim()).equals("")){
return;
}
/**
* ====================================
* 从信息的首行分解出:请求方式 请求路径 请求参数(get可能存在)
* 如:GET /index.html?uname=intputUname&pwd=inputPassword HTTP/1.1
*
* 如果为post方式,请求参数可能在最后正文中
* ====================================
*/
String paramString = "";//接收请求参数
//1、获取请求方式
String firstLine = requestInfo.substring(0,requestInfo.indexOf(CRLF));
int idx = requestInfo.indexOf("/");// /的位置
this.method = firstLine.substring(0,idx).trim();
String urlStr = firstLine.substring(idx,firstLine.indexOf("HTTP/")).trim();
if(this.method.equalsIgnoreCase("post")){//post方式
this.url = urlStr;
paramString = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
}else if(this.method.equalsIgnoreCase("get")){//get方式
if(urlStr.contains("?")){
String[] urlArray = urlStr.split("\\?");
this.url = urlArray[0];
paramString = urlArray[1];//接收请求参数
}else{
this.url = urlStr;
}
}
//2、将请求参数封装到Map中
parseParams(paramString);
}
/**
* 将请求参数封装到Map中
* @param paramString
*/
private void parseParams(String paramString){
//分割,将字符串转成数组
StringTokenizer token = new StringTokenizer(paramString,"&");
while(token.hasMoreTokens()){
String keyValue = token.nextToken();
String[] keyValues = keyValue.split("=");
if(keyValues.length == 1){
keyValues = Arrays.copyOf(keyValues, 2);
keyValues[1] = null;
}
String key = keyValues[0].trim();
String value = null==keyValues[1]?null:decode(keyValues[1].trim(),"gbk");
//分拣,转换成Map
if(!parameterMapValues.containsKey(key)){
parameterMapValues.put(key, new ArrayList<String>());
}
List<String> values = parameterMapValues.get(key);
values.add(value);
}
}
/**
* 解决中文
* @param value
* @param code
* @return
*/
private String decode(String value,String code){
try {
return java.net.URLDecoder.decode(value, code);
} catch (UnsupportedEncodingException e) {
//e.printStackTrace();
}
return null;
}
/**
* 根据页面的name获取对应的多个值
*/
public String[] getParameterValues(String name){
List<String> values = null;
if( (values=parameterMapValues.get(name))==null ){
return null;
}else{
return values.toArray(new String[0]);
}
}
/**
* 根据页面的name获取对应的单个值
*/
public String getParameterValue(String name){
String[] values = getParameterValues(name);
if(null==values){
return null;
}
return values[0];
}
}
package cn.feng.http_2;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;
/**
* 封装响应信息
*/
public class Response2 {
//两个常量
public static final String CRLF = "\r\n";
public static final String BLANK = " ";
//流
private BufferedWriter bw;
//正文
private StringBuilder content;
//存储头信息
private StringBuilder headInfo;
//存储正文长度
private int len = 0;
public Response2(){
headInfo = new StringBuilder();
content = new StringBuilder();
len = 0;
}
public Response2(OutputStream os){
this();
bw = new BufferedWriter(new OutputStreamWriter(os));
}
public Response2(Socket client){
this();
try {
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
} catch (IOException e) {
headInfo = null;
}
}
/**
* 构建正文
*/
public Response2 print(String info){
content.append(info);
len += (info + CRLF).getBytes().length;
return this;
}
/**
* 构建正文+回车
*/
public Response2 println(String info){
content.append(info).append(CRLF);
len += (info + CRLF).getBytes().length;
return this;
}
/**
* 构建响应头
*/
private void createHeadInfo(int code){
//1)HTTP协议版本、状态代码、描述
headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
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);
//2)响应头(Response Head)
headInfo.append("Server:test Server/0.0.1").append(CRLF);
headInfo.append("Date:").append(new Date()).append(CRLF);
headInfo.append("Content-type:text/html;charset=GBK").append(CRLF);
//正文长度:字节长度
headInfo.append("Content-type:").append(len).append(CRLF);
headInfo.append(CRLF);
}
/**
* 推送到客户端
* @throws IOException
*/
public void pushToClient(int code) throws IOException{
if(null==headInfo){
code = 500;
}
createHeadInfo(code);
//头信息+分割符
bw.append(headInfo.toString());
//正文
bw.append(content.toString());
bw.flush();
bw.close();
}
}
二、封装不同的小服务器Servlet,利用多态,这样就可以通过父类去调用子类
import java.io.IOException;
public abstract class Servlet2 {
public void service(Request2 req,Response2 re) throws IOException{
doGet(req,re);
doPost(req,re);
}
public abstract void doGet(Request2 req,Response2 re) throws IOException;
public abstract void doPost(Request2 req,Response2 re) throws IOException;
}
package cn.feng.http_2;
import java.io.IOException;
public class CopyOfLoginServlet extends Servlet2{
@Override
public void doGet(Request2 req, Response2 re) throws IOException {
String name = req.getParameterValue("uname");
String pwd = req.getParameterValue("pwd");
if(login(name, pwd)){
re.println("登录成功");
}else{
re.println("登录失败");
}
}
private boolean login(String name,String pwd){
return (name.equals("123"))&&(pwd.equals("456"));
}
@Override
public void doPost(Request2 req, Response2 re) throws IOException {
}
}
package cn.feng.http_2;
import java.io.IOException;
public class RegisterServlet extends Servlet2{
@Override
public void doGet(Request2 req, Response2 re) throws IOException {
}
@Override
public void doPost(Request2 req, Response2 re) throws IOException {
re.println("<html><head><title>返回注册</title>");
re.println("</head><body>");
re.println("你的用户名为").println(req.getParameterValue("uname"));
re.println("</body></html>");
}
}
三、主要工作:利用Map键与值之间的关系和多态来实现可以根据不同的请求实现不同的Servlet
package cn.feng.http_2;
import java.util.HashMap;
import java.util.Map;
public class ServletContext {
//为每一个Servlet取一个别名
//如:login-->LoginServlet,也就是本来可以通过login来调用LoginServlet
private Map<String,Servlet2> servlet;
//表示映射,也就是为login取一个别名 也就是url-->login
//这样就可以通过很多个路径来获取login,因为Map的值是可以重复的
private Map<String,String> mapping;
public ServletContext() {
servlet = new HashMap<>();
mapping = new HashMap<>();
}
public Map<String, Servlet2> getServlet() {
return servlet;
}
public void setServlet(Map<String, Servlet2> servlet) {
this.servlet = servlet;
}
public Map<String, String> getMapping() {
return mapping;
}
public void setMapping(Map<String, String> mapping) {
this.mapping = mapping;
}
}
package cn.feng.http_2;
import java.util.Map;
import cn.feng.http_1.Servlet;
public class WebApp {
private static ServletContext contxt;
static{
contxt = new ServletContext();
Map<String,String> mapping = contxt.getMapping();
mapping.put("/login", "login");
mapping.put("/log", "login");
mapping.put("/reg", "register");
Map<String,Servlet2> servlet = contxt.getServlet();
servlet.put("login", new CopyOfLoginServlet());
servlet.put("register", new RegisterServlet());
}
public static Servlet2 getServlet(String url){
if(null==url || (url=url.trim()).equals("")){
return null;
}
return contxt.getServlet().get(contxt.getMapping().get(url));
}
}
四、利用多线程实现对Servlet的调用
package cn.feng.http_2;
import java.io.IOException;
import java.net.Socket;
public class Dispacher2 implements Runnable{
private Request2 req;
private Response2 re;
private Socket soc;
private int code=200;
public Dispacher2(Socket soc) {
this.soc = soc;
try {
req = new Request2(soc.getInputStream());
re = new Response2(soc);
} catch (IOException e) {
// e.printStackTrace();
code = 500;
return ;
}
}
@Override
public void run() {
try {
//利用多态得到相应的Servlet,可以理解为Servlet2 ser = LoginServlet,或者Servlet2 ser = RegisterServlet
//然后下面就是执行不用Servlet的功能
Servlet2 ser = WebApp.getServlet(req.getUrl());
if(null==ser){
code = 404;
}else{
ser.service(req, re);
}
re.pushToClient(code);
} catch (IOException e) {
// e.printStackTrace();
this.code = 500;
try {
re.pushToClient(code);
} catch (IOException e1) {
// e1.printStackTrace();
}
}
try {
soc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.feng.http_2;
import java.io.IOException;
import java.net.ServerSocket;
public class MyServer2 {
private ServerSocket server ;
private static final String CRLF = "\r\n";
private static final String blank = " ";
private boolean flag = true;
public static void main(String[] args) {
new MyServer2().start();
}
public void start(){
start(10001);
}
public void start(int port){
try {
server = new ServerSocket(port);
receive();
} catch (IOException e) {
// e.printStackTrace();
stop();
}
}
public void receive(){
try {
while(flag){
new Thread(new Dispacher2(server.accept())).start();
}
} catch (IOException e) {
// e.printStackTrace();
stop();
}
}
public void stop(){
try {
flag = false;
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}