Socket(七)

news2025/1/23 2:05:19

文章目录

    • 1. 单文件服务器
    • 2. 重定向器Redirector
    • 3. 功能完备的HTTP服务器

1. 单文件服务器

要研究HTTP服务器,先从一个简单的服务器开始,无论接受什么请求,这个服务器都始终发送同一个文件。这个单文件服务器名为SingleFileHTTPServer,如下:

public class QuizCardBuilder {
    //获取Logger对象,用于记录日志
    private static final Logger logger=Logger.getLogger("SingelFileHTTPServer");
    //服务端发送给客户端的数据
    private final  byte[] content;
    //请求头
    private final  byte[] header;
    //端口
    private final int port;
    //编码方式
    private final  String encoding;
    //构造函数
    public QuizCardBuilder(String data, String encoding, String mimeType, int port) throws UnsupportedEncodingException {
    //调用另一个构造函数
        this(data.getBytes(encoding),encoding,mimeType,port);
    }
    public QuizCardBuilder(byte[] data, String encoding,String mimeType ,int port){
        this.content=data;
        this.port=port;
        this.encoding=encoding;
        String header="HTTP/1.0 200 Ok\r\n"
                +"Server:OneFile 2.0\r\n"
                +"Content-length:"+ this.content.length+ "\r\n"
                +"Content-type:"+mimeType+"; charset="+encoding+"\r\n\r\n";
        this.header=header.getBytes(Charset.forName("US-ASCII")) ;
    }
    public void start()  {
        ExecutorService pool=Executors.newFixedThreadPool(100);
        try(ServerSocket serverSocket=new ServerSocket(this.port)){
            logger.info("Accepting connections on port "+ serverSocket.getLocalPort());
            logger.info("Data to be sent:");
            logger.info(new String(this.content,encoding));
            while (true){
                try{
                    Socket connection=serverSocket.accept();
                    pool.submit(new HTTPHandler(connection));
                }catch (IOException e){
                    logger.log(Level.WARNING,"Exception accepting connection",e);
                }catch (RuntimeException e){
                    logger.log(Level.SEVERE,"Unexpected errors",e);
                }
            }
        }catch (IOException ex){
            logger.log(Level.SEVERE,"Could not start server"+ex);
        }
    }

    private  class  HTTPHandler implements Callable<Void>{
        private final Socket connection;
        HTTPHandler(Socket connection){
            this.connection=connection;
        }

        @Override
        public Void call() throws Exception {
            try{
            //获得输出流
                OutputStream out=new BufferedOutputStream(connection.getOutputStream());
                //获得输入流
                InputStream in=new BufferedInputStream(connection.getInputStream());
                StringBuilder request=new StringBuilder(80);
                while(true){
                    int c=in.read();
                    if(c=='\r' || c=='\n' || c==-1)break;
                    request.append((char) c);
                }
                //如果是HTTP/1.0以后的版本则发送一个MIME首部
                if(request.toString().indexOf("HTTP/")!=-1){
                     out.write(header);
                }
                out.write(content);
                out.flush();
            }catch (IOException e){
                logger.log(Level.WARNING,"Error writing to client ",e);
            }finally {
                connection.close();
            }
            return null;
        }
    }

    public static void main(String[] args) {
           String encoding="UTF-8";
           try{
               //以绝对路径获取文件
               Path path= Paths.get("/Users/jackchai/Desktop/自学笔记/java项目/leetcode/leetcodetest/MyText.txt");
               //将文件内容读成字节流
               byte[] data= Files.readAllBytes(path);
               String contentType= URLConnection.getFileNameMap().getContentTypeFor("/Users/jackchai/Desktop/自学笔记/java项目/leetcode/leetcodetest/MyText.txt");
               QuizCardBuilder server=new QuizCardBuilder(data,encoding,contentType,8080);
               server.start();
               } catch (ArrayIndexOutOfBoundsException ex) {
                   System.out.println("Usage:java QuizCardBuilder filename port encoding");
               }catch (IOException e){
               logger.severe(e.getMessage());
           }
    }

}

下面是服务端日志

在这里插入图片描述

下面使用Telnet测试结果

在这里插入图片描述

2. 重定向器Redirector

重定向是特殊用途的HTTP服务器的另一个简单而有用的应用程序。他能从一个Web网站重定向到另一个网站。下面代码从指定的URL和端口,在这个端口打开一个服务器Socket,使用302 FOUND编码将接收的所有请求重定向到新URL表示的网站。

public class QuizCardBuilder {
   //获取Logger对象,用于日志
   private static  final Logger logger=Logger.getLogger("Redirecotr");
   //端口
   private final int port;
   //重定向的地址
   private final String newSite;
   //构造函数
   public QuizCardBuilder(String newSite, int port){
       this.port=port;
       this.newSite=newSite;
   }
   public void start(){
       try(ServerSocket serverSocket=new ServerSocket(port)){
           logger.info("Redirecting connections on port "+ serverSocket.getLocalPort()+" to "+newSite);
           while(true){
               try {
                   //打开Socket开始等待
                   Socket s=serverSocket.accept();
                   //当接受到一个请求时,开启一个新的线程
                   Thread t=new RedirectThread(s);
                   //开启新线程
                   t.start();
               }catch (IOException e){
                   logger.warning("Exception accepting connection");
               }catch (RuntimeException ex){
                   logger.log(Level.SEVERE,"Unexpected error",ex);
               }
             
           }
       }catch (BindException ex){
           logger.log(Level.SEVERE,"Could not start server." +ex);
       }catch (IOException ex){
           logger.log(Level.SEVERE,"Error opening server socket ",ex);
       }
   }
   private class RedirectThread extends Thread{
       private final Socket connection;
       RedirectThread(Socket s){
           this.connection=s;
       } 
       public void run(){
           try{
               //打开输出流
               Writer out=new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(),"US-ASCII"));
               //打开输入流
               Reader in=new InputStreamReader(new BufferedInputStream(connection.getInputStream()));
               StringBuilder request=new StringBuilder(90);
               //读取用户请求的第一行
               while(true){
                   int c=in.read();
                   if(c=='\r' || c=='\n' || c==-1){
                       break;
                   }
                   request.append((char)c);
               }
               
               String get=request.toString();
               String[] pieces =get.split("\\w*");
               String theFile=pieces[1];
               
               if(get.indexOf("HTTP")!=-1){
                   out.write("HTTP/1.0 302 FOUND\r\n");
                   Date now=new Date();
                   out.write("Date:"+now+"\r\n");
                   out.write("Server:Redirector  1.1\r\n");
                   out.write("Location:"+newSite+theFile+"\r\n");
                   out.write("Content-type: text/html\r\n\r\n");
                   out.flush();
               }
               //并不是所有的浏览器都支持重定向,所以我们需要生成HTML指出文档转移到哪里
               out.write("<HTML><HEAD><TITLE>Document moved</TITLE></HEAD>\r\n");
               out.write("<BODY><H1>Document moved</H1>\r\n");
               out.write("The document"+theFile
               +" has moved to \r\n  <A HREF\""+newSite+theFile+"\">"
               +newSite+theFile
               +"</A>,\r\n Please update your bookmarks<p>");
               out.write("</BODY><HTML>\r\n");
               logger.log(Level.INFO,"Redirected "+connection.getRemoteSocketAddress());
           }catch (IOException e){
               logger.log(Level.WARNING,"Error talking to "+connection.getRemoteSocketAddress(),e);
           }finally {
               try{
                   connection.close();
               } catch (IOException e) {
               
               }
           }
       }
   }

    public static void main(String[] args) {
       String  theSite;
        try{
            theSite="www.baidu.com";
            //删除末尾斜线
            if(theSite.endsWith("/")){
                theSite=theSite.substring(0,theSite.length()-1);
            }
        }catch (RuntimeException e){
            System.out.println("Usage: java Redirector http://www.baidu.com/port");
            return; 
        }
        QuizCardBuilder redirector=new QuizCardBuilder(theSite,8080);
        redirector.start();
    }
}

允许代码,在浏览器中访问http://localhost:8080,就会发生重定向

在这里插入图片描述

3. 功能完备的HTTP服务器

上面介绍了单文件的HTTP服务器,它会接受用户请求,将本地的一个文件内容以流的形式返回给客户端。这里将开发一个功能完备的HTTP服务器,名为JHTTP,它可以提供一个完整的文档树,包括图像、applet、HTML文件、文本文件等。它与前面的单文件服务器非常相似,只不过它所关注的是GET请求。

下面是JHTTP web服务器

public class QuizCardBuilder {
    //返回由 Java 语言规范定义的基础类的规范名称。如果基础类没有规范名称(即,如果它是本地或匿名类或其组件类型没有规范名称的数组),则返回 null。
  private static  final Logger logger=Logger.getLogger(QuizCardBuilder.class.getCanonicalName());
  //线程数量
  private  static final int NUM_THREADS=50;
  private static final String INDEX_FILE="index.html";
  private final File rootDeirectory;
  private final int port;
  public QuizCardBuilder(File rootDeirectory, int port)throws IOException{
      //如果rootDeirectory不是个文件目录则抛出异常
      if(!rootDeirectory.isDirectory()){
          throw new IOException(rootDeirectory+" does not exist as a directory");
      }
      this.rootDeirectory=rootDeirectory;
      this.port=port;
  }
  public void start()throws IOException{
      //定义线程池
      ExecutorService pool= Executors.newFixedThreadPool(NUM_THREADS);
      try(ServerSocket serverSocket=new ServerSocket(port)){
          logger.info("Accepting connections on port "+serverSocket.getLocalPort());
          logger.info("Document Root "+rootDeirectory);
          while (true){
              try{
                  //等待客户端请求
                  Socket socket=serverSocket.accept();
                  //创建一个线程,参数为文件的服务端文件系统的根目录,文件索引和服务器Socket
                  Runnable r=new RequestProcessors(rootDeirectory,INDEX_FILE,socket);
                  //提交线程任务
                  pool.submit(r);
              }catch (IOException ex){
                  logger.log(Level.WARNING,"Error accepting connection",ex);
              }
          }
      }
  }

    public static void main(String[] args) {
        File docroot;
        try{
            docroot=new File("/Users/jackchai/Desktop/自学笔记/java项目/leetcode/leetcodetest");
        }catch (ArrayIndexOutOfBoundsException ex){
            System.out.println("Usage: java JHTTP docroot port");
            return;
        }
        //设置要监听的端口
        int port=8080;
        try {
            QuizCardBuilder Jhttp=new QuizCardBuilder(docroot,port);
            Jhttp.start();
        }catch (IOException e){
            logger.log(Level.SEVERE,"Server could not start ",e );
        }
    }
}
class RequestProcessors implements Runnable{
    //获取Logger对象,用于日志管理
    private final static Logger logger=Logger.getLogger(RequestProcessor.class.getCanonicalName());
    //文件系统的根目录
    private File rootDirectory;
    //文件索引
    private String indexFilename="index.html";
    //服务端Socket
    private Socket connection;

    public RequestProcessors(File rootDirectory,String indexFilename,Socket connection){       //如果文件系统的根目录是一个文件
        if(rootDirectory.isFile()){
            throw new IllegalArgumentException("rootDirectory must be a directory, not a file");
        }
        try {
        //返回此抽象路径名的规范形式。等效于 new File(this.getCanonicalPath)。
            rootDirectory=rootDirectory.getCanonicalFile();
        }catch (IOException e){

        }
        this.rootDirectory=rootDirectory;
        if(indexFilename!=null) this.indexFilename=indexFilename;
        this.connection=connection;
    }
    @Override
    public void run() {
        //安全检查
        String root=rootDirectory.getPath();
        try {//获得输出流
            OutputStream raw=new BufferedOutputStream(connection.getOutputStream());
            Writer out=new OutputStreamWriter(raw);
            //获得输入流
            Reader in =new InputStreamReader(new BufferedInputStream(connection.getInputStream()),"US-ASCII");
            //用于存储用户都请求头
            StringBuilder requestLine=new StringBuilder();
            while(true){
                int c=in.read();
                if(c=='\r'||c=='\n'||c==-1)break;
                requestLine.append((char) c);
            }
            String get=requestLine.toString();
            //获得客户端地址
            logger.info(connection.getRemoteSocketAddress()+" "+get); 
            String[] tokens=get.split("\\s+");
            //请求方法
            String method=tokens[0];
            //获得HTTP版本
            String version="";
            if(method.equals("GET")){
                //用户请求的文件名
                String filename=tokens[1];
                if(filename.endsWith("/"))filename+=indexFilename;
                //获得文件类型(这里肯定是text/html
                String contentType= URLConnection.getFileNameMap().getContentTypeFor(filename);
                if(tokens.length>2){
                    version=tokens[2];
                }
                File thefile=new File(rootDirectory,filename.substring(1,filename.length()));
                if(thefile.canRead()
                    //不要让客户端超出文档根之外
                        && thefile.getCanonicalPath().startsWith(root)){
                    byte[] thedata= Files.readAllBytes(thefile.toPath());
                    if(version.startsWith("HTTP/")){
                        sendHeader(out,"HTTP/1.0 200 OK",contentType,thedata.length);
                    }
                    //发送文件,这可能是一个图像或其他二进制数据
                    //所以要用底层流输出
                    //而不是writer
                    raw.write(thedata);
                    raw.flush();
                }else {
                    String body=new StringBuilder("<HTML>\r\n")
                            .append("<HEAD><TITLE>FIle Not Found</TITLe>\r\n")
                            .append("</HEAD>\r\n")
                            .append("<BODY>\r\n")
                            .append("<H1>HTTP Error 404: File Not Found <H1>\r\n")
                            .append("<BODY></HTML>\r\n").toString();
                    if(version.startsWith("HTTP/")){
                        sendHeader(out,"HTTP/1.0 404 File Not Found","text/html; charset=utf-8",body.length());
                    }
                    out.write(body);
                    out.flush();
                }
            }else {//方法不等于GET
                String body = new StringBuilder("<HTML>\r\n")
                        .append("<HEAD><TITLE>Not Implemented</TITLe>\r\n")
                        .append("</HEAD>\r\n")
                        .append("<BODY>\r\n")
                        .append("<H1>HTTP Error 501: Not Implemented <H1>\r\n")
                        .append("<BODY></HTML>\r\n").toString();
                if(version.startsWith("HTTP/")){
                    sendHeader(out,"HTTP/1.0 501 Not Implemented ","text/html; charset=utf-8",body.length());
                }
                out.write(body);
                out.flush();
            }
        }catch (IOException ex){
            logger.log(Level.WARNING,"Error talking to "+connection.getRemoteSocketAddress(),ex);
        }finally {
            try {
                connection.close();
            }catch (IOException e){}
        }

    }
    private void sendHeader(Writer out,String responseCode,String contentType,int length) throws IOException {
        out.write(responseCode+"\r\n");
        Date now=new Date();
        out.write("Date: "+now+"\r\n");
        out.write("Server:"+length+"\r\n");
        out.write("Content-length:"+length+"\r\n");
        out.write("Content-type:"+contentType+"\r\n\r\n");
        out.flush();
    }
}

运行成功获取文件

在这里插入图片描述

也可以获取图片

在这里插入图片描述
上面这个服务器提供了我们获取根目录下的文件,但还是十分简单,如果真的希望使用JHTTP来运行一个高流量网站,还可以做一些事情来加速这个服务器,最主要的是实现智能缓冲,跟踪记录接收到的请求,将最频繁请求的文件中的数据存储在一个Map中,使它们保存在内存中,使用一个低优先级的线程更新这个缓存。还可以使用非阻塞I/O和通道来代替线程和流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/588594.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

泛型的介绍以及原理

目录 一、前言 二、什么泛型 三、为什么要使用泛型 3.1、保证了类型的安全性。 3.2、消除强制转换 3.3、提高程序的性能 3.4、 提高了代码的重用性 四、如何使用泛型 4.1、 泛型类 4.2、泛型接口 4.3、泛型方法 五、泛型通配符 5.1、无边界的通配符 5.2、固定上边…

telnet 120.XX8888会超时或者无反应,防火墙加入8888,安全组也加入8888,但是访问120.XX:8888也没有反应

⚠️命令都是远程登录的哦 选这个远程连接&#xff1a; 初次密码自己设置别忘了 1、看是否8888端口是否有正常监听 netstat -ntlp我这边清楚看到没有8888 2、如果没有监听是无法连接&#xff0c;需要安装对应的程序同时监听8888端口 比如我想用宝塔面板 我就需要下载宝塔面…

一步步入门编写PHP扩展

1、写在最前 随着互联网飞速发展&#xff0c;lamp架构的流行&#xff0c;php支持的扩展也越来越多&#xff0c;这样直接促进了php的发展。 但是php也有脚本语言不可避免的问题&#xff0c;性能比例如C等编译型语言相差甚多&#xff0c;所以在考虑性能问题的时候最好还是通过php…

00后实在太强了,98年的我被卷废了,太离谱了...

前言 最近在公司我真的感受到了什么叫“卷”&#xff0c;以往的我划划水日子过的轻轻松松&#xff0c;直到公司最近招了一个00后进来&#xff0c;真的让我感受到了危机&#xff0c;刚进来工资就和我差不多&#xff0c;我刚开始其实有点不太舒服&#xff0c;凭什么我辛辛苦苦干…

在pycharm中调用qt界面功能

目录 一、新建designer文件 1、打开pycharm中的designer 2、创建个widget 3、拖动几个简单按钮 4、保存一下 5、右击test1.ui 这边首先环境已经配置完毕&#xff0c;可以参考之前写的博客&#xff1a; 关于PyQt5的环境搭建_Littlehero_121的博客-CSDN博客 一、新建desi…

如何使用PHM技术提高汽车工业的效率和性能?

在汽车工业中&#xff0c;预测性健康管理&#xff08;PHM&#xff09;技术正日益受到关注。作为一种基于数据驱动的解决方案&#xff0c;PHM技术通过实时监测和分析设备和系统的状态&#xff0c;实现对设备健康状况的预测和管理。 图.汽车制造&#xff08;iStock&#xff09; 汽…

代码示范【FabEdge v0.8.0】配置 connector 公开端口

FabEdge项目简介&#xff1a; FabEdge是博云在2021年8月发起&#xff0c;基于Kubernetes 构建的专注于边缘计算场景的容器网络方案&#xff0c;支持 KubeEdge 、SuperEdge、OpenYurt 等主流边缘计算框架。旨在解决边缘计算场景下容器网络配置管理复杂、网络割裂互不通信、缺少…

hadoop单机版部署

1.下载hadoop wget --no-check-certificate https://mirrors.bfsu.edu.cn/apache/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz 2.解压重命名 tar -zxvf hadoop-3.3.1.tar.gz mv hadoop-3.3.1.tar.gz hadoop 3.编辑hosts vim /etc/hosts 172.17.1.1 hadoop925 4.进入配置…

4.Apache网页优化

文章目录 Apache网页优化网页压缩网页缓存隐藏版本信息Apache防盗链 Apache网页优化 Apache网页优化 网页压缩网页缓存 Apache安全优化 隐藏版本信息配置防盗链 网页压缩 配置Apache的网页压缩功能&#xff0c;是使用gzip压缩算法来对网页内容进行压缩后再传输到客户端浏览器…

LNMT架构之反向代理负载均衡

目录 一、实验前提环境配置 &#xff08;一&#xff09;关闭防火墙&#xff0c;安装本地yum &#xff08;二&#xff09;部署tomcat &#xff08;三&#xff09;部署Mariadb &#xff08;四&#xff09;部署nginx 二、反向代理负载均衡 方法一&#xff1a;&#xff08;轮…

【算法学习系列】07 - 无序数组中的局部最小值问题

文章目录 说明约束条件简单说下思路解决方案随机无序数组样本生成器算法实现验证代码进行大样本随机测试验证算法正确性 说明 在算法中&#xff0c;局部最小值是指一个函数在一个局部范围内的最小值。 具体而言&#xff0c;如果一个函数在一个小区间内的取值都比该区间内的其他…

C++:STL--priority_queue

文章目录 一.STL设计思想:容器适配器STL--stack的代码设计STL--queue的代码设计stack和queue的默认容器适配器deque的数据结构解析deque的存储结构示意图 二.C仿函数仿函数示例 三.STL--priority_queue(优先级队列)1.C优先级队列的数据结构2.priority_queue的实现框架比较函数(…

chatgpt赋能python:Python中创建画布的函数——matplotlib

Python中创建画布的函数——matplotlib Python作为一种强大的编程语言&#xff0c;拥有许多重要且广泛应用的模块和库。其中&#xff0c;matplotlib是一种用于制作高质量的图形和图表的库&#xff0c;而创建画布的函数便是其基础功能之一。 什么是matplotlib&#xff1f; Ma…

C语言---初始C语言

1、初始C语言 1、编译器主要有&#xff1a;Clang、GCC、WIN-TC、MSVC、Turbo C等 什么是编译&#xff1f; test.c----------------------------->test.exe 这个过程需要经过编译、链接等过程&#xff0c;而众多编译器实现的功能就是把我们写的test.c进行编译。 2、VS20…

如何把“困在”内网的数据释放,进行安全的流转传输呢?

互联网大时代&#xff0c;数据的生产使用与互联网紧密相关&#xff0c;但数据安全和网络安全却既有联系又互不相同。数据安全和网络安全的突出区别是核心主体不同&#xff0c;数据安全关注的数据全生命周期的安全&#xff0c;而网络安全则是侧重保障网络体系和网络环境的安全性…

硬卷完了!低代码打怪升级进阶成神之路(2023年最新版)

一、背景 应用开发周期长一直是IT部门和业务部门面临的问题。 IT部门总是被新的应用需求弄得不堪重负。他们不可能完成业务部门想要完成的每一个项目。同时&#xff0c;业务部门的用户厌倦了等待&#xff0c;并开始完全绕过IT部门。 今天&#xff0c;我们来探索一下“低代码开发…

制药企业高效过滤器检漏参考法规、方法及操作步骤

对制药企业来讲&#xff0c;高效过滤器检漏主要是现场检漏&#xff0c;通过DOP法来发现滤器本身及运输、安装过程中可能存在的问题。常使用气溶胶光度计及多分散气溶胶进行检漏。依据的标准是2010药品GMP指南(测试方法采用ISO14644-3)。 对于制药企业来说&#xff0c;高效过滤器…

自动驾驶TPM技术杂谈 ———— 边缘检测

文章目录 介绍边缘检测与微分运算离散信号的差分滤波Robert算子Prewitt算子Sobel算子拉普拉斯算子 介绍 计算机视觉&#xff08;Computer Vision&#xff0c;CV&#xff09;是一门使用计算机模拟生物视觉的学科&#xff0c;目的是使用计算机代替人眼实现对目标的识别、分类、跟…

3.2. 数学类(Math、BigInteger、BigDecimal)

1. Math类 Math类提供了一些基本的数学函数&#xff0c;如求平方根、绝对值、三角函数等。它是一个final类&#xff0c;并且所有的方法都是static的&#xff0c;因此无需创建对象&#xff0c;直接使用类名调用方法即可。 以下是Math类的一些常用方法&#xff1a; abs(double…

抖音seo源码-抖音搜索源码-抖音下拉词-抖音关键词排名系统搭建

为了优化抖音平台上的内容&#xff0c;开发抖音关键词排名系统成为了必要的措施。该系统可以针对搜索结果和下拉词进行分析&#xff0c;为用户提供更准确的搜索结果。为实现这一目标&#xff0c;开发团队进行了大量的市场调查和用户研究。 在开发过程中&#xff0c;团队利用了…