JAVA-NIO实现(二)

来源:互联网 发布:淘宝网艾莱依羽绒服 编辑:程序博客网 时间:2024/04/29 05:52

在上篇文章中,JAVA-NIO的实现用浏览器能访问到,但是自己写了HTTP的JAVA代码来访问,发现了一个问题,得不到服务端传回来的数据。所以从新改动了服务端的代码。扩充了使用线程来处理请求。

打开火狐进行请求的跟踪,发现没有响应码,而我再JAVA代码中却写了相关的响应码验证,导致返回失败。

客户端代码如下:红色部分为出现问题的地方

        String clientContent = "THIS IS MY TEST DATA";
        BufferedReader br = null;
        URL url = null;
        HttpURLConnection connection = null;
        try {
            url = new URL(urlPath);
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod(type);
            connection.setRequestProperty("Content-type",
                    "application/x-www-form-urlencoded");
            connection.setRequestProperty("User-Agent", "liuceshi");
            connection.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
            connection.setRequestProperty("Connection", "keep-alive");
            connection.connect();
            OutputStreamWriter out = new OutputStreamWriter(
                    connection.getOutputStream(), charset);
            out.write(clientContent+"  ===序列号"+threadName);
            out.flush();
            System.out.println(threadName+"-----send Data");
            if(connection.getResponseCode() == 200){
                br = new BufferedReader(new InputStreamReader(
                        connection.getInputStream(), charset));
                StringBuffer resBuffer = new StringBuffer();
                String resTemp = "";
                while ((resTemp = br.readLine()) != null) {
                    resBuffer.append(resTemp);
                }
                System.out.println("服务器端返回的结果:"+resBuffer.toString());
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            connection.disconnect();
        }


于是改动服务端代码,添加响应头:红色部分为新添加内容

package org.com.liu.reactor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/*
 * Selector事件注册机要保证是单例模式的,
 * ServerSocketChannel也要保证是单例模式的
 *
 *
 * 当然也可以想办法来对其进行多线程的扩展
 */

public class Reactor implements Runnable{
    
    
    private final static int SERVER_PORT = 9000;
    
    private final static String SERVER_URI = "127.0.0.1";
    
    //事件监听
    private static Selector selector = null;
    
    //服务器端套接字通道对象
    private static ServerSocketChannel serverSocketChannel = null;
    
    //缓存块大小
    private int BLOCK = 4096;
    
    
    /*******************这里一定要区分的是,服务器端的Channel和客户端的Cchannel事件区别*******************************/
    public Reactor() {
        try {
            
            //打开服务器端的通道
            serverSocketChannel  = ServerSocketChannel.open();
            
            //实例化单利模式的selector注册机
            selector = Selector.open();
            
            //服务器通道设置为非阻塞,如果为true 那么就为传统的阻塞方式
            serverSocketChannel.configureBlocking(false);
            
            /*
             * 通过这个选项,可以使多个Socket对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close方法关闭Socket连接后,
             * Socket对象所绑定的端口并不一定马上释放;系统有时在Socket连接关闭才会再确认一下是否有因为延迟面未到达的数据包,
             * 这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket类时完全不会感觉到。
                 * 这种处理机制对于随机绑定端口的Socket对象没有什么影响,
             * 但对于绑定在固定端口的Socket对象就可能会抛出“Address already in use: JVM_Bind”例外。
             * 因此,使用这个选项可以避免个例外的发生。
             */
            serverSocketChannel.socket().setReuseAddress(true);
            
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(SERVER_URI,SERVER_PORT));
            
            /*
             * 这里要注意:这里是将服务器端的Channel注册为可接听,而且服务器端的Channel也只能注册为OP_ACCEPT事件
             */
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            
            /*
             * 绑定处理事件:如果服务器端监听到有事件到来,交给Acceptor来处理
             */
            selectionKey.attach(new Acceptor());
            
            System.out.println("N-AServer is starting at " + SERVER_PORT + "......");
        } catch (IOException e) {
        }
    }

    @Override
    public void run() {
        while(true){
            try {
                if(selector.select() > 0){
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    System.out.println("目前已经接受的key的 size : " + selectionKeys.size());
                    while(iterator.hasNext()){
                        SelectionKey selectionKey = iterator.next();
                        iterator.remove();
                        if(!selectionKey.isValid() || !selectionKey.channel().isOpen()){
                            continue;
                        }
                        //这里打印判断没回接受到的事件的类型
                        System.out.println("表示是否可连接"+selectionKey.isAcceptable());
                        System.out.println("表示是否可读入"+selectionKey.isReadable());
                        System.out.println("表示是否可写出"+selectionKey.isWritable());
                        
                        //将之前绑定的OP_ACCEPT事件的对象取出来,调用该对象中的方法,基于接口调用
                        dispatch(selectionKey);
                    }
                }
            } catch (Exception e) {
                System.out.println("N-AServer cannot select events.....");
                e.printStackTrace();
            }
        }
    }
    
    public void dispatch (SelectionKey selectionKey){
        Runnable runnable = (Runnable) selectionKey.attachment();
        if(runnable != null){
            runnable.run();
        }
    }
    
    //请求接受事件的处理类,Reactor模式中的accept
    class Acceptor implements Runnable{

        @Override
        public void run() {
            SocketChannel socketChannel = null;
            try {
                /*
                 * 通过服务器端的Channel的接受,来接受一个客户端的Channel通道,
                 * 这里的设计是,服务器端只有一个serverSocketChannel通道来连接请求
                 */
                socketChannel = serverSocketChannel.accept();
                if(socketChannel != null){
                     System.out.println("=======》接收到来自客户端("  
                             + socketChannel.socket().getInetAddress().getHostAddress()  
                             + ")的连接《======");
                     System.out.println();
                     new RequestHandler(socketChannel,selector);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    
    //这是处理客户端Channel数据的线程,客户端通道也要在Selector注册机中注册时间
    class RequestHandler implements Runnable{
        
         //客户端通道
         private SocketChannel socketChannel;  
        
         private SelectionKey selectionKey;
        
         private String MIMEType = "application/json;charset=UTF-8";
        
         public RequestHandler(SocketChannel socketChannel , Selector selector) {
             System.out.println("启动线程来处理客户端Channel。。。。。。====>进入客户端通道的处理:");
             this.socketChannel = socketChannel;
             try {
                
                 //这里将"客户端"的阻塞方式设置为非阻塞,
                 socketChannel.configureBlocking(false);
                 
                 //为客户端通道注册读事件
                 selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
                 
                 //如果客户端发生了读事件,那么在run本类方法中来解析读事件,或者单独为客户端的时间处理写个handler
                 selectionKey.attach(this);
             } catch (IOException e) {  
                 e.printStackTrace();  
             }  
         }

        @Override
        public void run() {
             try {  
                 if(selectionKey.isReadable()) {  
                     read();  
                     selectionKey.interestOps(SelectionKey.OP_WRITE);  
                 }else if (selectionKey.isWritable()){
                     process();
                     selectionKey.cancel();
                     //采用流的方式向客户端输出内容,而不采用通道的方式
                     System.out.println("服务器发送消息成功!");
                 }  
             }catch (IOException e) {  
                 e.printStackTrace();  
             }  
        }
        public void read() throws IOException {
            ByteBuffer buffer = ByteBuffer.allocate(BLOCK);  
            socketChannel.read(buffer);  
            System.out.println("接收到来自客户端(" + socketChannel.socket().getInetAddress().getHostAddress()  
                                   + ")的消息:" );
            System.out.println(new String(buffer.array(),"UTF-8"));
        }
        
        public void process() throws IOException {
             String content = "这是我的测试数据";
            //生成HTTP 相应头
             String header="HTTP/1.0 200 OK\r\n"+  
                      "Server: N-AServer 1.0\r\n"+
                      "Content-length: "+content.length()+"\r\n"+  
                      "Content-type: "+MIMEType+"\r\n\r\n";

            ByteBuffer sendBuffer = ByteBuffer.allocate(2048);
            //想向客户端写入响应头
            sendBuffer.put(header.getBytes("GBK"));
            sendBuffer.put(content.getBytes());
            sendBuffer.flip();
            socketChannel.write(sendBuffer);
            socketChannel.close();
        }
    }
    
    public static void main(String[] args) {
        Runnable runnable = new Reactor();
        runnable.run();
    }
}




原创粉丝点击