java nio http服务器_Java使用NioSocket手动实现HTTP服务器

niosocket简单复习

重要概念

niosocket里面的三个重要概念:buffer、channel、selector

buffer为要传输的数据

channel为传输数据的通道

selector为通道的分配调度者

使用步骤

使用niosocket实现通信大概如以下步骤:

serversocketchannel可以通过configureblocking方法来设置是否采用阻塞模式,设置为false后就可以调用register注册selector,阻塞模式下不可以用selector。

注册后,selector就可以通过select()来等待请求,通过参数设置等待时长,若传入参数0或者不传入参数,将会采用阻塞模式直到有请求出现。

接收到请求后selector调用selectedkeys方法,返回selectedkey集合。

selectedkey保存了处理当前请求的channel和selector,并提供了不同的操作类型。四种操作属性:selectedkey.op_accept、selectedkey.op_connect、selectedkey.op_read、selectedkey.op_write。

通过selectedkey的isacceptable、isconnectable、isreadable和iswritable来判断操作类型,并处理相应操作。

在相应的handler中提取selectedkey中的channel和buffer信息并执行相应操作。

实现http

创建httpserver类作为程序的主要入口

public class httpserver {

public static void main(string[] args) throws exception{

serversocketchannel serversocketchannel = serversocketchannel.open();

serversocketchannel.socket().bind(new inetsocketaddress((8080)));

serversocketchannel.configureblocking(false);

selector selector = selector.open();

// it must be accept, or it will throw exception

serversocketchannel.register(selector, selectionkey.op_accept);

while(true){

if (selector.select(3000) == 0){

continue;

}

iterator keyiter = selector.selectedkeys().iterator();

while (keyiter.hasnext()){

selectionkey key = keyiter.next();

new thread(new httphandler(key)).run();

keyiter.remove();

}

}

}

}

以上代码的逻辑大致遵循着niosocket的大概用法,其中serversocketchannel使用register方法注册到selector仅是op_accept,使用其他操作就会操作。但是并不是说不能进行其他操作,而是其他操作稍后实现。

在serversocketchannel.configureblocking(false)后,非阻塞模式启动。server接收到请求后就会将记录了请求信息的key交给httphandler做详细处理,处理完就把key从迭代器里面remove掉。可以看到出来,httpserver对请求里面的信息一概不知,这样才能成为一个出色的管理层,它管理着httphandler来处理请求。

既然选用了niosocket这样的new io,httphandler必然是多线程的实现(否则还有什么意义)。

创建httphandler来处理请求

对于来自httpserver的不加工信息,httphandler必须要做全套,因此需要httphandler自己考虑好有没有中文乱码、buffer大小是多少等等。httphandler大概框架如下即可:

class httphandler implements runnable{

private int buffersize = 1024;

private string localcharset = "utf-8";

private selectionkey key;

public httphandler(selectionkey key){

this.key = key;

}

public void handleaccept() throws ioexception{}

public void handleread() throws ioexception{}

@override

public void run() {

try {

if(key.isacceptable()){

handleaccept();

}

if(key.isreadable()){

handleread();

}

}catch (ioexception ex){

ex.printstacktrace();

}

}

}

如上框架简单明了,重载run实现多线程,handleaccept和handleread用于详细地处理相关操作,buffersize规定buffer大小,localcharset的设定提前防止中文乱码。

需要注意的是httpserver里面,我们只注册了op_accept这个操作,那么在httphandler里面只有isacceptable()判定为真,那么handleread()怎么办呢?我们会在handleaccept()注册好的:

public void handleaccept() throws ioexception{

socketchannel clientchannel =

((serversocketchannel)key.channel()).accept();

clientchannel.configureblocking(false);

clientchannel.register(

key.selector(), selectionkey.op_read, bytebuffer.allocate(buffersize)

);

}

在handleaccept里面,我们先取得key里面的请求信息,如对应客户端的socketchannel (socketchannel需要serversocketchannel接受了后才有),接着就可以为socketchannel注册op_read操作了,带上指定大小的buffer。注册后,key可是isreadable()了,接下来则是在handleread中对key进行解剖处理:(代码有点长,但大多是控制台输出和对字符串的拼接操作,看官可放心食用。)

public void handleread() throws ioexception{

socketchannel sc = (socketchannel)key.channel();

bytebuffer buffer = (bytebuffer)key.attachment();

buffer.clear();

if (sc.read(buffer) == -1){

sc.close();

}else {

buffer.flip();

string receivestring = charset.forname(localcharset).newdecoder().decode(buffer).tostring();

string[] requestmessage = receivestring.split("\r\n");

for (string s: requestmessage){

system.out.println(s);

if (s.isempty()){

break;

}

}

string[] firstline = requestmessage[0].split(" ");

system.out.println();

system.out.println("method:\t"+ firstline[0]);

system.out.println("url:\t"+firstline[1]);

system.out.println("http version:\t" + firstline[2]);

system.out.println();

stringbuilder sendstring = new stringbuilder();

sendstring.append("http/1.1 200 ok\r\n");

sendstring.append("content-type:text/html;charset="+localcharset+"\r\n");

sendstring.append("\r\n");

sendstring.append("

show");

sendstring.append("received:
");

for (string s : requestmessage){

sendstring.append(s + "
");

}

sendstring.append("

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值