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>