【愚公系列】2023年10月 Java教学课程 083

编程入门 行业动态 更新时间:2024-10-14 20:19:09

【<a href=https://www.elefans.com/category/jswz/34/1769546.html style=愚公系列】2023年10月 Java教学课程 083"/>

【愚公系列】2023年10月 Java教学课程 083

🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏

文章目录

  • 🚀一、自建HTTP服务器解析浏览器请求案例
    • 🔎1.环境搭建
    • 🔎2.获取请求信息并解析
    • 🔎3.给浏览器响应数据
    • 🔎4.代码优化
  • 🚀感谢:给读者的一封信


🚀一、自建HTTP服务器解析浏览器请求案例

🔎1.环境搭建

  • 实现步骤

    • 编写HttpServer类,实现可以接收浏览器发出的请求
    • 其中获取连接的代码可以单独抽取到一个类中
  • 代码实现

    // 服务端代码
    public class HttpServer {public static void main(String[] args) throws IOException {//1.打开服务端通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//2.让这个通道绑定一个端口serverSocketChannel.bind(new InetSocketAddress(10000));//3.设置通道为非阻塞serverSocketChannel.configureBlocking(false);//4.打开一个选择器Selector selector = Selector.open();//5.绑定选择器和服务端通道serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);while(true){//6.选择器会监视通道的状态.int count = selector.select();if(count != 0){//7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.//获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while(iterator.hasNext()){//selectionKey 依次表示每一个服务端通道的令牌SelectionKey selectionKey = iterator.next();if(selectionKey.isAcceptable()){//获取连接AcceptHandler acceptHandler = new AcceptHandler();acceptHandler.connSocketChannel(selectionKey);}else if(selectionKey.isReadable()){}//任务处理完毕以后,将SelectionKey从集合中移除iterator.remove();}}}}
    }
    // 将获取连接的代码抽取到这个类中
    public class AcceptHandler {public SocketChannel connSocketChannel(SelectionKey selectionKey){try {//获取到已经就绪的服务端通道ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();SocketChannel socketChannel = ssc.accept();//设置为非阻塞状态socketChannel.configureBlocking(false);//把socketChannel注册到选择器上socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);return socketChannel;} catch (IOException e) {e.printStackTrace();}return null;}
    }
    

🔎2.获取请求信息并解析

  • 实现步骤

    • 将请求信息封装到HttpRequest类中
    • 在类中定义方法,实现获取请求信息并解析
  • 代码实现

    /*** 用来封装请求数据的类*/
    public class HttpRequest {private String method; //请求方式private String requestURI; //请求的uriprivate String version;   //http的协议版本private HashMap<String,String> hm = new HashMap<>();//所有的请求头//parse --- 获取请求数据 并解析public void parse(SelectionKey selectionKey){try {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();StringBuilder sb = new StringBuilder();//创建一个缓冲区ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int len;//循环读取while((len = socketChannel.read(byteBuffer)) > 0){byteBuffer.flip();sb.append(new String(byteBuffer.array(),0,len));//System.out.println(new String(byteBuffer.array(),0,len));byteBuffer.clear();}//System.out.println(sb);parseHttpRequest(sb);} catch (IOException e) {e.printStackTrace();}}//解析http请求协议中的数据private void parseHttpRequest(StringBuilder sb) {//1.需要把StringBuilder先变成一个字符串String httpRequestStr = sb.toString();//2.获取每一行数据String[] split = httpRequestStr.split("\r\n");//3.获取请求行String httpRequestLine = split[0];//GET / HTTP/1.1//4.按照空格进行切割,得到请求行中的三部分String[] httpRequestInfo = httpRequestLine.split(" ");this.method = httpRequestInfo[0];this.requestURI = httpRequestInfo[1];this.version = httpRequestInfo[2];//5.操作每一个请求头for (int i = 1; i < split.length; i++) {String httpRequestHeaderInfo = split[i];//Host: 127.0.0.1:10000String[] httpRequestHeaderInfoArr = httpRequestHeaderInfo.split(": ");hm.put(httpRequestHeaderInfoArr[0],httpRequestHeaderInfoArr[1]);}}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public String getRequestURI() {return requestURI;}public void setRequestURI(String requestURI) {this.requestURI = requestURI;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public HashMap<String, String> getHm() {return hm;}public void setHm(HashMap<String, String> hm) {this.hm = hm;}@Overridepublic String toString() {return "HttpRequest{" +"method='" + method + '\'' +", requestURI='" + requestURI + '\'' +", version='" + version + '\'' +", hm=" + hm +'}';}
    }
    

🔎3.给浏览器响应数据

  • 实现步骤

    • 将响应信息封装HttpResponse类中
    • 定义方法,封装响应信息,给浏览器响应数据
  • 代码实现

    public class HttpResponse {private String version; //协议版本private String status;  //响应状态码private String desc;    //状态码的描述信息//响应头数据private HashMap<String, String> hm = new HashMap<>();private HttpRequest httpRequest;  //我们后面要根据请求的数据,来进行一些判断//给浏览器响应数据的方法public void sendStaticResource(SelectionKey selectionKey) {//1.给响应行赋值this.version = "HTTP/1.1";this.status = "200";this.desc = "ok";//2.将响应行拼接成一个单独的字符串 // HTTP/1.1 200 okString responseLine = this.version + " " + this.status + " " + this.desc + "\r\n";//3.给响应头赋值hm.put("Content-Type", "text/html;charset=UTF-8");//4.将所有的响应头拼接成一个单独的字符串StringBuilder sb = new StringBuilder();Set<Map.Entry<String, String>> entries = hm.entrySet();for (Map.Entry<String, String> entry : entries) {sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");}//5.响应空行String emptyLine = "\r\n";//6.响应行,响应头,响应空行拼接成一个大字符串String responseLineStr = responseLine + sb.toString() + emptyLine;try {//7.将上面三个写给浏览器SocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer1 = ByteBuffer.wrap(responseLineStr.getBytes());socketChannel.write(byteBuffer1);//8.单独操作响应体//因为在以后响应体不一定是一个字符串//有可能是一个文件,所以单独操作String s = "哎哟,妈呀,终于写完了.";ByteBuffer byteBuffer2 = ByteBuffer.wrap(s.getBytes());socketChannel.write(byteBuffer2);//9.释放资源socketChannel.close();} catch (IOException e) {e.printStackTrace();}}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public HashMap<String, String> getHm() {return hm;}public void setHm(HashMap<String, String> hm) {this.hm = hm;}public HttpRequest getHttpRequest() {return httpRequest;}public void setHttpRequest(HttpRequest httpRequest) {this.httpRequest = httpRequest;}@Overridepublic String toString() {return "HttpResponse{" +"version='" + version + '\'' +", status='" + status + '\'' +", desc='" + desc + '\'' +", hm=" + hm +", httpRequest=" + httpRequest +'}';}
    }
    

🔎4.代码优化

  • 实现步骤

    • 根据请求资源路径不同,响应不同的数据
    • 服务端健壮性处理
    • 访问不存在的资源处理
  • 代码实现

    /*** 接收连接的任务处理类*/
    public class AcceptHandler {public SocketChannel connSocketChannel(SelectionKey selectionKey){try {//获取到已经就绪的服务端通道ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();SocketChannel socketChannel = ssc.accept();//设置为非阻塞状态socketChannel.configureBlocking(false);//把socketChannel注册到选择器上socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);return socketChannel;} catch (IOException e) {e.printStackTrace();}return null;}
    }
    /*** 接收客户端请求的类*/
    public class HttpServer {public static void main(String[] args) throws IOException {//1.打开服务端通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//2.让这个通道绑定一个端口serverSocketChannel.bind(new InetSocketAddress(10000));//3.设置通道为非阻塞serverSocketChannel.configureBlocking(false);//4.打开一个选择器Selector selector = Selector.open();//5.绑定选择器和服务端通道serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);while(true){//6.选择器会监视通道的状态.int count = selector.select();if(count != 0){//7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.//获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while(iterator.hasNext()){//selectionKey 依次表示每一个服务端通道的令牌SelectionKey selectionKey = iterator.next();if(selectionKey.isAcceptable()){//获取连接AcceptHandler acceptHandler = new AcceptHandler();acceptHandler.connSocketChannel(selectionKey);}else if(selectionKey.isReadable()){//读取数据HttpRequest httpRequest = new HttpRequest();httpRequest.parse(selectionKey);System.out.println("http请求的数据为 ---->" + httpRequest);if(httpRequest.getRequestURI() == null || "".equals(httpRequest.getRequestURI())){selectionKey.channel();continue;}System.out.println("...数据解析完毕,准备响应数据....");//响应数据HttpResponse httpResponse = new HttpResponse();httpResponse.setHttpRequest(httpRequest);httpResponse.sendStaticResource(selectionKey);}//任务处理完毕以后,将SelectionKey从集合中移除iterator.remove();}}}}
    }
    /*** 用来封装请求数据的类*/
    public class HttpRequest {private String method; //请求方式private String requestURI; //请求的uriprivate String version;   //http的协议版本private HashMap<String,String> hm = new HashMap<>();//所有的请求头//parse --- 获取请求数据 并解析public void parse(SelectionKey selectionKey){try {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();StringBuilder sb = new StringBuilder();//创建一个缓冲区ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int len;//循环读取while((len = socketChannel.read(byteBuffer)) > 0){byteBuffer.flip();sb.append(new String(byteBuffer.array(),0,len));//System.out.println(new String(byteBuffer.array(),0,len));byteBuffer.clear();}//System.out.println(sb);parseHttpRequest(sb);} catch (IOException e) {e.printStackTrace();}}//解析http请求协议中的数据private void parseHttpRequest(StringBuilder sb) {//1.需要把StringBuilder先变成一个字符串String httpRequestStr = sb.toString();if(!(httpRequestStr == null || "".equals(httpRequestStr))){//2.获取每一行数据String[] split = httpRequestStr.split("\r\n");//3.获取请求行String httpRequestLine = split[0];//GET / HTTP/1.1//4.按照空格进行切割,得到请求行中的三部分String[] httpRequestInfo = httpRequestLine.split(" ");this.method = httpRequestInfo[0];this.requestURI = httpRequestInfo[1];this.version = httpRequestInfo[2];//5.操作每一个请求头for (int i = 1; i < split.length; i++) {String httpRequestHeaderInfo = split[i];//Host: 127.0.0.1:10000String[] httpRequestHeaderInfoArr = httpRequestHeaderInfo.split(": ");hm.put(httpRequestHeaderInfoArr[0],httpRequestHeaderInfoArr[1]);}}}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public String getRequestURI() {return requestURI;}public void setRequestURI(String requestURI) {this.requestURI = requestURI;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public HashMap<String, String> getHm() {return hm;}public void setHm(HashMap<String, String> hm) {this.hm = hm;}@Overridepublic String toString() {return "HttpRequest{" +"method='" + method + '\'' +", requestURI='" + requestURI + '\'' +", version='" + version + '\'' +", hm=" + hm +'}';}
    }
    /*** 用来封装响应数据的类*/
    public class HttpResponse {private String version; //协议版本private String status;  //响应状态码private String desc;    //状态码的描述信息//响应头数据private HashMap<String, String> hm = new HashMap<>();private HttpRequest httpRequest;  //我们后面要根据请求的数据,来进行一些判断//给浏览器响应数据的方法public void sendStaticResource(SelectionKey selectionKey) {//1.给响应行赋值this.version = "HTTP/1.1";this.status = "200";this.desc = "ok";//3.给响应头赋值//先获取浏览器请求的URIString requestURI = this.getHttpRequest().getRequestURI();if(requestURI != null){File file = new File(WEB_APP_PATH + requestURI);//判断这个路径是否存在if(!file.exists()){this.status = "404";this.desc = "NOT FOUNG";}if("200".equals(this.status)){if("/".equals(requestURI)){hm.put("Content-Type", "text/html;charset=UTF-8");}else if("/favicon.ico".equals(requestURI)){hm.put("Content-Type", "image/x-icon");}else if("/a.txt".equals(requestURI)){hm.put("Content-Type", "text/html;charset=UTF-8");}else if("/1.jpg".equals(requestURI)){hm.put("Content-Type", "image/jpeg");}else if("/1.png".equals(requestURI)){hm.put("Content-Type", "image/png");}}else{hm.put("Content-Type", "text/html;charset=UTF-8");}}//2.将响应行拼接成一个单独的字符串 // HTTP/1.1 200 okString responseLine = this.version + " " + this.status + " " + this.desc + "\r\n";//4.将所有的响应头拼接成一个单独的字符串StringBuilder sb = new StringBuilder();Set<Map.Entry<String, String>> entries = hm.entrySet();for (Map.Entry<String, String> entry : entries) {sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");}//5.响应空行String emptyLine = "\r\n";//6.响应行,响应头,响应空行拼接成一个大字符串String responseLineStr = responseLine + sb.toString() + emptyLine;try {//7.将上面三个写给浏览器SocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer1 = ByteBuffer.wrap(responseLineStr.getBytes());socketChannel.write(byteBuffer1);//8.单独操作响应体//因为在以后响应体不一定是一个字符串//有可能是一个文件,所以单独操作// String s = "哎哟,妈呀,终于写完了.";byte [] bytes = getContent();ByteBuffer byteBuffer2 = ByteBuffer.wrap(bytes);socketChannel.write(byteBuffer2);//9.释放资源socketChannel.close();} catch (IOException e) {e.printStackTrace();}}public static final String WEB_APP_PATH = "mynio\\webapp";private byte[] getContent() {try {//1.获取浏览器请求的URIString requestURI = this.getHttpRequest().getRequestURI();if(requestURI != null){if("200".equals(this.status)){//2.判断一下请求的URI,根据不同的URI来响应不同的东西if("/".equals(requestURI)){String s = "哎哟,妈呀,终于写完了.";return s.getBytes();}else/* if("/favicon.ico".equals(requestURI))*/{//获取一个ico文件FileInputStream fis = new FileInputStream(WEB_APP_PATH + requestURI);//把ico文件变成一个字节数组返回return IOUtils.toByteArray(fis);}}else{return "访问的资源不存在".getBytes();}}} catch (IOException e) {e.printStackTrace();}return new byte[0];}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public HashMap<String, String> getHm() {return hm;}public void setHm(HashMap<String, String> hm) {this.hm = hm;}public HttpRequest getHttpRequest() {return httpRequest;}public void setHttpRequest(HttpRequest httpRequest) {this.httpRequest = httpRequest;}@Overridepublic String toString() {return "HttpResponse{" +"version='" + version + '\'' +", status='" + status + '\'' +", desc='" + desc + '\'' +", hm=" + hm +", httpRequest=" + httpRequest +'}';}
    }
    

🚀感谢:给读者的一封信

亲爱的读者,

我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。

如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。

我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。

如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。

再次感谢您的阅读和支持!

最诚挚的问候, “愚公搬代码”

更多推荐

【愚公系列】2023年10月 Java教学课程 083

本文发布于:2024-03-04 16:59:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1709853.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:愚公   课程   系列   Java

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!