使用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會覆蓋,所以在讀寫處理中交替注冊