html
<html>
<head>
<title>表单</title>
</head>
<body>
<form method="post" action="http://localhost:8888">
<!--id常用在js中,用于前端区分唯一性;name常用在后端区分唯一性-->
用户名:<input type="text" name="username" id="username"/>
密码:<input type="password" name="password" id="password"/>
<input type="submit" value="登录">
</form>
</body>
</html>
http协议
HTTP请求头和响应头含义
1、请求(客户端->服务端[request])
GET(请求的方式) /books/java.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
Accept: */*(客户端能接收的资源类型)
Accept-Language: en-us(客户端接收的语言类型)
Connection: Keep-Alive(维护客户端和服务端的连接关系)
Host: localhost:8080(连接的目标主机和端口号)
Referer: http://localhost/links.asp(从来于哪里)
User-Agent: Mozilla/4.0(客户端版本号的名字)
Accept-Encoding: gzip, deflate(客户端能接收的压缩数据的类型)
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(缓存时间)
Cookie(客户端暂存服务端的信息)
Date: Tue, 11 Jul 2000 18:23:51 GMT(客户端请求服务端的时间)
2、响应(服务端->客户端[response])
HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
302(客户端请求服务端,但服务端没有对应的资源,服务端要客户端再次请求找其它的服务端,即客户端二次请求,重定向)
307(客户端请求服务端,但服务端没有对应的资源,服务端自行再次请求找其它的服务端,即客户端一次请求,转发)
304(客户端请求服务端,此时客户端缓存中有,无需再从服务端下载新的内容,服务端叫客户端自行找缓存,优化)
500(客户端请求的资源,服务端存在,但在执行时出错)
Location: http://www.baidu.com(服务端需要客户端访问的页面路径)
Server:apache tomcat(服务端的Web服务端名)
Content-Encoding: gzip(服务端能够发送压缩编码类型)
Content-Length: 80(服务端发送的压缩数据的长度)
Content-Language: zh-cn(服务端发送的语言类型)
Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.it315.org(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked(分块传递数据到客户端)
Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
Expires: -1//3种(服务端禁止客户端缓存页面数据)
Cache-Control: no-cache(服务端禁止客户端缓存页面数据)
Pragma: no-cache(服务端禁止客户端缓存页面数据)
Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)
Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)
httpserver
/**
* 创建服务器,并启动
* 1、请求
* 2、响应
* @author L J
*/
public class Server {
private ServerSocket server;
//是否停止服务
private boolean isShutDown;
public static void main(String[] args) {
Server server = new Server();
server.start();
}
/**
* 启动服务器方法
*/
public void start() {
start(8888);
}
/**
* 启动服务器方法
*/
public void start(int port) {
try {
server = new ServerSocket(port);
this.receive();
} catch (IOException e) {
stop();
}
}
/**
* 接收客户端
*/
private void receive() {
try {
while(!isShutDown) {
new Thread(new Dispatcher(server.accept())).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止服务器方法
*/
public void stop() {
isShutDown = true;
CloseUtil.close(server);
}
}
/**
* 封装Request
* @author L J
*/
public class Request {
//请求方式
private String method;
//请求资源
private String url;
//请求参数
private Map<String, List<String>> parameterValue;
//换行符常量
public static final String CRLF = "\r\n";
//输入流
private InputStream is;
//请求信息
private String requestInfo;
public Request() {
method = "";
url = "";
parameterValue = new HashMap<String, List<String>>();
}
public Request(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();
}
/**
* 分析请求头信息
*/
private void parseRequestInfo() {
if(requestInfo == null || (requestInfo = requestInfo.trim()).equals("")) {
return;
}
/**
* 思路
* =======================================
* 从头信息中分析出:请求方式 请求路径 请求参数(可能不存在)
* 如果是POST请求方式,请求参数可能在最后的正文中
* =======================================
*/
//接收请求参数
String paramString = "";
//获取请求方式
String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF));
int index = requestInfo.indexOf("/"); // /的位置
this.method = firstLine.substring(0, index).trim();
String urlStr = firstLine.substring(index, firstLine.indexOf("HTTP/1.1")).trim();
//只考虑get和post请求方式
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;
}
}
//不存在请求参数
if(paramString.equals("")) {
return;
}
//将请求参数封装到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("=");
//如果有参数没有值,将值设为null
if(keyValues.length == 1){
keyValues = Arrays.copyOf(keyValues, 2);
keyValues[1] = null;
}
String key = keyValues[0].trim();
String value = keyValues[1] == null ? null : decode(keyValues[1].trim(), "utf-8");
//转换成Map 分拣存储
if(!parameterValue.containsKey(key)) {
parameterValue.put(key, new ArrayList<String>());
}
List<String> values = parameterValue.get(key);
values.add(value);
}
}
/**
* 根据页面的name获取对应的多个值
* @param name
* @return
*/
public String[] getParameterValues(String name) {
List<String> values = null;
if((values=parameterValue.get(name)) == null) {
return null;
}else{
return values.toArray(new String[0]);
}
}
/**
* 根据页面的name获取对应的一个值
* @param name
* @return
*/
public String getParameter(String name) {
String[] values = getParameterValues(name);
if(values == null) {
return null;
}
return values[0];
}
//解码,解决中文乱码问题
private String decode(String value, String code) {
try {
return URLDecoder.decode(value, code);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public String getUrl() {
return url;
}
}
/**
* 封装响应信息
* @author L J
*/
public class Response {
//存储响应头信息
private StringBuilder headInfo;
//存储响应正文信息
private StringBuilder content;
//输出流
private BufferedWriter bw;
//换行符常量
private static final String CRLF = "\r\n";
//空格常量
private static final String BLANK = " ";
//存储正文长度
private int len = 0;
public Response() {
headInfo = new StringBuilder();
content = new StringBuilder();
len = 0;
}
public Response(OutputStream os) {
this();
bw = new BufferedWriter(new OutputStreamWriter(os));
}
public Response(Socket client) {
this();
try {
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
} catch (IOException e) {
headInfo = null;
}
}
/**
* 构建正文
* @param info
* @return
*/
public Response print(String info) {
content.append(info);
len += info.getBytes().length;
return this;
}
/**
* 构建正文+回车
* @param info
* @return
*/
public Response 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)响应头
headInfo.append("Server:bjsxt 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-Length:").append(len).append(CRLF);
// 3)正文之前
headInfo.append(CRLF);
}
/**
* 推送到客户端
* @throws IOException
*/
void pushToClient(int code) throws IOException {
if(headInfo == null) {
code = 500;
}
createHeadInfo(code);
//头信息+分隔符
bw.append(headInfo.toString());
//正文
bw.append(content.toString());
bw.flush();
CloseUtil.close(bw);
}
}
/**
* 一个请求与一个响应 就一个此对象
* @author L J
*/
public class Dispatcher implements Runnable{
private Socket client;
//请求
private Request req;
//响应
private Response res;
//状态码
private int code;
public Dispatcher(Socket client) {
this.client = client;
try {
req = new Request(client.getInputStream());
res = new Response(client.getOutputStream());
} catch (IOException e) {
code = 500;
return;
}
}
@Override
public void run() {
try {
Servlet servlet = WebApp.getServlet(req.getUrl());
if(servlet == null) {
this.code = 404;
}else{
//调用服务
servlet.service(req, res);
}
//将响应信息输出到客户端
res.pushToClient(code);
} catch (Exception e) {
this.code = 500;
}
//关闭socket
CloseUtil.close(client);
}
}
/**
* 上下文容器
* @author L J
*/
public class ServletContext {
//为每一个Servlet取一个别名,一个Servlet可以有多条路径进行访问
//login-->com.belief.server.LoginServlet
private Map<String, String> servlet;
//映射 /log-->login /login-->login
private Map<String, String> mapping;
public ServletContext() {
servlet = new HashMap<String, String>();
mapping = new HashMap<String, String>();
}
public Map<String, String> getServlet() {
return servlet;
}
public void setServlet(Map<String, String> servlet) {
this.servlet = servlet;
}
public Map<String, String> getMapping() {
return mapping;
}
public void setMapping(Map<String, String> mapping) {
this.mapping = mapping;
}
}
/**
* 存储:
* <servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.belief.server.LoginServlet</servlet-class>
</servlet>
* @author L J
*/
public class Entity {
//Servlet的别名
private String name;
//Servlet的路径
private String clz;
public Entity() {
}
public Entity(String name, String clz) {
this.name = name;
this.clz = clz;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClz() {
return clz;
}
public void setClz(String clz) {
this.clz = clz;
}
}
/**
* 存储:
* <servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
* @author L J
*/
public class Mapping {
//Servlet的别名
private String name;
//要访问Servlet的url
private List<String> urlPattern;
public Mapping() {
urlPattern = new ArrayList<String>();
}
public Mapping(String name, List<String> urlPattern) {
this.name = name;
this.urlPattern = urlPattern;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getUrlPattern() {
return urlPattern;
}
public void setUrlPattern(List<String> urlPattern) {
this.urlPattern = urlPattern;
}
}
//SAX解析web.xml文件
public class WebHandler extends DefaultHandler{
//存储解析出来的Entity
private List<Entity> entityList;
//存储解析出来的Mapping
private List<Mapping> mappingList;
//Entity实体--><servlet></servlet>标签
private Entity entity;
//Mapping实体--><servlet-mapping></servlet-mapping>标签
private Mapping mapping;
//记录开始标签
private String beginTag;
//是否是在解析servlet-mapping
private boolean isMap;
@Override
public void startDocument() throws SAXException {
//文档解析开始
entityList = new ArrayList<Entity>();
mappingList = new ArrayList<Mapping>();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
//开始元素
if(qName != null) {
beginTag = qName;
if(qName.equals("servlet")) {
isMap = false;
entity = new Entity();
}else if(qName.equals("servlet-mapping")) {
isMap = true;
mapping = new Mapping();
}
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
//处理内容
if(beginTag != null) {
String str = new String(ch, start, length);
if(isMap) {
if(beginTag.equals("servlet-name")) {
mapping.setName(str);
}else if(beginTag.equals("url-pattern")) {
mapping.getUrlPattern().add(str);
}
}else{
if(beginTag.equals("servlet-name")) {
entity.setName(str);
}else if(beginTag.equals("servlet-class")) {
entity.setClz(str);
}
}
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
//结束元素
if(qName != null) {
if(qName.equals("servlet")) {
entityList.add(entity);
}else if(qName.equals("servlet-mapping")) {
mappingList.add(mapping);
}
}
beginTag = null;
}
@Override
public void endDocument() throws SAXException {
//文档解析结束
}
public List<Entity> getEntityList() {
return entityList;
}
public void setEntityList(List<Entity> entityList) {
this.entityList = entityList;
}
public List<Mapping> getMappingList() {
return mappingList;
}
public void setMappingList(List<Mapping> mappingList) {
this.mappingList = mappingList;
}
}
//模拟应用
public class WebApp {
private static ServletContext context;
static {
try {
//获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//获取解析器
SAXParser sax = factory.newSAXParser();
//指定xml+处理器
WebHandler web = new WebHandler();
sax.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("WEB-INF/web.xml"), web);
context = new ServletContext();
//将List转成Map
Map<String,String> mapp = context.getMapping();
Map<String,String> servlet = context.getServlet();
//servlet-name servlet-class
for(Entity entity : web.getEntityList()) {
servlet.put(entity.getName(), entity.getClz());
}
//url-pattern servlet-name
for(Mapping mapping : web.getMappingList()) {
List<String> urls = mapping.getUrlPattern();
for(String url : urls) {
mapp.put(url, mapping.getName());
}
}
} catch (Exception e) {
}
}
public static Servlet getServlet(String url) throws Exception {
//如果没有url或url==null
if(url == null && (url = url.trim()).equals("")) {
return null;
}
//利用反射根据字符串(完整路径)创建对象
//return context.getServlet().get(context.getMapping().get(url));
String name = context.getServlet().get(context.getMapping().get(url));
return (Servlet)Class.forName(name).newInstance();
}
}
/**
* 关闭流工具类
*
* @author L J
*/
public class CloseUtil {
// 关闭流
public static void close(Closeable... io) {
for (Closeable temp : io) {
if (temp != null) {
try {
temp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 关闭Socket
public static void close(Socket socket) {
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭ServerSocket
public static void close(ServerSocket serverSocket) {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public abstract class Servlet {
//服务端的处理
public void service(Request req, Response res) throws Exception {
this.doGet(req, res);
this.doPost(req, res);
}
protected abstract void doGet(Request req, Response res) throws Exception;
protected abstract void doPost(Request req, Response res) throws Exception;
}
//登录
public class LoginServlet extends Servlet{
@Override
public void doGet(Request req, Response res) throws Exception {
String username = req.getParameter("username");
String password = req.getParameter("password");
boolean flag = login(username, password);
if(flag) {
res.println("登录成功!");
}else{
res.println("登录失败!");
}
}
public boolean login(String username, String password) {
return username.equals("哈哈") && password.equals("123456");
}
@Override
public void doPost(Request req, Response res) throws Exception {
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
<!-- 登录 -->
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.belief.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
<url-pattern>/log</url-pattern>
</servlet-mapping>
<!-- 注册 -->
<servlet>
<servlet-name>register</servlet-name>
<servlet-class>com.belief.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>register</servlet-name>
<url-pattern>/register</url-pattern>
</servlet-mapping>
</web-app>