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