查看http响应报文:谷歌浏览器——>右键——>检查——>Network——>ALL——>Name
public int available() throws IOException
InputStream
的某些实现将返回流中的字节总数,但许多实现则不会public InputStream getInputStream() throws IOException
返回此套接字的输入流,如果此套接字具有相关联的通道,则所得到的输入流将其所有操作委派给通道
如果通道处于非阻塞模式,则输入流的read
操作将抛出IllegalBlockingModeException
报文信息
General(请求的url路径)
Request URL: http://localhost:10000/
Request Method: POST
Status Code: 200 OK
Remote Address: [::1]:10000
Referrer Policy: no-referrer-when-downgrade
Response Headers(服务器响应信息)
Access-Control-Allow-Headers: *
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Keep-Alive: timeout=10
Transfer-Encoding: chunked
Request Headers(请求头)
Provisional headers are shown
Accept: text/plain, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://127.0.0.1:8848
Referer: http://127.0.0.1:8848/JqueryTest/socket_index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Form Data(请求参数)
action: A
报文响应内容
// 设置Content-Length方式
response.append("HTTP/1.1 200 OK\r\n");
response.append("Access-Control-Allow-Origin:"+this.allowOrigin+"\r\n");
response.append("Access-Control-Allow-Credentials:true\r\n");
response.append("Access-Control-Allow-Headers:*\r\n");
response.append("Content-Type: text/html; charset=utf-8\r\n");
response.append("Connection: keep-alive\r\n");
response.append("Keep-Alive: timeout=30\r\n");
//内容
String info = this.resultMessage;
//Content-Length表明了实体主体部分的大小,不能大也不能小,单位是字节
response.append("Content-Length:"+info.getBytes("utf-8").length+"\r\n\r\n");
//头部信息完后用"\r\n\r\n"分隔,然后正文内容
response.append(info);
// 设置Transfer-Encoding方式
response.append("HTTP/1.1 200 OK\r\n");
response.append("Access-Control-Allow-Origin:"+this.allowOrigin+"\r\n");
// response.append("Access-Control-Allow-Origin:http://127.0.0.1:8848\r\n");
response.append("Access-Control-Allow-Credentials:true\r\n");
response.append("Access-Control-Allow-Headers:*\r\n");
// response.append("Accept-Ranges:bytes\r\n");
response.append("Content-Type: text/html; charset=utf-8\r\n");
response.append("Connection: keep-alive\r\n");
response.append("Keep-Alive: timeout=30\r\n");
response.append("Transfer-Encoding:chunked\r\n\r\n");
String info = this.resultMessage;
// 报文分段,字节大小占用一行,然后具体内容占用一段,注意"\r\n"
response.append(Integer.toHexString(info.getBytes().length)+"\r\n");
response.append(info+"\r\n");
// 结尾标识
response.append("0\r\n\r\n");
html源码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>
// Get请求方式
function sendGet(param) {
var url = "http://127.0.0.1:8080/queryid";
if(param!=null){
url +="?"+param;
}
// 参数需要转义
url = encodeURI(url);
// 创建XMLHttpRequest
var req = new XMLHttpRequest();
// 创建与服务器的连接
req.open("GET", url, true);
// 返回结果的处理函数
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
var tip = document.getElementById("tip");
var res = ""
;
res +=req.responseText;
// res += decodeURIComponent(req.responseText);
res += "
";
tip.innerHTML = res;
}
};
// 发生错误的处理函数
req.onerror = function(){
alert("请求错误");};
// 设置跨域请求提供凭据信息
req.withCredentials = true;
req.send();
}
function sendPost(param){
var url = "http://127.0.0.1:8080/querycontent";
var req = new XMLHttpRequest();
req.open("POST",url,true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
var tip = document.getElementById("tip");
var res = ""; // res += req.responseText; // res += decodeURIComponent(req.responseText); res += req.responseText; res += ""; tip.innerHTML = res; } }; // req.withCredentials = true; req.onerror = function(){ alert("请求错误");}; req.send(param); } function dealRequest(obj){ var elem = document.getElementsByClassName("socketName"); var name=elem[obj].getAttribute("name"); // alert(typeof name); var param = null; if(elem[obj].value!=""){ param = name; param +="="; param += elem[obj].value; param +="&msg=附加信息"; } if(obj==0){ sendPost(param); }else{ sendGet(param); } } </script> <p>查询方式 查找内容:<input type="text" value="Java" placeholder="找关键字:如java" class="socketName" name="content"> <input type="button" onclick="dealRequest(0)" value="查询"> ID编号:<input type="number" value="101" placeholder="101~107" class="socketName" name="id"> <input type="button" onclick="dealRequest(1)" value="查询"> <div id="tip" style="color:blue"></div> </body> </html>
java源码
package socket;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RequestMapping {
String name() default "other";
String value() default "没有请求对应的处理方法,回复默认请求消息";
}
/**
* @author wonzeng
* 2020年7月13日
*/
class MyController {
HashMap<String, Object> mp = new HashMap<String, Object>();
public MyController() {
mp.put("101", "Java实例内部类、匿名内部类、静态内部类和局部内部类使用方法");
mp.put("102", "Java关于向上转型与向下转型的区别");
mp.put("103", "Java关于final关键字的使用方法");
mp.put("104", "Java匿名对象与对象引用");
mp.put("105", "Java抽象方法与抽象类和接口的区别");
mp.put("106", "Java关于静态代码块、构造代码块和局部代码块的区别");
mp.put("107", "Java关于修饰符public、private和protected的访问权限");
System.out.println("MyController.MyController()执行了构造方法");
}
@RequestMapping
public String defaultRequest() {
return "没有查询到相关内容,请求时间:"
+LocalDateTime.now().toString()+"";
}
// 根据id查找
@RequestMapping(name = "/queryid")
public String queryById(Map<String, Object> param) {
String temp = (String) mp.getOrDefault(param.get("id"), "");
if (temp.equals("")) {
return defaultRequest();
}
return temp;
}
// 根据content查找
@RequestMapping(name = "/querycontent")
public String queryByContent(Map<String, Object> param) {
String str = param.get("content").toString();
Collection<Object> lst = mp.values();
StringBuilder builder = new StringBuilder("");
lst.forEach((e) -> {
String p = e.toString();
if (p.toLowerCase().contains(str.toLowerCase())) {
builder.append(p + "\r\n");
}
});
if (builder.toString() != "") {
return builder.toString();
} else {
return defaultRequest();
}
}
}
/**
* @author wonzeng
* 2020年7月13日
*/
public class HttpService {
private int port = 8080;
private int threadSize = 5;
private ServerSocket serverSocket = null;
private ExecutorService fixedPool = null;
public static void main(String[] args) {
new HttpService();
}
//测试内容
// public static void main(String[] args) throws InstantiationException, IllegalAccessException, UnsupportedEncodingException, NoSuchMethodException, SecurityException {
MyController p = new MyController();
// Class cls = MyController.class;
// MyController cont = (MyController) cls.newInstance();
// Method []methods = cls.getDeclaredMethods();
// String res = "";
// HashMap paramObj =new HashMap();
// String noteName="/querycontent";
// paramObj.put("content", "");
// for(Method md:methods) {
//
// if(md.isAnnotationPresent(RequestMapping.class)) {
// RequestMapping req = md.getAnnotation(RequestMapping.class);
读取类上的注解名称
// if(req.name().equals(noteName)) {
InvocationTargetException
// try {
// System.out.println("方法名称:"+md.getName());
// res = (String) md.invoke(cont, paramObj);
// System.out.println("执行结果:"+res);
// }catch(InvocationTargetException e) {
// System.out.println("HttpService.StreamProcess.controllerTool()==========================");
continue;
//
// }
// }
// }
// }
没有找到对应的方法
// if(res.equals("")) {
// Method md = cls.getMethod("defaultRequest", null);
使用注解的默认值
// RequestMapping mp = md.getAnnotation(RequestMapping.class);
// res = mp.value();
//
// }
// String info = URLEncoder.encode(res, "UTF-8");
// System.out.println("HttpService.main():"+info);
new HttpService();
// }
/**
* @author wonzeng
* 2020年7月13日
*/
public class StreamProcess implements Runnable {
private Socket socket = null;
private String requestHead = "";
private HashMap<String, Object> params = null;
private String url = null;
private String resultMessage = null;
private MyController cont = null;
private String allowOrigin = "http://127.0.0.1:8848";
public StreamProcess(Socket socket) {
this.socket = socket;
this.params = new HashMap<String, Object>();
}
@Override
public void run() {
readRequestStream();
responseStream();
}
// 读取请求头信息
public void readRequestStream() {
try {
InputStream in = socket.getInputStream();
int byteSize = in.available();
byte[] arr = new byte[byteSize];
in.read(arr);
String msg = new String(arr, "utf-8");
if (msg.length() > 0) {
this.requestHead = msg;
System.out.println("开始解析头部信息!");
// 解析头部信息
parseHeaders(this.requestHead);
// 回复内容
controllerTool();
} else {
System.out.println("请求头部信息为空!");
}
} catch (IOException e) {
System.out.println(e);
}
}
// 反射读取注解,调用对应方法,返回字符串
@SuppressWarnings("unused")
private void controllerTool() {
try {
String noteName = "";
// 请求没有指定路径或只有 "/",没有请求参数
if (!this.url.startsWith("/") || this.url.equals("/")) {
noteName = "defaultRequest";
} else {
// 有指定请求比如 /query
noteName = this.url.substring(this.url.lastIndexOf("/"));
// 请求参数为空,指定方法不能执行,否则空指针异常
if(this.params.size()==0) {
noteName = "defaultRequest";
}
}
System.out.println("请求方法名:" + noteName);
Class<MyController> cls = MyController.class;
if (cont == null) {
cont = cls.newInstance();
}
Method[] methods = cls.getDeclaredMethods();
String res = "";
for (Method md : methods) {
// 判断方法上是否有注解
if (md.isAnnotationPresent(RequestMapping.class)) {
RequestMapping req = md.getAnnotation(RequestMapping.class);
// 读取类上的注解名称
if (req.name().equals(noteName)) {
System.out.println("注解名称:" + req.name());
System.out.println("实际名称:" + noteName);
System.out.println("参数:" + this.params.get("content") + "\t" + this.params.get("id"));
System.out.println("方法名称:" + md.getName());
res = (String) md.invoke(cont, this.params);
System.out.println("执行结果:" + res);
}
}
}
// 没有找到对应的方法
if (res.equals("")) {
Method md = cls.getMethod("defaultRequest", null);
// 使用注解的默认值
RequestMapping mp = md.getAnnotation(RequestMapping.class);
System.out.println("默认方法:"+mp.value());
res = (String) md.invoke(cont, null);
}
System.out.println("返回的结果:" + res);
this.resultMessage = res;
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException
| IllegalArgumentException | InvocationTargetException e) {
System.out.println(e);
}
}
//Content-Type类型
// Content-Type: text/plain
// 请求返回字符串可用:Content-Type:text/html; charset=utf-8
// 请求图片jpg、jpeg图片:Content-Type:image/jpeg
// 请求gif图片:Content-Type:image/gif
// Content-Type:application/octet-stream
//浏览器直接输入地址访问,请求头不一样:
// GET /favicon.ico HTTP/1.1
// Host: 127.0.0.1:8080
// Connection: keep-alive
// User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
// Accept: image/webp,image/apng,image/*,*/*;q=0.8
// Sec-Fetch-Site: same-origin
// Sec-Fetch-Mode: no-cors
// Referer: http://127.0.0.1:8080/
// Accept-Encoding: gzip, deflate, br
// Accept-Language: zh-CN,zh;q=0.9
// Cookie: __guid=96992031.2219997149635148000.1585503560511.6748; monitor_count=19
//post请求方式:
// POST / HTTP/1.1
// Host: 127.0.0.1:8080
// Connection: keep-alive
// Content-Length: 0
// Origin: http://127.0.0.1:8848
// User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
// Content-Type: application/x-www-form-urlencoded;charset=utf-8
// Accept: */*
// Referer: http://127.0.0.1:8848/JqueryTest/socket/location.html
// Accept-Encoding: gzip, deflate, br
// Accept-Language: zh-CN,zh;q=0.9
//get请求方式
// GET / HTTP/1.1
// Host: 127.0.0.1:8080
// Connection: keep-alive
// Origin: http://127.0.0.1:8848
// User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
// Accept: */*
// Referer: http://127.0.0.1:8848/JqueryTest/socket/location.html
// Accept-Encoding: gzip, deflate, br
// Accept-Language: zh-CN,zh;q=0.9
// 解析请求头部信息
public void parseHeaders(String headers) {
System.out.println("解析的头部信息********************:"+headers);
if(headers==null || headers.equals("")) {
return;
}
// 跨源请求可能没有
int oripos1 = headers.indexOf("Origin");
if(oripos1!=-1) {
int oripos2 = headers.indexOf("\r\n",oripos1);
this.allowOrigin = headers.substring(oripos1,oripos2);
this.allowOrigin = this.allowOrigin.split(" ")[1];
System.out.println("请求源:"+this.allowOrigin);
}
String firstLine = headers.substring(0, headers.indexOf("\r\n"));
String[] arr = firstLine.split(" ");
// get请求方式
String methods = arr[0].toLowerCase();
if (methods.equals("get")) {
int pos = arr[1].indexOf("?");
// 包含请求参数
if (pos != -1) {
this.url = arr[1].substring(0, pos);
String ps = arr[1].substring(pos + 1);
System.out.println("get请求的字符串:" + ps);
for (String ss : ps.split("&")) {
String[] kv = ss.split("=");
this.params.put(kv[0], kv[1]);
System.out.println("get请求参数键值对:" + kv[0] + "\t" + kv[1]);
}
// 不包含请求参数
} else {
this.url = arr[1];
}
// post请求方式
} else {
this.url = arr[1];
// post请求方式
// 如果包含参数
if (!headers.endsWith("\r\n")) {
int pos = headers.lastIndexOf("\r\n");
String ps = headers.substring(pos + 2);
System.out.println("Post请求字符串:" + ps);
for (String ss : ps.split("&")) {
String[] kv = ss.split("=");
params.put(kv[0], kv[1]);
System.out.println("post请求参数键值对:" + kv[0] + "\t" + kv[1]);
}
// 不包含参数
}
}
Set<String> p = this.params.keySet();
Iterator<String> it = p.iterator();
while (it.hasNext()) {
System.out.println("键值对象:" + it.next()+"\t"+params.get(it.next()));
}
System.out.println("请求的url:" + this.url);
}
public void responseHTML(OutputStream os) {
StringBuilder response = new StringBuilder("");
response.append("HTTP/1.1 200 OK\r\n");
response.append("Access-Control-Allow-Origin:"+this.allowOrigin+"\r\n");
// response.append("Access-Control-Allow-Origin:http://127.0.0.1:8848\r\n");
response.append("Access-Control-Allow-Credentials:true\r\n");
response.append("Access-Control-Allow-Headers:*\r\n");
response.append("Content-Type:text/html; charset=utf-8\r\n\r\n");
response.append("\r\n" +
"\r\n" +
" \r\n" +
" \r\n" +
" \r\n" +
" \r\n" +
" \r\n" +
" 请求处理成功!
\r\n"+
" 当前时间:"
+LocalDateTime.now().toString()+"\r\n"+
" \r\n" +
"");
try {
os.write(response.toString().getBytes());
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void responseStreamTest(OutputStream out) {
try {
System.out.println("#######################测试方法:"+this.url);
// BufferedOutputStream bf = new BufferedOutputStream(out);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
PrintWriter pw = new PrintWriter(out);
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 200 OK\r\n");
response.append("Access-Control-Allow-Origin:"+this.allowOrigin+"\r\n");
// response.append("Access-Control-Allow-Origin:http://127.0.0.1:8848\r\n");
response.append("Access-Control-Allow-Credentials:true\r\n");
response.append("Access-Control-Allow-Headers:*\r\n");
// response.append("Accept-Ranges:bytes\r\n");
response.append("Content-Type: text/html; charset=utf-8\r\n");
response.append("Connection: keep-alive\r\n");
response.append("Keep-Alive: timeout=30\r\n");
response.append("Content-Length:"+this.resultMessage.getBytes("utf-8").length+"\r\n\r\n");
response.append(this.resultMessage);
//
System.out.println("请求头部信息\n################################\n" + this.requestHead);
System.out.println("################################");
System.out.println("输出:\n" + response.toString());
pw.write(response.toString());
pw.flush();
pw.close();
// pw.flush();
} catch (IOException | IllegalArgumentException | SecurityException e) {
System.out.println(e);
}
}
// 返回的报文信息
public void responseStream() {
try {
OutputStream out = socket.getOutputStream();
if(this.url==null||this.url.equals("/")||this.params==null) {
responseHTML(out);
return;
}
// 分支方法测试
// else if(this.url.equals("/queryid")) {
// responseStreamTest(out);
// return;
// }
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$:"+this.url);
BufferedOutputStream bf = new BufferedOutputStream(out);
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
PrintWriter pw = new PrintWriter(out);
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 200 OK\r\n");
response.append("Access-Control-Allow-Origin:"+this.allowOrigin+"\r\n");
// response.append("Access-Control-Allow-Origin:http://127.0.0.1:8848\r\n");
response.append("Access-Control-Allow-Credentials:true\r\n");
response.append("Access-Control-Allow-Headers:*\r\n");
// response.append("Accept-Ranges:bytes\r\n");
response.append("Content-Type: text/html; charset=utf-8\r\n");
response.append("Connection: keep-alive\r\n");
response.append("Keep-Alive: timeout=30\r\n");
response.append("Transfer-Encoding:chunked\r\n\r\n");
String info = this.resultMessage;
// String info = URLEncoder.encode(this.resultMessage, "utf-8");
// Content-Length表明了实体主体部分的大小,单位是字节
// response.append("Content-Length:"+info.getBytes("utf-8").length+"\r\n\r\n");
response.append(Integer.toHexString(info.getBytes().length)+"\r\n");
response.append(info+"\r\n");
response.append("0\r\n\r\n");
//
System.out.println("请求头部信息\n################################\n" + this.requestHead);
System.out.println("################################");
System.out.println("输出:\n" + response.toString());
pw.write(response.toString());
pw.flush();
pw.close();
// bf.write(response.toString().getBytes());
// bf.flush();
// bf.close();
// pw.flush();
} catch (IOException | IllegalArgumentException | SecurityException e) {
System.out.println(e);
}
}
// 该方法不能用,会造成请求信息读取不到
/*
* public void requestStream() throws IOException {
*
* InputStream in = socket.getInputStream();
*
* InputStreamReader read = new InputStreamReader(in); BufferedReader buff = new
* BufferedReader(read); StringBuilder builder = new StringBuilder(); String
* temp = null; while((temp=buff.readLine())!=null) {
* builder.append(temp+"\r\n"); } if(builder.toString().length()>0) {
* this.requestMessage = builder.toString(); }
*
* }
*/
}
public HttpService() {
try {
serverSocket = new ServerSocket(this.port);
fixedPool = Executors.newFixedThreadPool(this.threadSize);
System.out.println("ServerSocket正在监听端口:" + this.port);
while (true) {
Socket socket = serverSocket.accept();
StreamProcess task = new StreamProcess(socket);
Thread td = new Thread(task);
fixedPool.execute(td);
System.out.println("当前线程名:" + td.getName());
}
} catch (IOException e) {
System.out.println(e);
}
}
}
总结分析:以上用的是socket模拟实现http请求的响应,后端使用了固定的线程池处理socket请求,并对http请求头进行解析,得到请求后通过反射调用方法处理,方法使用了注解,反射单独有一个分发控制的方法,根据请求通过反射读取注解对应的方法,然后执行,模仿了spring的设计模式,这个程序捣鼓了近三天做成现在这个样,相当不容易,这次最大的成就是解决了编码的问题,了解了http请求方式。