作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
Java Socket编程(四) 异步服务器

Custom Tab

下面给出代码。

package com.yjp.server;  
  
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;  
  
public class ToUpperTCPNonBlockServer {  
    //服务器IP  
    public static final String SERVER_IP = "127.0.0.1";  
      
    //服务器端口号  
    public static final int SERVER_PORT = 10005;  
      
    //请求终结字符串  
    public static final char REQUEST_END_CHAR = '#';  
      
    public void startServer(String serverIP, int serverPort) throws IOException {  
        //使用NIO需要用到ServerSocketChannel  
        //其中包含一个ServerSocket对象  
        ServerSocketChannel serverChannel = ServerSocketChannel.open();  
          
        //创建地址对象  
        InetSocketAddress localAddr = new InetSocketAddress(serverIP, serverPort);  
          
        //服务器绑定地址  
        serverChannel.bind(localAddr);  
          
        //设置为非阻塞  
        serverChannel.configureBlocking(false);  
          
        //注册到selector,会调用ServerSocket的accept  
        //我们用selector监听accept能否返回  
        //当调用accept可以返回时,会得到通知  
        //注意,是可以返回,还需要调用accept  
        Selector selector = Selector.open();  
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);  
          
        while (true) {  
              
            //调用select,阻塞在这里,直到有注册的channel满足条件  
            selector.select();  
              
            //如果走到这里,有符合条件的channel  
            //可以通过selector.selectedKeys().iterator()拿到符合条件的迭代器  
            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();  
              
            //处理满足条件的keys  
            while (keys.hasNext()) {  
                //取出一个key并移除  
                SelectionKey key = keys.next();  
                keys.remove();  
                try {  
                    if (key.isAcceptable()) {  
                        //有accept可以返回  
                        //取得可以操作的channel  
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();  
                          
                        //调用accept完成三次握手,返回与客户端可以通信的channel  
                        SocketChannel channel = server.accept();  
                          
                        //将该channel置非阻塞  
                        channel.configureBlocking(false);  
                          
                        //注册进selector,当可读或可写时将得到通知,select返回  
                        channel.register(selector, SelectionKey.OP_READ);  
                    } else if (key.isReadable()) {  
                        //有channel可读,取出可读的channel  
                        SocketChannel channel = (SocketChannel) key.channel();  
                          
                        //创建读取缓冲区,一次读取1024字节  
                        ByteBuffer buffer = ByteBuffer.allocate(1024);  
                        channel.read(buffer);  
                          
                        //锁住缓冲区,缓冲区使用的大小将固定  
                        buffer.flip();  
                          
                        //附加上buffer,供写出使用  
                        key.attach(buffer);  
                        key.interestOps(SelectionKey.OP_WRITE);  
                    } else if (key.isWritable()) {  
                        //有channel可写,取出可写的channel  
                        SocketChannel channel = (SocketChannel) key.channel();  
                          
                        //取出可读时设置的缓冲区  
                        ByteBuffer buffer = (ByteBuffer) key.attachment();  
                          
                        //将缓冲区指针移动到缓冲区开始位置  
                        buffer.rewind();  
                          
                        //读取为String  
                        String recv = new String(buffer.array());  
                          
                        //清空缓冲区  
                        buffer.clear();  
                        buffer.flip();  
                          
                        //写回数据  
                        byte[] sendBytes = recv.toUpperCase().getBytes();  
                        channel.write(ByteBuffer.wrap(sendBytes));  
                    }  
                } catch (IOException e) {  
                    //当客户端Socket关闭时,会走到这里,清理资源  
                    key.cancel();  
                    try {  
                        key.channel().close();  
                    } catch (IOException e1) {  
                        e1.printStackTrace();  
                    }  
                }  
            }  
        }  
    }  
      
    public static void main(String[] args) {  
        ToUpperTCPNonBlockServer server = new ToUpperTCPNonBlockServer();  
        try {  
            server.startServer(SERVER_IP, SERVER_PORT);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}

可以看到,新的服务器使用了ServerSocketChannel以及SocketChannel,而不再是之前ServerSocket以及Socket,异步服务器的好处在于,服务器没有工作可做的时候,会等在select调用上,不会占用系统资源,而当不同的条件满足时,又可以第一时间被唤醒,执行相应的操作,所以无论从资源的利用上,还是从响应的及时性上都优于前两种。另外,如果write和read的时间比较长,处理也可以放到线程中处理,这样就结合了并发服务器的优势。

转载自:http://blog.csdn.net/yjp19871013/article/details/53635116

 Java Socket编程(一) Socket编程原理及基本概念

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=501

Java Socket编程(二) 几种常见的服务器模型

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=502

 Java Socket编程(三) 并发服务器

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=503


Home