Java NIO網絡編程demo


使用Java NIO進行網絡編程,看下服務端的例子

import java.io.IOException;
import java.net.InetAddress;
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.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Server {
    private static int PORT = 8000;
    private static final long PAUSE_BETWEEEN_MSGS = 10; // millisecs
    private static ByteBuffer echoBuffer = ByteBuffer.allocate(4);
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(256);
    private static ConcurrentHashMap<Integer, SocketChannel> chm
            = new ConcurrentHashMap<Integer, SocketChannel>();
    private static int msg = 0;

public static void main(String args[]) throws Exception {
        Selector selector = Selector.open();
        // Open a listener on each port, and register each one
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        InetSocketAddress address = new InetSocketAddress("localhost",PORT);
        ssc.bind(address);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Going to listen on " + PORT);
        while (true){
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectedKeys.iterator();

            System.out.println(selectedKeys.size());
            while (it.hasNext()){
                String msg = new String();
                SelectionKey key = (SelectionKey) it.next();
                if(key.isAcceptable()){
                    ServerSocketChannel sscNew = (ServerSocketChannel) key
                            .channel();
                    SocketChannel sc = sscNew.accept();
                    sc.configureBlocking(false);
                    // Add the new connection to the selector
                    sc.register(selector, SelectionKey.OP_READ);
                    // Add the socket channel to the list
                    chm.put(sc.hashCode(), sc);
                    it.remove();
                }else if(key.isReadable()){
                    SocketChannel sc = (SocketChannel) key.channel();
                    int code = 0;
                    while ((code = sc.read(echoBuffer)) > 0) {
                        byte b[] = new byte[echoBuffer.position()];
                        echoBuffer.flip();
                        echoBuffer.get(b);
                        msg+=new String(b, "UTF-8");
                    }
//                    if(msg.length()>1)
//                        msg = msg.substring(0, msg.length()-2);

                    //client關閉時,收到可讀事件,code = -1
                    if (code == -1 ||
                            msg.toUpperCase().indexOf("BYE")>-1){
                        chm.remove(sc.hashCode());
                        sc.close();
                    } else {
                        //code=0,消息讀完或者echoBuffer空間不夠時,部分消息內容下一次select后收到
                        echoBuffer.clear();
                    }
                    System.out.println("msg: " + msg  + " from: " + sc + "code:  " + code );
                    it.remove();

                    //注冊可寫通知
                    sc.register(selector,SelectionKey.OP_WRITE);
                }else if(key.isWritable()){
                    SocketChannel client = (SocketChannel) key.channel();
                    String sendTxt = "Message from Server";
                    sendBuffer.put(sendTxt.getBytes());
                    sendBuffer.flip();
                    int code = 0;

                    //如果sendBuffer內容一次沒有寫完,會在下一次事件中處理嗎?
                    while (client.write(sendBuffer) != 0){
                    }
                    if (code == -1 ){
                        chm.remove(client.hashCode());
                        client.close();
                    } else {
                        //code=0,消息寫完
                        sendBuffer.clear();
                    }
                    it.remove();
                    System.out.println("Send message to client ");

                    //在讀通知里面注冊為寫事件,所以這里還需要注冊為讀,否則不在接受客戶端消息
                    client.register(selector,SelectionKey.OP_READ);
                }
            }
            Thread.sleep(5000);
        }
    }
}

使用windows telnet與服務端交互,在windows telnet中,需要使用send命令來按行發送消息,如下所示

一些說明:

1.select操作為阻塞操作,直至至少一個事件發生

2.server端只需注冊accept事件

3.read、write為非阻塞操作,需要在代碼中判斷返回結果

4.read操作,如果接受的buffer大小不夠,會在下一次select操作中接受

5.write操作呢?如果消息沒有發完,怎么處理,下面這種循環么?

while (buffer.hasRemaining()){
                socketChannel.write(buffer);
            }

6.register會覆蓋,所以在讀寫處理中交替注冊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM