最近闲来无事想做个小型的聊天工具,客户端用android已经做好了,跟服务器的交互不想使用Http的方式,改用socket长连接,这样的话消息能及时送达就避免了http不断轮询的消耗。
网上关于Java NIO大部分都是些基础的例子和结构,关于长连接的实在是很少,
现在已经能正常监听请求和读写内容,但是没办法让服务器发送消息到指定的一台或若干台客户端,老的JavaIO可以通过保存Socket,但是nio每次事件都是一个新的SelectionKey,保存了也是旧的(目前的理解),请教各位,如何才能保存下跟每个客户端的会话呢,如下是部分代码public class SeekServer extends Thread{
private final int ACCPET_PORT = 55555;
private final int TIME_OUT = 3000;
private Selector mSelector = null;
private ServerSocketChannel mSocketChannel = null;
private ServerSocket mServerSocket = null;
private InetSocketAddress mAddress = null;

public SeekServer() {
long sign = System.currentTimeMillis();
try {
mSocketChannel = ServerSocketChannel.open();
if(mSocketChannel == null) {
System.out.println("can't open server socket channel");
}
mServerSocket = mSocketChannel.socket();
mAddress = new InetSocketAddress(ACCPET_PORT);
mServerSocket.bind(mAddress);
Log.i("server bind port is " + ACCPET_PORT);
mSelector = Selector.open();
mSocketChannel.configureBlocking(false);
SelectionKey key = mSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT);
key.attach(new Acceptor());
Log.i("Seek server startup in " + (System.currentTimeMillis() - sign) + "ms!");
} catch (ClosedChannelException e) {
Log.e(e.getMessage());
} catch (IOException e) {
Log.e(e.getMessage());

}

public void run() {
Log.i("server is listening...");
while(!Thread.interrupted()) {
try {
if(mSelector.select(TIME_OUT) > 0) {
Log.i("find a new selection key");
Set<SelectionKey> keys = mSelector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
SelectionKey key = null;
while(iterator.hasNext()) {
key = iterator.next();
Runnable at = (Runnable) key.attachment();
if(at != null) {
at.run();
}
keys.clear();
}
}
} catch (IOException e) {
Log.e(e.getMessage());
}
}
} class Acceptor implements Runnable{ public void run(){
try {
SocketChannel sc = mSocketChannel.accept();
new Handler(mSelector, sc);//开始处理这个Channel
} catch (ClosedChannelException e) {
Log.e(e);
} catch (IOException e) {
Log.e(e);
}
}
}
}

解决方案 »

  1.   

    sokect 其实我也不懂,  过些天会看看
      

  2.   

    肯定能吧,不是有NIO的聊天室吗?
      

  3.   

    你不是已经拿到了SocketChannel吗,把准备需要发送给客户端的数据在这个上面注册一个写事件.
    SocketChannel channel = (SocketChannel) key.channel();
    channel.register(ioSelector, SelectionKey.OP_WRITE, "这是传回的数据.");
    其中第三个参数就是你要转给客户端的数据封装,接着你只需要在写事件的响应方法中写输出逻辑就行了.这里简单用了字符串.
    private void writeable(SelectionKey key) throws IOException {
            SocketChannel socketChannel = (SocketChannel) key.channel();
            byte[] data = ((String) key.attachment()).getByte("utf8);        int len = 0;
            
            ByteBuffer buff = ByteBuffer.wrap(data,0,data.length);
            len = socketChannel.write(buff);        if (len == EOF_FLAG) {
                //认为客户端已经被关闭了
                throw new ConnectException("close channel.");
            }        //判断数据没有输出完的处理
        }
    这里有一点要注意,因为是异步的IO所以数据不能保证一次write就能输出完.所以记录下已经输出的字节数,然后保存下
    剩下的数据以便下一次写事件时输出.最后数据完全输出完毕后建议将写事件从这个管道中去掉,不然一直有写就绪的事件浪费CPU.
    key.interestOps(SelectionKey.OP_READ);
    这句话就会接收读就绪事件了.
      

  4.   


    比如有1-10个用户,我想把消息发送给2,3,4 我就得事先保存下SocketChannel,然后去这个集合里找2,3,4对应的SocketChannel,是不是切换读写事件就ok了呢?
      

  5.   

    可以考虑根据TCP协议的原理,用心跳包的方式连接确认。我们有个项目就是这样做的。
      

  6.   


    对 项目里下一步就会有心跳包,但首先我要把这些Channel存起来,然后去给这些Channel写数据,比较不理解的是每次都要切换read和write,不知道这样写会不会有什么问题
      

  7.   

    我做过阻塞式socket的简单的聊天室,我用的是cookie保存客户端信息,不知道你能否借鉴。
      

  8.   


    我也做过,但我在服务器端保存客户端会话,而且是nio
      

  9.   

    两天不可能搞定 NIO 长连接的?
      

  10.   


    你是需要对指定的SocketChannel发送数据,当然你保存的时候必须对这些SocketChannel进行标识。比如对每一个连接会产生一个唯一的ID,再利用一个Map来持有。
      

  11.   


    是应该定义一个全局的map存储链接标识以及对应的SocketChannel。
      

  12.   

    上手弄nio的时候,就觉得太麻烦,太贴近底层,正好当时看到介绍mina的文章,觉得更能贴近业务,就直接用mina了。
      

  13.   


    连Mina底层的实现都不知道的话就直接去使用,感觉有点不充实
      

  14.   

    一直忘记来csdn了,把前段时间写的NIO雏形放到博客园了
    传送门
      

  15.   

    楼主,其实可以不用socket,我们组一直用servlet 3.0的异步功能和android保持长连接的,只需要写一个监听类,然后在请求进来,处理完逻辑后如果不用返回,挂起即可,完全能满足你的需求.
      

  16.   


    以前貌似听说过Tomcat7有这种功能哈,有相关的例子吗跪求
      

  17.   

    当然有,做过不止一个这样的项目了,不过我一直用的glassfish做服务器,tomcat支持的不是很好.你留个邮箱,或者QQ我晚上回家跟你讨论讨论.
      

  18.   

    NIO啊~~NIO 我写个非阻塞的通信就很郁闷啊~表示勉强看懂。
    帮忙顶一下~~
      

  19.   

    NIO啊~~NIO 我写个非阻塞的通信就很郁闷啊~表示勉强看懂。
    帮忙顶一下~~
      

  20.   

    package com.meran.nio.echoServer;import java.io.IOException;
    import java.nio.ByteBuffer; 
    import java.nio.channels.ClosedChannelException;
    import java.nio.channels.ServerSocketChannel; 
    import java.nio.channels.SocketChannel;
    import java.nio.channels.Selector;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.SelectableChannel;
    import java.net.Socket;
    import java.net.ServerSocket; 
    import java.net.InetSocketAddress;
    import java.util.Iterator;public class Server {
    public static final int PORT=8033;
       public static void main(String [] args) throws IOException{
       Server s=new Server();
       s.go();
       }
       private static ByteBuffer buff=ByteBuffer.allocateDirect(1024);
       
       public void go() throws IOException {
       ServerSocketChannel serverChannel=ServerSocketChannel.open();
       ServerSocket serverSocket=serverChannel.socket();
       serverSocket.bind(new InetSocketAddress(PORT));
       System.out.println("server start to listen port"+PORT);
       Selector selector =Selector.open();
       serverChannel.configureBlocking(false);
       serverChannel.register(selector, SelectionKey.OP_ACCEPT);
       while (true){
       int n=selector.select();
       if(n==0){
       continue;
       }
       else{
    //   System.out.println(n+"complicated");
       Iterator it =selector.selectedKeys().iterator();
       while( it.hasNext()){
       SelectionKey key=(SelectionKey) it.next();
       if(key.isAcceptable()){

       ServerSocketChannel server = (ServerSocketChannel) key.channel();
       SocketChannel channel=server.accept();
       registerChannel(selector, channel, SelectionKey.OP_READ); 
       sayHello(channel);
       }
     if (key.isReadable()) 
       
     {
     
      readDataFromSocket(key); 
       
           }
       it.remove();
       
       }
       }
       }
       }
       
       private void readDataFromSocket(SelectionKey key) throws IOException {
    SocketChannel channel=(SocketChannel)key.channel();
    int count =0;
    buff.clear();
    while((count=channel.read(buff))>0){
    buff.flip();
    while(buff.hasRemaining()){
    channel.write(buff);
    }

    buff.clear();

    }
    if(count<0)
    channel.close();


    }private void sayHello(SocketChannel channel) throws IOException {
    buff.clear();
    buff.put("hello\r\n".getBytes());
    buff.flip();
    channel.write(buff);

    }private void registerChannel(Selector selector, SocketChannel channel,
    int opRead) throws IOException {
    if(channel==null)
    return;
    channel.configureBlocking(false);
    channel.register(selector, SelectionKey.OP_READ);



    }}
     获得 SocketChannel 直接 注册感兴趣的事件就是了
      

  21.   

    以前做个项目 加上心跳以后逻辑更乱 郁闷  nio不是一两天的功夫啊 = =
      

  22.   

    前段时间已经脱离JavaWeb了,现在搞Android 所以面向NIO的话合适点,留个邮箱以后方便交流
    [email protected]  公司不让用扣扣,你懂的
      

  23.   

    来迟了,想来问下你的android客户端是怎样和java nio写的服务器端保持长连接的,采用socket吗?