抱歉,在我发布此问题之前,我搜索了大约2天。有类似的问题,但没有一个帮助我。
我正在尝试创建一个简单的聊天应用程序,其中客户端使用(非NIO)Socket连接到侦听NIO ServerSocketChannel的服务器。服务器使用选择器。在第一个客户端连接之前,Selector.select()方法将被阻止,如预期的那样。但是在第一个客户端连接后,Selector.select()不会阻塞并立即返回。这导致我的while循环连续运行。
抱歉,我已粘贴整个代码,以便您可以复制粘贴并运行它。我刚刚开始使用Java,因此非常感谢任何帮助/指针。谢谢。
P.S。:现在,客户端通过套接字连接发送序列化对象(Message对象),Server读取它。由于连接是非阻塞的,因此序列化对象在发送到服务器之前预先固定了对象大小(以字节为单位)。这允许服务器读取下一个" x" bytes和un-serialize到Message对象。服务器代码正在进行中。
客户代码----------
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
public class ChatClient {
void go(){
User u = new User();
u.setName("UserA");
try{
u.setInet(InetAddress.getLocalHost());
}catch (UnknownHostException ex){
System.out.println(ex);
return;
}
Message m = new Message();
m.setType(3);
m.setText("This is the 1st message.");
m.setFromUser(u);
try{
Socket sock = new Socket (InetAddress.getLocalHost(), 5000);
DataOutputStream dataOut = new DataOutputStream(sock.getOutputStream());
ByteArrayOutputStream byteTemp = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream (byteTemp);
objOut.writeObject(m);
objOut.flush();
objOut.close();
byte[] byteMessage = byteTemp.toByteArray();
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(byteMessage.length);
byte[] size = new byte[4];
size = bb.array();
System.out.println("Object size = "+byteMessage.length); //370
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
byteOut.write(size);
byteOut.write(byteMessage);
byte[] finalMessage = byteOut.toByteArray();
dataOut.write(finalMessage,0,finalMessage.length);
dataOut.flush();
System.out.println("Flushed out");
}catch (Exception ex){
System.out.println(ex);
}
}
public static void main (String args[]){
new CopyOfChatClient().go();
}
}
服务器代码---------------
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
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 java.util.concurrent.locks.ReentrantLock;
public class CopyOfChatServer {
Object a, b;//Dummy objects for synchronization
SocketChannel clientSock=null;
Selector selector;
SelectionKey key;
void go(){
try{
a=new Object();//Dummy objects for synchronization
b=new Object();//Dummy objects for synchronization
ServerSocketChannel serverSock = ServerSocketChannel.open();
serverSock.socket().bind(new InetSocketAddress(5000));
//Note: ServerSocketChannel is blocking, but each new connection returned by accept() will be made non-blocking (see below)
selector = Selector.open();
new Thread(new SelectorThread()).start(); //Start the SelectorThread
int i=0;
while (true){
clientSock = serverSock.accept();
if (clientSock!=null){
clientSock.configureBlocking(false); //The default client socket returned by accept() is blocking. Set it to non-blocking.
synchronized (b){
selector.wakeup();
synchronized (a){
key = clientSock.register(selector, SelectionKey.OP_READ); //register new client Socket with selector
key.attach(clientSock);
}//sync(a)
}//sync(b)
i++;
}
System.out.println("Here");
}//while(true)
}catch (Exception ex){
System.out.println(ex);
}
}
class SelectorThread implements Runnable{
Set <SelectionKey> selectedKeys;
int readyChannels;
public void run(){
while (true){
try {
synchronized(a){
System.out.println("1. Selector trying to select");
readyChannels = selector.select();//Note: select() is blocking ?? Does not block. Behaves like non-blocking
System.out.println("2. Selector has selected");
}//sync a
synchronized (b){
//just wait till registration is done in main thread
}
if (readyChannels == 0) continue; //Even if select() is blocking, this check is to handle suprious wake-ups
System.out.println("readyChannels>0");
selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()){
SelectionKey key = keyIterator.next();
keyIterator.remove();//added after the first answer to my question
if (key.isReadable()){
System.out.println("3. Got incoming data");
SocketChannel tempSock = (SocketChannel)key.attachment();
ByteBuffer bb=ByteBuffer.allocate(8000);
int bytesRead=tempSock.read(bb);
System.out.println("4. Bytes read = "+bytesRead);
if (bytesRead>4){
bb.flip();
bb.rewind();
int size = bb.getInt();
System.out.println("5. Size of object = "+size);
byte[] objIn = new byte[size];
for (int i=0;i<size;i++){
objIn[i]=bb.get();
}
bb.compact();
ByteArrayInputStream bIn= new ByteArrayInputStream(objIn);
ObjectInputStream objStream= new ObjectInputStream(bIn);
Message temp1 = (Message) objStream.readObject();
System.out.println("6. Read object back");
System.out.println(temp1.getFromUser().getName());
}
}
}
selectedKeys.clear();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main (String args[]){
new CopyOfChatServer().go();
}
}
MESSAGE Class ----
import java.io.Serializable;
public class Message implements Serializable{
private int type;
private User fromUser;
private User toUser;
private String text;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public User getFromUser() {
return fromUser;
}
public void setFromUser(User fromUser) {
this.fromUser = fromUser;
}
public User getToUser() {
return toUser;
}
public void setToUser(User toUser) {
this.toUser = toUser;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
USER CLASS --------
import java.io.Serializable;
import java.net.InetAddress;
public class User implements Serializable{
private String name;
private InetAddress inet;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public InetAddress getInet() {
return inet;
}
public void setInet(InetAddress inet) {
this.inet = inet;
}
}
答案 0 :(得分:0)
你必须把
keyIterator.remove()
后
keyIterator.next()
选择器不会从selectedKeys()本身删除任何内容。
注意:您不需要将频道作为附件附加到密钥。你可以从key.channel()获得它。