java 服务器阻塞_基于阻塞模式的简易javaHTTP服务器 | 学步园

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的子类,默认是阻塞式的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值