使用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会覆盖,所以在读写处理中交替注册