这几天一直在研究这东西,然后自己照着例子写了点东东。不过一直测试都有问题,在这里贴上代码,,,希望好心人能够耐心解答。感激不尽!以下是服务端代码,我只想先实现收信息功能,能够在服务端的JTextArea上显示出来。package server;import java.io.IOException;
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.Iterator;
import java.util.Set;import javax.swing.*;import common.*;public class TetrisServer extends JFrame implements Constants{
ServerSocketChannel serverSocket;
SocketChannel socket;
Selector selector;

// SelectionKey [][] tables=new SelectionKey[10][2];

JTextArea jtaMessage=new JTextArea();


public TetrisServer(){
setSize(300, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new JScrollPane(jtaMessage));
setVisible(true);

try {
serverSocket=ServerSocketChannel.open();
selector=Selector.open();
serverSocket.socket().setReuseAddress(true);
serverSocket.socket().bind(new InetSocketAddress(8000));
jtaMessage.append("start server successfully!\n");

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
public void accept() {
for(;;){
try {
socket=serverSocket.accept();
socket.configureBlocking(false);
jtaMessage.append("client linked:"+socket.socket().getInetAddress()+":"+socket.socket().getPort()+"\n");
// ByteBuffer buffer=ByteBuffer.allocate(1024);
Message message=new Message();
synchronized (gate) {
selector.wakeup();
socket.register(selector, SelectionKey.OP_READ, message);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private Object gate=new Object();
public void service() throws IOException{
for(;;){
synchronized (gate) {}
int n=selector.select();
if(n==0) continue;

Set<SelectionKey> readyKeys=selector.selectedKeys();
Iterator<SelectionKey> iterator=readyKeys.iterator();
while(iterator.hasNext()){
SelectionKey key=null;
try {
key=iterator.next();
iterator.remove();
if(key.isReadable()){
jtaMessage.append("server read");
receive(key);

}
if(key.isWritable()){
send(key);
jtaMessage.append("server write");
}
} catch (IOException e) {
// TODO: handle exception
}catch (ClassNotFoundException e) {
// TODO: handle exception
}
}
}

}
public void receive(SelectionKey key) throws IOException,ClassNotFoundException{
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
SocketChannel socket=(SocketChannel) key.channel();
socket.read(readBuffer);
readBuffer.flip();
jtaMessage.append(new String(readBuffer.array())+"\n");

// Message playerMessage=null;
// Message sendMessage=null;
// SelectionKey opponentKey=null; }
public void send(SelectionKey key) throws IOException{
// ByteBuffer sendBuffer=(ByteBuffer)key.attachment();
Message sendMessage=(Message)key.attachment();
SocketChannel socket=(SocketChannel)key.channel();
socket.write(ByteUtil.getByteBuffer(sendMessage));
}
public boolean checkPlayMessage(String name,String password){
return true;
}
/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub

final TetrisServer frameServer=new TetrisServer();
Thread accept=new Thread(){
public void run(){
frameServer.accept();
}
};
accept.start();

try {
frameServer.service();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}}以下是客户端代码,我想有个面板输入信息,通过button触发事件来发出信息。但是,事实却是要发出一条信息必须要点击button 不定次数,几次到十几次不等= =才能够在服务端显示出该信息。看了很久不知道问题出在哪,求解答package client;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;import common.*;public class TetrisClient implements Constants{
SocketChannel socket;
Selector selector;
ByteBuffer sendBuffer=ByteBuffer.allocate(1024);
ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);
private Charset charset=Charset.forName("GBK");

public TetrisClient(){
try {
socket=SocketChannel.open();
socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8000));
socket.configureBlocking(false);
System.out.println("与服务器的连接建立成功");
    selector=Selector.open();
    
//     talkToServer();
    
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void packMessage(String name,String password,int position,int state){
Message message=new Message();
message.position=position;
message.state=state;
message.playerName=name;
message.password=password;
// try {
// sendBuffer=ByteUtil.getByteBuffer(message);
sendBuffer=ByteBuffer.wrap(name.getBytes());
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
public void talkToServer() throws IOException{
socket.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);

while(selector.select()>0){
Set<SelectionKey> readyKeys=selector.selectedKeys();
Iterator<SelectionKey> iterator=readyKeys.iterator();

while(iterator.hasNext()){
SelectionKey key=(SelectionKey)iterator.next();
iterator.remove();
if(key.isReadable()){
receive(key);
}
if(key.isWritable()){
send(key);
}
}
// System.out.println("talk to server");
}
}
public void send(SelectionKey key)throws IOException{
    SocketChannel socketChannel=(SocketChannel)key.channel();
    synchronized(sendBuffer){
        sendBuffer.flip(); //把极限设为位置
        socketChannel.write(sendBuffer);
        sendBuffer.compact();
// System.out.println("send");
     }
  }
  public void receive(SelectionKey key)throws IOException{
    SocketChannel socketChannel=(SocketChannel)key.channel();
    socketChannel.read(receiveBuffer);
    receiveBuffer.flip();
    String receiveData=decode(receiveBuffer);     if(receiveData.indexOf("\n")==-1)return;     String outputData=receiveData.substring(0,receiveData.indexOf("\n")+1);
    System.out.print(outputData);
    if(outputData.equals("echo:bye\r\n")){
        key.cancel();
        socketChannel.close();
        System.out.println("关闭与服务器的连接");
        selector.close();
        System.exit(0);
    }     ByteBuffer temp=encode(outputData);
    receiveBuffer.position(temp.limit());
    receiveBuffer.compact();
  }   public String decode(ByteBuffer buffer){  //解码
    CharBuffer charBuffer= charset.decode(buffer);
    return charBuffer.toString();
  }
  public ByteBuffer encode(String str){  //编码
    return charset.encode(str);
  }}以下是客户端的main函数package client;import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;public class Login extends JFrame{
JTextField jtfName=new JTextField(10);
JTextField jtfPassword=new JTextField(10);
JButton jbtLogin=new JButton("Login");
static TetrisClient client;
public Login(){
setLayout(new FlowLayout());
add(jtfName);
add(jtfPassword);
add(jbtLogin);
setSize(200, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);

jbtLogin.addActionListener(new LoginListener());

}
public class LoginListener implements ActionListener{ @Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("login");
client.packMessage(jtfName.getText(), jtfPassword.getText(), client.LOGIN, client.LOGIN_EXIST);
}

}
/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub

Login login=new Login();
client= new TetrisClient();
Thread thread=new Thread(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
try {
client.talkToServer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
thread.start();
}}最后再提个问题:可以看到我的客户端里有个Message 对象,虽然还没用上,那是我准备将一个对象整个传过去的。请问,可以将对象转化为ByteBuffer后传给服务端,在再服务端转化回来,然后输出对象里面的信息吗?

解决方案 »

  1.   

    额,呵呵,没法执行那应该是因为少了一个我自定义的包。就那个common那个再贴出来怪吓人的。不过既然有要求,那我一定满足靠你了,呜呜package common;
    import java.io.Serializable;
    public class Message implements Serializable{
    public int position=0;
    public int state=0;
    public String playerName=null;
    public String password=null;
    public String words=null;}
    package common;public interface Constants {
    //position Constant
    public static final int LOGIN=1;
    public static final int HALL=2;
    public static final int ROOM=3;
    public static final int EXIT=4;
    //state Constant
    public static final int LOGIN_NEW=5;
    public static final int LOGIN_EXIST=6;

    public static final int ROOM_WAITING=7;
    public static final int ROOM_PREPARED=8;
    public static final int ROOM_BEGIN=9;
    public static final int ROOM_LEAVE=10;
    public static final int ROOM_ESCAPE=11;
    //Others
    public static final int BREAK_LINE=12;

    }
    package common;
    import java.io.*;
    import java.nio.ByteBuffer;
    public class ByteUtil {
    public static byte[] getBytes(Object obj) throws IOException   
        {   
            ByteArrayOutputStream bout = new ByteArrayOutputStream();   
            ObjectOutputStream out = new ObjectOutputStream(bout);   
            out.writeObject(obj);   
            out.flush();   
            byte[] bytes = bout.toByteArray();   
            bout.close();   
            out.close();   
            return bytes;   
        }   
          
       public static Object getObject(byte[] bytes) throws IOException, ClassNotFoundException   
        {   
               ByteArrayInputStream bi = new ByteArrayInputStream(bytes);   
               ObjectInputStream oi = new ObjectInputStream(bi);   
               Object obj = oi.readObject();   
               bi.close();   
               oi.close();   
               return obj;   
        }   
          
       public static ByteBuffer getByteBuffer(Object obj) throws IOException   
        {   
            byte[] bytes = ByteUtil.getBytes(obj);   
            ByteBuffer buff = ByteBuffer.wrap(bytes);   
            return buff;   
        }   
    }就这些了加上去,应该能运行的麻烦了
      

  2.   

    “事实却是要发出一条信息必须要点击button 不定次数,几次到十几次不等= =才能够在服务端显示出该信息。看了很久不知道问题出在哪”
    原因在于TetrisClient类的send()方法
    sendBuffer.flip(); 
    当你输入数据点了按钮,然后执行到这一句,那么数据不会发送出去。原因是极限设为位置,而位置为0,导致你的数据访问不到。
    socketChannel.write(sendBuffer);
    当你输入数据点了按钮,刚好执行到这一句,数据就会发送出去,这里位置为0,极限刚好就是你的数据的长度,所以能够发送出去。我帮你改了下,加了个flag变量,当flag为true时,表示可以发送数据,false则不可以。
    package temp;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.channels.ClosedChannelException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    import java.util.Set;public class TetrisClient implements Constants{
        SocketChannel socket;
        Selector selector;
        ByteBuffer sendBuffer=ByteBuffer.allocate(1024);
        ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);
        private Charset charset=Charset.forName("GBK");
        private boolean flag;
        
        public TetrisClient(){
            try {
                socket=SocketChannel.open();
                socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8000));
                socket.configureBlocking(false);
                System.out.println("与服务器的连接建立成功");
                selector=Selector.open();
                
    //            talkToServer();
                
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        public void packMessage(String name,String password,int position,int state){
            Message message=new Message();
            message.position=position;
            message.state=state;
            message.playerName=name;
            message.password=password;
    //        try {
    //            sendBuffer=ByteUtil.getByteBuffer(message);
                sendBuffer=ByteBuffer.wrap(name.getBytes());
    //        } catch (IOException e) {
    //            // TODO Auto-generated catch block
    //            e.printStackTrace();
    //        }
             flag=true;
        }
        public void talkToServer() throws IOException{
            socket.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
            
            while(selector.select()>0){
                Set<SelectionKey> readyKeys=selector.selectedKeys();
                Iterator<SelectionKey> iterator=readyKeys.iterator();
                
                while(iterator.hasNext()){
                    SelectionKey key=(SelectionKey)iterator.next();
                    iterator.remove();
                    if(key.isReadable()){
                        receive(key);
                    }
                    if(key.isWritable()){
                        send(key);
                    }
                }
    //            System.out.println("talk to server");
            }
        }
        public void send(SelectionKey key)throws IOException{
         if(!flag)
         return;
            SocketChannel socketChannel=(SocketChannel)key.channel();
            synchronized(sendBuffer){
    //            sendBuffer.flip(); //把极限设为位置
                socketChannel.write(sendBuffer);
                sendBuffer.compact();
    //        System.out.println("send");
             }
            flag=false;
          }
          public void receive(SelectionKey key)throws IOException{
            SocketChannel socketChannel=(SocketChannel)key.channel();
            socketChannel.read(receiveBuffer);
            receiveBuffer.flip();
            String receiveData=decode(receiveBuffer);        if(receiveData.indexOf("\n")==-1)return;        String outputData=receiveData.substring(0,receiveData.indexOf("\n")+1);
            System.out.print(outputData);
            if(outputData.equals("echo:bye\r\n")){
                key.cancel();
                socketChannel.close();
                System.out.println("关闭与服务器的连接");
                selector.close();
                System.exit(0);
            }        ByteBuffer temp=encode(outputData);
            receiveBuffer.position(temp.limit());
            receiveBuffer.compact();
          }      public String decode(ByteBuffer buffer){  //解码
            CharBuffer charBuffer= charset.decode(buffer);
            return charBuffer.toString();
          }
          public ByteBuffer encode(String str){  //编码
            return charset.encode(str);
          }}
    在TetrisServer类的service()方法也有个问题,里面的异常捕获你没有做处理,要加上如下代码
    ,不然客户端断开连接你服务端那边就会死循环一直触发读事件。catch (IOException e) {
        key.cancel();
        key.channel().close();
    }
    “可以看到我的客户端里有个Message 对象,虽然还没用上,那是我准备将一个对象整个传过去的。请问,可以将对象转化为ByteBuffer后传给服务端,在再服务端转化回来,然后输出对象里面的信息吗?

    这个我没有实现过,但是应该可以的,Message类实现序列化,传输在客户端那边进行反序列化就可以了。
      

  3.   

    另外,我一般不注册写事件。为什么呢,你试下在send()方法中第一句代码写
    System.out.println("写事件触发了");
    看是不是一直死循环在打印这一句。
      

  4.   

    恩,问题解决了,果然是高手呀~~~太感动了!!!原来问题出在flip函数这边,看来对于缓冲区的理解还要加强呀!还帮我指出了其他的问题,真心谢谢了!这个问题我也有注意到,在一开始注册写事件,超级占用cpu一般会达到50%以上= =。但是,如果不一开始就注册,那是不是在每次有写事件的时候注册,写完后再注销呢?麻烦再进一步解答,能付上点代码更好了,,,菜鸟呀,小弟。
      

  5.   

    按我的做法,我不会去注册这个事件,也不会在需要写的时候注册,写完后又取消。
    我只在合适的地方去调用发送方法,例如你的代码如果让我写的话,我会把写事件去掉,send()方法在点按钮后执行即可。public void packMessage(String name,String password,int position,int state){
            Message message=new Message();
            message.position=position;
            message.state=state;
            message.playerName=name;
            message.password=password;
            send();
        }
      

  6.   

    使用NIO不建议直接调用NIO接口,需要考虑的东西太多了
    还是用MINA之类的封装比较好
      

  7.   

    呀呀,这样吗MINA还没接触过呢,NIO还是这一个星期现学的= =。怎么搞现在好不容易有点眉目了!难道我又要换个知识点来做项目= =。
      

  8.   


    貌似不行呢今天试了下,发不出消息呀!求进一步指教!首先在获取Selectionkey这步貌似就不行了
      

  9.   

    MINA是APACHE下的NIO框架项目
    可以去下一个来看看,MINA应用还是很简单的,基本都封装好了
    GOOGLE上随便找个例子看看就明白了
      

  10.   


    既然不注册写事件,那么自然不会有Selectionkey对象。在send方法中,有以下代码
    SocketChannel socketChannel=(SocketChannel)key.channel();
    其实这个socketChannel对象就是你TetrisClient类的成员变量socket。所以你的send方法改为如下即可。public void send(SelectionKey key)throws IOException{
            if(!flag)
                return;
            //SocketChannel socketChannel=(SocketChannel)key.channel();
            SocketChannel socketChannel=socket;
            synchronized(sendBuffer){
    //            sendBuffer.flip(); //把极限设为位置
                socketChannel.write(sendBuffer);
                sendBuffer.compact();
    //        System.out.println("send");
             }
            flag=false;
          }
      

  11.   

    http://download.csdn.net/detail/y172158950/4361355
    mina聊天室
      

  12.   

    想不到那样直接send真的可以!!!也不占用cpu!谢谢“qunhao”这位大牛了帮我解决了好多问题,解开了很多疑惑,真心感激。