客户端可以给服务端发消息,服务端也可以给客户端反馈。
注释最好清晰点,说明selector,channel,buffer的用法的实例。谢谢各位大牛,小弟刚学nio,c/s通信,希望给一个易懂的例子,谢谢大家。!:)

解决方案 »

  1.   

    楼主OUT了,现在都用MINA了,apache上有现成的文档例子。
      

  2.   

    服务器端代码为:   
    29.  
    30.package helloweenpad;   
    31.  
    32.import java.io.FileInputStream;   
    33.import java.net.InetSocketAddress;   
    34.import java.net.Socket;   
    35.import java.nio.ByteBuffer;   
    36.import java.nio.CharBuffer;   
    37.import java.nio.channels.SelectionKey;   
    38.import java.nio.channels.Selector;   
    39.import java.nio.channels.ServerSocketChannel;   
    40.import java.nio.channels.SocketChannel;   
    41.import java.nio.charset.Charset;   
    42.import java.nio.charset.CharsetDecoder;   
    43.import java.nio.charset.CharsetEncoder;   
    44.import java.util.Iterator;   
    45.import java.util.Properties;   
    46.  
    47.public class MyFirstNIOServer {   
    48.  
    49.public static final int PORT = 12315;   
    50.  
    51.protected Selector selector;   
    52.protected Charset charset = Charset.forName("UTF-8");   
    53.protected CharsetEncoder charsetEncoder = charset.newEncoder();   
    54.protected CharsetDecoder charsetDecoder = charset.newDecoder();   
    55.  
    56.protected Properties talks = new Properties();   
    57.  
    58.int clientCount;   
    59.  
    60.public MyFirstNIOServer() throws Exception {   
    61.  
    62.talks.load(new FileInputStream("E:\\talk.properties"));   
    63.  
    64.selector = Selector.open();   
    65.  
    66.ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();   
    67.serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); // port   
    68.serverSocketChannel.configureBlocking(false);   
    69.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// register   
    70.  
    71.p("Server localhost:" + PORT + " started. waiting for clients. ");   
    72.  
    73.while (true) {   
    74.   // selector 线程。select() 会阻塞,直到有客户端连接,或者有消息读入   
    75.   selector.select();   
    76.   Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();   
    77.   while (iterator.hasNext()) {   
    78.  
    79.    SelectionKey selectionKey = iterator.next();   
    80.    iterator.remove(); // 删除此消息   
    81.  
    82.    // 并在当前线程内处理。(为了高效,一般会在另一个线程中处理此消息,例如使用线程池等)   
    83.    handleSelectionKey(selectionKey);   
    84.   }   
    85.}   
    86.  
    87.}   
    88.  
    89.public void handleSelectionKey(SelectionKey selectionKey) throws Exception {   
    90.  
    91.if (selectionKey.isAcceptable()) {   
    92.  
    93.   // 有客户端进来   
    94.   clientCount++;   
    95.  
    96.   ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();   
    97.   SocketChannel socketChannel = serverSocketChannel.accept();   
    98.   socketChannel.configureBlocking(false);   
    99.   Socket socket = socketChannel.socket();   
    100.  
    101.   // 立即注册一个 OP_READ 的SelectionKey, 接收客户端的消息   
    102.   SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);   
    103.   key.attach("第 " + clientCount + " 个客户端 [" + socket.getRemoteSocketAddress() + "]: ");   
    104.  
    105.   p(key.attachment() + "\t[connected] =========================================");   
    106.  
    107.} else if (selectionKey.isReadable()) {   
    108.  
    109.   // 有消息进来   
    110.  
    111.   ByteBuffer byteBuffer = ByteBuffer.allocate(100);   
    112.   SocketChannel socketChannel = (SocketChannel) selectionKey.channel();   
    113.  
    114.   try {   
    115.    int len = socketChannel.read(byteBuffer);   
    116.  
    117.    // 如果len>0,表示有输入。如果len==0, 表示输入结束。需要关闭 socketChannel   
    118.    if (len > 0) {   
    119.  
    120.     byteBuffer.flip();   
    121.     String msg = charsetDecoder.decode(byteBuffer).toString();   
    122.  
    123.     // 根据客户端的消息,查找到对应的输出   
    124.     String newMsg = talks.getProperty(msg);   
    125.     if (newMsg == null)   
    126.      newMsg = "Sorry? I don't understand your message. ";   
    127.  
    128.     // UTF-8 格式输出到客户端,并输出一个'n'   
    129.  
    130.     socketChannel.write(charsetEncoder.encode(CharBuffer.wrap(newMsg + "\n")));   
    131.     p(selectionKey.attachment() + "\t[recieved]: " + msg + " ----->\t[send]: " + newMsg);   
    132.  
    133.    } else {   
    134.     // 输入结束,关闭 socketChannel   
    135.     p(selectionKey.attachment() + "read finished. close socketChannel. ");   
    136.     socketChannel.close();   
    137.    }   
    138.  
    139.   } catch (Exception e) {   
    140.  
    141.    // 如果read抛出异常,表示连接异常中断,需要关闭 socketChannel   
    142.    e.printStackTrace();   
    143.  
    144.    p(selectionKey.attachment() + "socket closed? ");   
    145.    socketChannel.close();   
    146.   }   
    147.  
    148.} else if (selectionKey.isWritable()) {   
    149.   p(selectionKey.attachment() + "TODO: isWritable() ???????????????????????????? ");   
    150.} else if (selectionKey.isConnectable()) {   
    151.   p(selectionKey.attachment() + "TODO: isConnectable() ????????????????????????? ");   
    152.} else {   
    153.   p(selectionKey.attachment() + "TODO: else. ");   
    154.}   
    155.  
    156.}   
    157.  
    158.public static void p(Object object) {   
    159.System.out.println(object);   
    160.}   
    161.  
    162.public static void main(String[] args) throws Exception {   
    163.new MyFirstNIOServer();   
    164.}   
    165.  
    166.}   
    //add by 郄永军
      

  3.   

    客户端:
    package helloweenpad;   
    171.  
    172.import java.io.BufferedReader;   
    173.import java.io.FileInputStream;   
    174.import java.io.InputStream;   
    175.import java.io.InputStreamReader;   
    176.import java.io.OutputStream;   
    177.import java.net.Socket;   
    178.import java.util.Properties;   
    179.import java.util.Random;   
    180.  
    181.public class MyFirstNIOClientTest extends Thread {   
    182.  
    183.public static final String HOST = "localhost";   
    184.public static final int PORT = 12315;   
    185.  
    186.boolean exist = false;   
    187.  
    188.Properties talks = new Properties();   
    189.Random random = new Random();   
    190.String[] keys;   
    191.  
    192.int messageCount = 0;   
    193.  
    194.public void run() {   
    195.  
    196.try {   
    197.  
    198.   // 对话内容   
    199.   talks.load(new FileInputStream("E:\\talk.properties"));   
    200.  
    201.   // 客户端发送 "=" 左边的内容   
    202.   keys = new String[talks.size()];   
    203.   talks.keySet().toArray(keys);   
    204.  
    205.   Socket socket = new Socket(HOST, PORT);   
    206.  
    207.   OutputStream ous = socket.getOutputStream();   
    208.   InputStream ins = socket.getInputStream();   
    209.  
    210.   // 接收线程,接收服务器的回应   
    211.   RecieverThread reciever = new RecieverThread(ins);   
    212.   reciever.start();   
    213.  
    214.   while (!exist) {   
    215.  
    216.    messageCount++;   
    217.  
    218.    // 选择一个随机消息   
    219.    String msg = chooseMessage();   
    220.  
    221.    synchronized (ins) {   
    222.  
    223.     // 发送给服务器端   
    224.     ous.write(msg.getBytes("UTF-8"));   
    225.  
    226.     System.out.println("[send]\t" + messageCount + ": " + msg);   
    227.  
    228.     // 然后等待接收线程   
    229.     ins.wait();   
    230.    }   
    231.  
    232.    if (msg.equals("Bye")) {   
    233.     break;   
    234.    }   
    235.   }   
    236.  
    237.   ins.close();   
    238.   ous.close();   
    239.   socket.close();   
    240.  
    241.} catch (Exception e) {   
    242.   e.printStackTrace();   
    243.}   
    244.  
    245.}   
    246.  
    247.public String chooseMessage() {   
    248.  
    249.int index = random.nextInt(keys.length);   
    250.String msg = keys[index];   
    251.  
    252.// 如果 10 次就选中 Bye,则重新选择,为了使对话内容多一些   
    253.if (messageCount < 10 && msg.equalsIgnoreCase("Bye")) {   
    254.   return chooseMessage();   
    255.}   
    256.  
    257.return msg;   
    258.}   
    259.  
    260.// 接收线程   
    261.class RecieverThread extends Thread {   
    262.private InputStream ins;   
    263.  
    264.public RecieverThread(InputStream ins) {   
    265.   this.ins = ins;   
    266.}   
    267.  
    268.@Override  
    269.public void run() {   
    270.  
    271.   try {   
    272.    String line = null;   
    273.  
    274.    BufferedReader r = new BufferedReader(new InputStreamReader(   
    275.      ins, "UTF-8"));   
    276.  
    277.    // readLine()会阻塞,直到服务器输出一个 '\n'   
    278.    while ((line = r.readLine()) != null) {   
    279.  
    280.     System.out.println("[Recieved]: " + line);   
    281.  
    282.     synchronized (ins) {   
    283.      // 接收到服务器的消息,通知下主线程   
    284.      ins.notify();   
    285.     }   
    286.     if (line.trim().equals("Bye")) {   
    287.      exist = true;   
    288.      break;   
    289.     }   
    290.    }   
    291.   } catch (Exception e) {   
    292.    e.printStackTrace();   
    293.   }   
    294.}   
    295.  
    296.}   
    297.  
    298.public static void main(String[] args) throws Exception {   
    299.  
    300.// 开三个客户端线程   
    301.for (int i = 0; i < 3; i++) {   
    302.   try {   
    303.    new MyFirstNIOClientTest().start();   
    304.   } catch (Exception e) {   
    305.    e.printStackTrace();   
    306.   }   
    307.}   
    308.  
    309.}   
    310.}   //add by 郄永军