基于阻塞模式的简易javaHTTP服务器

论坛 期权论坛 脚本     
匿名技术用户   2021-1-3 23:37   11   0
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 此类实现 IP 套接字地址(IP 地址 + 端口号)。SocketAddress
public class SimpleHttpServer {
 private int port = 80;
 private ServerSocketChannel serverSocketChannel = null;
 private ExecutorService executorService;
 private static final int POOL_MULTIPLE = 4;

 public SimpleHttpServer() throws IOException {

  executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
    .availableProcessors() * POOL_MULTIPLE);
  serverSocketChannel = ServerSocketChannel.open();
  // 确保socket关闭之后,端口可以立即被另一个程序使用
  serverSocketChannel.socket().setReuseAddress(true);
  // 将服务器程序绑定到一个端口
  serverSocketChannel.socket().bind(new InetSocketAddress(port));
  System.out.println("服务器已经启动!");

 }

 public void service() {
  while (true) {
   SocketChannel socketChannel = null;
   try {
    socketChannel = serverSocketChannel.accept();
    executorService.execute(new Handler(socketChannel));
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

 public static void main(String args[]) throws IOException {
  new SimpleHttpServer().service();
 }

 class Handler implements Runnable {
  private SocketChannel socketChannel;

  public Handler(SocketChannel socketChannel) {
   this.socketChannel = socketChannel;
  }

  @Override
  public void run() {
   handle(socketChannel);
  }

  public void handle(SocketChannel socketChannel) {
   try {
    Socket socket = socketChannel.socket();
    System.out.println("接收到用户连接,来自:" + socket.getInetAddress()
      + ":" + socket.getPort());
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    socketChannel.read(buffer);// 接收HTTP请求,假定其长度不超过1024个字节
    buffer.flip();// 将极限设置为位置,再把位置设置为0
    String request = decode(buffer);
    System.out.println(request);// 打印HTTP请求

    // 生成HTTP响应结果
    StringBuffer sb = new StringBuffer("HTTP/1.1 200 OK\r\n");
    sb.append("Content-Type:text/html\r\n\r\n");
    socketChannel.write(encode(sb.toString()));// 输出响应头

    FileInputStream in;
    // 获得HTTP请求的第一行 解析请求

    String firstLineOfRequest = request.substring(0,
      request.indexOf("\r\n"));
    if (firstLineOfRequest.indexOf("login.htm") != -1) {
     System.out.println("hehe");
     in = new FileInputStream(new File("D://root/login.htm"));
    }
    LinkedList 
    else
     in = new FileInputStream(new File("D://root/hello.htm"));
    // 通道Channel用来连接缓冲区与数据源或者数据目的地
    // FileChannel类是Channel的实现类,代表这一个与文件相连的通道。
    // 该类实现了ByteChannel、ScatteringByteChannel和GatheringByteChannel接口,支持读写
    // 、分散读和集中写操作,FIleChannel没有公开的构造方法,因此客户程序不能用new来创建它的实例
    // 不过在FileInputStream和FileOutStream当中提供了getChannel()返回FileChannel的实例
    FileChannel fileChannel = in.getChannel();
    // 将字节从此通道的文件传输到给定的可写入字节通道。
    /*
     * public abstract long transferTo(long position, long count,
     * WritableByteChannel target) throws IOException
     */

    /*
     * 将字节从此通道的文件传输到给定的可写入字节通道。
     * 
     * 试图读取从此通道的文件中给定 position 处开始的 count
     * 个字节,并将其写入目标通道。此方法的调用不一定传输所有请求的字节;是否传输取决于通道的性质和状态。如果此通道的文件从给定的
     * position 处开始所包含的字节数小于 count 个字节,或者如果目标通道是非阻塞的并且其输出缓冲区中的自由空间少于
     * count 个字节,则所传输的字节数要小于请求的字节数。
     * 
     * 此方法不修改此通道的位置。如果给定的位置大于该文件的当前大小,则不传输任何字节。如果目标通道中有该位置,则从该位置开始写入各字节
     * ,然后将该位置增加写入的字节数。
     * 
     * 与从此通道读取并将内容写入目标通道的简单循环语句相比,此方法可能高效得多。很多操作系统可将字节直接从文件系统缓存传输到目标通道
     * ,而无需实际复制各字节。
     * 
     * 参数: position - 文件中的位置,从此位置开始传输;必须为非负数 count -
     * 要传输的最大字节数;必须为非负数 target - 目标通道 返回: 实际已传输的字节数,可能为零
     */
    long num = fileChannel.transferTo(0, fileChannel.size(),
      socketChannel);
    System.out.println(num);
    // fileChannel.close();
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    try {
     if (socketChannel != null)
      socketChannel.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }

  /*
   * java.nio.Charset类的每个实例代表特定的字符编码类型。 将字节序列转换为字符串的过程称之为解码;decode
   * 将字符串转换为字节序列的过程称之为编码 ;encode Charset的静态forName(String
   * encode)方法返回一个Charset对象 代表encode指定的编码类型
   */
  private Charset charset = Charset.forName("GBK");

  public String decode(ByteBuffer buffer) {
   // decode(ByteBuffer buffer) 将ByteBuffer构成的字节序列进行解码 生成CharBuffer
   CharBuffer charBuffer = charset.decode(buffer);
   return charBuffer.toString();
  }

  public ByteBuffer encode(String str) { // 编码
   return charset.encode(str);
  }
 }
}

通过输入http://localhost:80/login.htm可以访问本地的这个htm文件,

这个编程是基于阻塞模式的,ServerSocketChannel是继承与SelecteableChannel的子类,默认是阻塞式的

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP