这几天一直在研究这东西,然后自己照着例子写了点东东。不过一直测试都有问题,在这里贴上代码,,,希望好心人能够耐心解答。感激不尽!以下是服务端代码,我只想先实现收信息功能,能够在服务端的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后传给服务端,在再服务端转化回来,然后输出对象里面的信息吗?
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后传给服务端,在再服务端转化回来,然后输出对象里面的信息吗?
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;
}
}就这些了加上去,应该能运行的麻烦了
原因在于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类实现序列化,传输在客户端那边进行反序列化就可以了。
System.out.println("写事件触发了");
看是不是一直死循环在打印这一句。
我只在合适的地方去调用发送方法,例如你的代码如果让我写的话,我会把写事件去掉,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();
}
还是用MINA之类的封装比较好
貌似不行呢今天试了下,发不出消息呀!求进一步指教!首先在获取Selectionkey这步貌似就不行了
可以去下一个来看看,MINA应用还是很简单的,基本都封装好了
GOOGLE上随便找个例子看看就明白了
既然不注册写事件,那么自然不会有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;
}
mina聊天室