解决了几个问题:
1.返回的响应信息可能是多行展示的,我们可以在传回的响应信息中用
代表\n,展示的时候通过replace方法将用\n代替
2.可以将响应信息直接写回,当响应信息内容较多时,也可以将响应信息放在根目录的专门装载信息的文件下,然后直接去读,就不用一遍一遍地拼接,返回
3.我们返回的响应信息展示的时候,很多时候不是单纯地展示,比如登录界面,会有输入账号密码的过程。我们可以在存响应信息的文件中放置一些特定的符号标记,代表动作,比如 请输入账号
所以我们解析信息的方法也对应修改
4.在解析信息时可能会遇到标签form,这几乎意味着我们的请求中包含了新的请求,我们需要将其剥离出来再次解析发送**
package browser;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;
import java.util.logging.Handler;
public class Browser {
private Scanner input=new Scanner(System.in);
private Socket socket=null;
private String ip;
private int port;
public void openBrowser(){//模拟打开浏览器
//输入一个URL统一资源定位符 ip:port/资源名?key=value&key=value
System.out.println("URL:");
String url=input.nextLine();
this.parseURL(url);
}
private void parseURL(String url){//解析URL,拆成三段
int colonIndex=url.indexOf(":");
int slashIndex=url.indexOf("/");
ip=url.substring(0,colonIndex);//获取ip地址
port=Integer.parseInt(url.substring(colonIndex+1,slashIndex));//获取port
String contentAndParams=url.substring(slashIndex+1);
this.createSocketAndSendRequest(ip,port,contentAndParams);
}
//创建一个socket,将contentAndParams发送给服务器
private void createSocketAndSendRequest(String ip,int port,String contentAndParams){
try {
//通过ip和port创建一个socket
socket=new Socket(ip,port);
//将contentAndParams发送出去(给服务器)
PrintWriter out=new PrintWriter(socket.getOutputStream());//需要一个高级流来发送
out.println(contentAndParams);
out.flush();
//浏览器等待响应消息
this.receiveResponseContent();
}catch (Exception e){
e.printStackTrace();
}
}
//设计一个方法,接收服务器会写的响应信息
private void receiveResponseContent(){
InputStream is = null;//最基本的字节流
try {
is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);//将字节流转化为字符流
BufferedReader br = new BufferedReader(isr);//包装成高级流,可以读取一行
String responseContent=br.readLine();
//解析响应信息并展示
this.parseResponseControllerAndShow(responseContent);
} catch (IOException e) {
e.printStackTrace();
}
}
//设计一个方法,解析响应信息并展示
private void parseResponseControllerAndShow(String responseContent){
String content=null;
HashMap<String,String> paramsMap=null;
responseContent=responseContent.replace("<br>","\n");
//解析其他标签
while (true) {
int lessThanIndex=responseContent.indexOf("<");
int greaterIndex=responseContent.indexOf(">");
if(lessThanIndex!=-1&&greaterIndex!=-1&&lessThanIndex<greaterIndex){
System.out.println(responseContent.substring(0,lessThanIndex));
//分析标签是什么类型,做相应的处理
String tag=responseContent.substring(lessThanIndex,greaterIndex+1);
if(tag.contains("input")){
String value=input.nextLine();
if(paramsMap==null){
paramsMap=new HashMap<>();
}
String[] str=tag.split(" ");
for(String keyAndValues: str){
if(keyAndValues.contains("=")){
String[] keys=keyAndValues.split("=");
if("name".equals(keys[0]))
paramsMap.put(keys[1].substring(1,keys[1].length()-1),value);
}
}
responseContent=responseContent.substring(greaterIndex+1);
}else if(tag.contains("form")){
if(paramsMap==null){
paramsMap=new HashMap<>();
}
String[] str=tag.split(" ");
for(String keyAndValues: str){
if(keyAndValues.contains("=")){
String[] keys=keyAndValues.split("=");
if("action".equals(keys[0]))
content=keys[1].substring(1, keys[1].length()-1);
}
}
responseContent=responseContent.substring(greaterIndex+1);
}
}else{//没有其他标签
System.out.println(responseContent);
break;
}
}
//至此将所有响应信息解析完毕,但如果在标签中遇到form标签,表示又有新的请求
this.sendNewRequest(content,paramsMap);
}
private void sendNewRequest(String content,HashMap<String,String> paramsMap){
if(content!=null){
StringBuilder url=new StringBuilder(ip);
url.append(":");
url.append(port);
url.append("/");
url.append(content);
if(paramsMap!=null){//证明新请求还有参数
url.append("?");
Iterator<String> iterator=paramsMap.keySet().iterator();
while (iterator.hasNext()){
String key=iterator.next();
String value=paramsMap.get(key);
url.append(key);
url.append("=");
url.append(value);
url.append("&");
}
//循环结束后多一个&,我们需要将其去除
url.delete(url.length()-1,url.length());
}
//发送信息给服务器
this.parseURL(url.toString());
}
}
}
package test;
import browser.Browser;
public class TestMain {
public static void main(String[] args){
new Browser().openBrowser();
}
}
package controller;
import com.sun.net.httpserver.HttpsServer;
import server.HttpServlet;
import server.HttpServletRequest;
import server.HttpServletResponse;
public class IndexController extends HttpServlet {
public void service(HttpServletRequest hsr, HttpServletResponse response) {
//传入的参数也是用于携带返回的信息的参数,例如之前学到的流,传入数组,返回值也在数组里
//1.获取请求发送过来携带的参数(可能没有)
System.out.println("控制层工作了");
response.sendRedirect("index.view");
//2.找到某一个业务层做事
//3.将最终业务层执行完毕的结果交还给服务器,服务器再将结果返回浏览器
}
}
package controller;
import server.HttpServlet;
import server.HttpServletRequest;
import server.HttpServletResponse;
public class LoginController extends HttpServlet {
public void service(HttpServletRequest hsr, HttpServletResponse response) {
String name= hsr.getParameter("name");//直接获取整个集合不如只获取我们需要的元素好
String password= hsr.getParameter("password");
System.out.println(name+"--------"+password);
}
}
package server;
public abstract class HttpServlet {//为了定义一个规则,方便服务器寻找,方法名字和方法参数必须统一
public abstract void service(HttpServletRequest hsr,HttpServletResponse response);
}
package server;
import java.util.HashMap;
public class HttpServletRequest {//为了存储浏览器发送请求时携带的所有信息
private String content;
private HashMap<String,String>map=new HashMap<>();
public String getParameter(String key){
return this.map.get(key);
}
public HttpServletRequest() {
}
public HttpServletRequest(String content, HashMap<String, String> map) {
this.content = content;
this.map = map;
}
public String getContent() {
return content;
}
public HashMap<String, String> getMap() {
return map;
}
public void setContent(String content) {
this.content = content;
}
public void setMap(HashMap<String, String> map) {
this.map = map;
}
}
package server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class HttpServletResponse {//用于接收响应回来的结果
private StringBuilder responseContent=new StringBuilder();
public void write(String str){
this.responseContent.append(str);
}
public void sendRedirect(String path){
try {
File file=new File("src//file//"+path);
FileReader fr=new FileReader(file);
BufferedReader br=new BufferedReader(fr);
String value=br.readLine();
while (value!=null){
this.responseContent.append(value);
value= br.readLine();
}
}catch (Exception e){
e.printStackTrace();
}
}
public String getResponseContent(){
return this.responseContent.toString();
}
}
package server;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public void startServer(){
try {
System.out.println("====启动服务====");
//自己创建服务
ServerSocket server=new ServerSocket(9999);
while (true){
//等待某一个客户端过来连接
Socket socket=server.accept();//要想同时很多用户访问,可以用多线程来控制
new ServerHandler(socket).start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
package server;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
public class ServerHandler extends Thread {
private Socket socket;
public ServerHandler(Socket socket) {
this.socket = socket;
}
public void run() {
this.receiveRequest();
}
private void receiveRequest() {//读取消息
try {
InputStream is = socket.getInputStream();//最基本的字节流
InputStreamReader isr = new InputStreamReader(is);//将字节流转化为字符流
BufferedReader br = new BufferedReader(isr);//包装成高级流,可以读取一行
//读取消息 资源名?key=value&key=value
String contentAndParams = br.readLine();
//调用一个方法解析读取过来的信息
this.parseContentAndParams(contentAndParams);
} catch (Exception e) {
e.printStackTrace();
}
}
private void parseContentAndParams(String contentAndParams) {//解析
//资源名?key=value&key=value
HashMap<String, String> map = new HashMap<>();
String content = null;
int questionMarkIndex = contentAndParams.indexOf("?");
if (questionMarkIndex != -1) {
content = contentAndParams.substring(0, questionMarkIndex);
String params = contentAndParams.substring(contentAndParams.indexOf("?") + 1);
String[] kv = params.split("&");
for (String str : kv) {
String[] values = str.split("=");
map.put(values[0], values[1]);
}
} else {
//没有携带参数,请求发过来的信息就是完整的资源名
content = contentAndParams;
}
//自己创建两个对象,一个用于包含所有携带的信息,一个用于接收响应回来的结果
HttpServletRequest hsr=new HttpServletRequest(content,map);
HttpServletResponse response=new HttpServletResponse();//空的
ServletController.findController(hsr,response);//执行完毕后response中就有响应的信息了
this.responseToBrowser(response);
}
private void responseToBrowser(HttpServletResponse response) {//将最终响应信息写回浏览器
try {
PrintWriter out=new PrintWriter(socket.getOutputStream());
out.println(response.getResponseContent());
out.flush();
//浏览器接收响应信息
}catch (Exception e){
e.printStackTrace();
}
}
}
package server;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
public class ServletController { //用于管理findController方法
//添加一个集合,存储被管理的所有Controller类对象
private static HashMap<String,HttpServlet>controllerObjectMap=new HashMap<>();//延迟加载
//2.每次找寻控制层的某个类都需要读取文件,性能较低,可以建立缓存机制
private static HashMap<String,String>controllerNameMap=new HashMap<>();//延迟加载
static {
Properties properties=new Properties();
try {
properties.load(new FileReader("src//web.properties"));
Enumeration enumeration=properties.propertyNames();
while(enumeration.hasMoreElements()){
String content=(String)enumeration.nextElement();
String realControllerName=properties.getProperty(content);
controllerNameMap.put(content,realControllerName);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//1.之所以抽离出来单独成为一个类是因为findController在ServerHandler中做的事与该类不符
public static void findController(HttpServletRequest hsr,HttpServletResponse response) {//找人做事---控制层
String content=hsr.getContent();
try {
//获取request对象中的请求名字(简单名,我们在内存中已经将简单名与类全名对应存好)
HttpServlet controllerObject=controllerObjectMap.get(content);
if(controllerObject==null){
//参考配置文件(缓存)
String realControllerName=controllerNameMap.get(content);
if(realControllerName!=null){
//反射获取类
Class clazz=Class.forName(realControllerName);
controllerObject=(HttpServlet)clazz.newInstance();
controllerObjectMap.put(content,controllerObject);
}
}
Class controllerClass=controllerObject.getClass();
Method method= controllerClass.getMethod("service",HttpServletRequest.class,HttpServletResponse.class);
method.invoke(controllerObject,hsr,response);
}catch (ClassNotFoundException e){
response.write("请求的"+content+"Controller不存在");
}catch (NoSuchMethodException e){
response.write("405 没有可以执行的方法");
}catch (Exception e){
e.printStackTrace();
}
}
//3.找寻Controller类是为了执行对应方法,所以我们让类中的方法有统一规则,便于查找使用
//4.发现Controller类与之前的Service、Dao类很像,几乎只有方法执行,没什么属性,所以我们可以让其变为单例模式
}
package test;
import server.Server;
public class TestMain {
public static void main(String[] args){
new Server().startServer();
}
}