- 说明
需要理解阻塞和非阻塞的区别,特别要注意非阻塞和异步不是一个概念,这个很容易弄错。云盘里面netty的书会讲这几个方面的区别,nodejs深入浅出关于异步编程章节里面 也会讲到网络通信底层的知识,可以看看下面文章:
http://blog.csdn.net/hguisu/article/details/7453390
http://www.cnblogs.com/dolphin0520/p/3916526.html
- Handler接口类
1 package study.socket.tcp.nonblock.simpleserver; 2 3 import java.io.IOException; 4 import java.nio.channels.SelectionKey; 5 6 public interface Handler { 7 8 public void handle(SelectionKey selectionKey) throws IOException; 9 }
- 请求连接Handler实现类
1 package study.socket.tcp.nonblock.simpleserver; 2 3 import java.io.IOException; 4 import java.nio.channels.SelectionKey; 5 import java.nio.channels.ServerSocketChannel; 6 import java.nio.channels.SocketChannel; 7 8 public class AcceptHandler implements Handler{ 9 10 @Override 11 public void handle(SelectionKey selectionKey) throws IOException { 12 System.out.println("开始处理连接请求"); 13 try { 14 Thread.sleep(60000); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 //得到事件发生的通道 19 ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel(); 20 //获得和客户端连接的通道 21 SocketChannel socketChannel = ssc.accept(); 22 //设置曾非阻塞模式 23 socketChannel.configureBlocking(false); 24 //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读权限 25 socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ, new RequestHandler()); 26 System.out.println("连接成功"); 27 } 28 29 }
- 请求处理Handler实现类
1 package study.socket.tcp.nonblock.simpleserver; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.nio.ByteBuffer; 6 import java.nio.CharBuffer; 7 import java.nio.channels.FileChannel; 8 import java.nio.channels.SelectionKey; 9 import java.nio.channels.SocketChannel; 10 import java.nio.charset.Charset; 11 import java.nio.charset.CharsetDecoder; 12 13 public class RequestHandler implements Handler{ 14 15 @Override 16 public void handle(SelectionKey selectionKey) throws IOException { 17 FileInputStream fis = null; 18 SocketChannel socketChannel = null; 19 try { 20 socketChannel = (SocketChannel) selectionKey.channel(); 21 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 22 socketChannel.read(byteBuffer); 23 byte[] data = byteBuffer.array(); 24 String msg = new String(data); 25 System.out.println("接收客户端的消息:" + msg); 26 byteBuffer.flip(); 27 String request = decode(byteBuffer); 28 System.out.println("客户端请求消息:" + request); 29 //生成http响应消息 30 StringBuffer sb = new StringBuffer("HTTP/1.1 200 OK "); 31 sb.append("Content-Type:text/html "); 32 //发送http响应第一行和响应头 33 socketChannel.write(encode(sb.toString())); 34 35 //获取http请求的第一行 36 String firstLineOfRequst = request.substring(0, request.indexOf(" ")); 37 String filePath = SimpleHttpServer.class.getResource("/").getPath(); 38 System.out.println("路径:" + filePath); 39 System.out.println("测试"); 40 if(firstLineOfRequst.indexOf("login.html") != -1) { 41 fis = new FileInputStream(filePath + "study/socket/block/httpserver/login.html"); 42 }else { 43 fis = new FileInputStream(filePath + "study/socket/block/httpserver/hello.html"); 44 } 45 FileChannel fc = fis.getChannel(); 46 fc.transferTo(0, fc.size(), socketChannel); 47 } catch(Exception e){ 48 e.printStackTrace(); 49 }finally { 50 if(fis != null) { 51 try { 52 fis.close(); 53 } catch (IOException e1) { 54 e1.printStackTrace(); 55 } 56 } 57 if(socketChannel != null) { 58 try { 59 socketChannel.close(); 60 } catch (IOException e1) { 61 e1.printStackTrace(); 62 } 63 } 64 } 65 } 66 //编码 67 private String decode(ByteBuffer bb) throws Exception{ 68 Charset charset = Charset.forName("utf-8"); 69 CharsetDecoder decoder = charset.newDecoder(); 70 CharBuffer charBuffer = decoder.decode(bb); 71 System.out.println( " charBuffer= " + charBuffer); 72 System.out.println(charBuffer.toString()); 73 System.out.println("编码"); 74 return charBuffer.toString(); 75 } 76 //解码 77 private ByteBuffer encode(String str) { 78 System.out.println("解码"); 79 return ByteBuffer.wrap(str.getBytes()); 80 } 81 }
- nio编写的服务端类
1 package study.socket.tcp.nonblock.simpleserver; 2 3 import java.io.IOException; 4 import java.net.InetSocketAddress; 5 import java.nio.channels.SelectionKey; 6 import java.nio.channels.Selector; 7 import java.nio.channels.ServerSocketChannel; 8 import java.util.Iterator; 9 10 /** 11 * 非阻塞式简易http服务器 12 * @author yj 13 * 14 */ 15 public class SimpleHttpServer { 16 17 private int port = 8080; 18 private Selector selector; 19 20 public void initServer() throws IOException{ 21 //打开服务器套接字通道 22 ServerSocketChannel ssc = ServerSocketChannel.open(); 23 //将服务端套接字通道连接方式改为非阻塞模式 24 ssc.configureBlocking(false); 25 //绑定端口 26 ssc.socket().bind(new InetSocketAddress(port)); 27 //打开通道选择器 28 this.selector = Selector.open(); 29 //将服务器套接字通道的OP_ACCEPT事件注册到通道选择器上 30 ssc.register(selector, SelectionKey.OP_ACCEPT, new AcceptHandler()); 31 } 32 33 public void service() throws IOException{ 34 while(true) { 35 int n = selector.select(); 36 System.out.println("开始处理请求"); 37 if(n == 0) continue; 38 //获取通道选择器事件 39 Iterator<SelectionKey> itr = this.selector.selectedKeys().iterator(); 40 while(itr.hasNext()) { 41 SelectionKey sk = null; 42 try { 43 sk = itr.next(); 44 itr.remove(); 45 Handler handler = (Handler) sk.attachment(); 46 handler.handle(sk); 47 } catch(Exception e) { 48 e.printStackTrace(); 49 } 50 } 51 System.out.println("请求处理完成"); 52 } 53 } 54 55 public static void main(String[] args) { 56 try { 57 SimpleHttpServer2 nioServer = new SimpleHttpServer2(); 58 nioServer.initServer(); 59 nioServer.service(); 60 } catch (Exception e) { 61 e.printStackTrace(); 62 } 63 } 64 }
- html文件
html文件使用《简易阻塞http服务器》中的两个html文件,注意文件目录与上面类文件所在目录一致。
- 测试
启动main方法,浏览器输入:http://ip:port/或http://ip:port/hello.html访问hello.html,输入http://ip:port/login.html访问login.html.
- 云盘资料
http://yunpan.cn/cwJGv4vUm9kTf 访问密码 a74b 里面包含了io.socket和nio.socket的一些简单代码,还有上面说的netty的书。