Java 11 仿Tomcat服务器及postman模拟发送
package tomcat;
import java.util.HashMap;
import java.util.Map;
//请求
public class Request {
String method;//方式
String uri;//路由
String protocol;//协议
Map<String,String> heads;//请求头
String content;//请求体
//区别于static{} 普通方法
{
heads = new HashMap<>();
}
}
package tomcat;
import java.util.Map;
//响应
public class Response {
String protocol; //协议
int code; //状态码
String status;//状态描述
Map<String,String> heads;//头
{
heads = new HashMap<>();
}
String filename;//静态资源
//动态资源
OutputStream out;//仿照真实对象
}
面向对象技术 HttpServer
package tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
//仿Tomcat服务器
public class HttpServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8080);//Tomcat默认绑定8080端口 127.0.0.1:8080
while(true){//每次有客户端(浏览器)访问,就会产生一个Socket
final Socket socket = serverSocket.accept();
new Thread(() -> {//子线程 处理具体业务 new Thread(new Runnanber() 生成run方法,具体方法在run()里面,无法抛
try {//请求对象 //面向过程技术
byte[] buffer = new byte[1024];
InputStream is = socket.getInputStream();//一次读完
int len = is.read(buffer);
System.out.println("aaaaaaaaa"+len);
System.out.println(new String(buffer,0,len));
//响应对象
//固定回应一个<h1> 面向过程,弊端:过程内容多了就
OutputStream os = socket.getOutputStream();
os.write("HTTP/1.1 200 OK\r\n".getBytes());//请求行
os.write("Content-Type: text/html; charset=utf-8\r\n".getBytes());//请求头
os.write("\r\n\r\n".getBytes());
os.write("<h1>你好!世界</h1>".getBytes());//
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("服务器结束了!");
}
}
package tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//仿Tomcat服务器 面向对象技术
public class HttpServer2 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8080);
while(true){//每次有客户端(浏览器)访问,就会产生一个Socket
final Socket socket = serverSocket.accept();
new Thread(() -> {
try {//请求对象 //面向对象技术
Request request = parseRequest(socket);
Response response = parseResponse(socket,request);
sendBrower(request,response); //发送东西给浏览器
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("服务器结束了!");
}
//3.响应给浏览器
private static void sendBrower(Request request, Response response) {
OutputStream out = response.out;
//输出响应行
out.write((response.protocol + " " + response.code + " " + response.status +"\r\n").getBytes());
//输出响应头
Set<Map.Entry<String,String>> entries = response.heads.entrySet();
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry<String, String> entry= iterator.next();
out.write((entry.getKey()+": "+entry.getValue()+"\r\n").getBytes());
}
/*
for (Map.Entry<String, String> entry : entries) {
out.write((entry.getKey()+": "+entry.getValue()+"\r\n").getBytes());
}*/
out.write("\r\n".getBytes());//输出隔行
//out.write("<h1>首页</h1>".getBytes());一般不怎么写,所以删掉这行
//输出响应体 响应文件首页
if(response.filename !=null){
InputStream fis = new FileInputStream(response.filename);
//边读边写
byte[] buf = new byte[1024];
int len;
while ((len= fis.read(buf))!= -1){
out.write(buf,0,len);
}
fis.close();
}
}
//2.解析响应对象
private static Response parseResponse(Socket socket, Request request) {
Response response = new Response();
//解析响应行
response.protocol= request.protocol;
response.code=200;
response.status="OK";
//解析响应体
if("/".equals(request.uri)) request.uri="/index.html";
switch (request.uri){//动态路由有各个case管理,一般需要与数据库进行交流,才能得出结论
case "/login"://登录事件
break;
case "/register": //注册事件
break;
//静态路由由各个default管理
default:
int pos = request.uri.lastIndexOf('.');
if (pos != -1) {//如果找不到 =-1
String extension = request.uri.substring(pos);
if(".html".equals(extension)) response.heads.put("Content-Type", "text/html");
if(".ico".equals(extension)) response.heads.put("Content-Type", "image/x-icon");
if(".png".equals(extension)) response.heads.put("Content-Type", "image/png");
}
response.filename = request.uri.substring(1); // index.html
break;
}
return response;
}
//1.解析请求对象
private static Request parseRequest(Socket socket) throws IOException {
Request request = new Request();
byte[] buffer = new byte[1024*3];
int len =socket.getInputStream().read(buffer);
//底层的字节流打印 16进制
String strRequest = bytes2Str(buffer,len);
System.out.println(strRequest);
//请求的完整字符串
String strReq = new String(buffer,0,len);
System.out.println(strReq);
//解析请求行
int iRN1st = strReq.indexOf("\r\n"); //第一个\r\n的位置截取
String reqLine = strReq.substring(0, iRN1st);//请求行
System.out.println(reqLine.length());
String[] reqLineArr = reqLine.split(" ");
request.method = reqLineArr[0];
request.uri = reqLineArr[1];
request.protocol= reqLineArr[2];
//解析请求头
int iRNRN1st = strReq.indexOf("\r\n\r\n");
String reqHead = strReq.substring(iRN1st+2, iRNRN1st);//请求头 \r\n\r\n结尾
String[] reqHeadArr = reqHead.split("\r\n\r\n");
//遍历
for (String reqHeadItem : reqHeadArr) {
String[] reqHeadItemArr = reqHeadItem.split(": ");
request.heads.put(reqHeadItemArr[0],reqHeadItemArr[1]);
}
//解析请求体
request.content = strReq.substring(iRNRN1st+4);
return request;
}
/*
* 字节数组转字符串,配有右侧字符显示
* eg: A8H 30H 41H -> "A8 30 41 .0A"
*/
private static String bytes2Str(byte[] bts, int len) {
StringBuffer stringBuffer = new StringBuffer();
int line = 0;
StringBuffer sb = new StringBuffer();
for (int i=0; i< len; i++) {
stringBuffer.append(byte2Hex(bts[i]) + " ");
if (bts[i]>= 0x20 && bts[i] <= 0x7e) {
sb.append((char)bts[i]);
} else {
sb.append('.');
}
line++;
if (line == 8) {
stringBuffer.append(" ");
}
if (line >= 16) {
stringBuffer.append(" " + sb);
sb = new StringBuffer();
stringBuffer.append("\r\n");
line = 0;
}
}
if (sb.length() > 0) {
stringBuffer.append(" " + sb);
}
return stringBuffer.toString();
}
/*
* 字节转十六进制
* eg: A8H -> "A8"
*/
private static String byte2Hex(byte bt) {
char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int lo = bt & 0x0f;
int hi = (bt >> 4) & 0x0f;
return "" + hex[hi] + hex[lo];
}
}
1. postman 发送get请求
请求体content=null,请求方法为 get,以键值对的形式附到url中发送参数。
2. 普通表单post请求
以键值对的形式发送表单数据,参数名和参数值之间用=号连接,多个参数之间用&符号连接,同时表单参数携带到了url中。
3. 文件上传表单post请求
每次发送请求需要重新传入文件信息,数据处理为一条消息,以标签为单元,用分隔符分开