Java-ReadObject与nio

Java-ReadObject与nio,java,serialization,nio,Java,Serialization,Nio,在传统的阻塞线程服务器中,我会这样做 class ServerSideThread { ObjectInputStream in; ObjectOutputStream out; Engine engine; public ServerSideThread(Socket socket, Engine engine) { in = new ObjectInputStream(socket.getInputStream()); ou

在传统的阻塞线程服务器中,我会这样做

class ServerSideThread {

    ObjectInputStream in;
    ObjectOutputStream out;
    Engine engine;

    public ServerSideThread(Socket socket, Engine engine) {
        in = new ObjectInputStream(socket.getInputStream());
        out = new ObjectOutputStream(socket.getOutputStream());
        this.engine = engine;
    }

    public void sendMessage(Message m) {
        out.writeObject(m);
    }

    public void run() {
        while(true) {
            Message m = (Message)in.readObject();
            engine.queueMessage(m,this); // give the engine a message with this as a callback
        }
    }
}
public static void send(SocketChannel socket,  Serializable serializable) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for(int i=0;i<4;i++) baos.write(0);
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(serializable);
    oos.close();
    final ByteBuffer wrap = ByteBuffer.wrap(baos.toByteArray());
    wrap.putInt(0, baos.size()-4);
    socket.write(wrap);
}

private final ByteBuffer lengthByteBuffer = ByteBuffer.wrap(new byte[4]);
private ByteBuffer dataByteBuffer = null;
private boolean readLength = true;

public Serializable recv(SocketChannel socket) throws IOException, ClassNotFoundException {
    if (readLength) {
        socket.read(lengthByteBuffer);
        if (lengthByteBuffer.remaining() == 0) {
            readLength = false;
            dataByteBuffer = ByteBuffer.allocate(lengthByteBuffer.getInt(0));
            lengthByteBuffer.clear();
        }
    } else {
        socket.read(dataByteBuffer);
        if (dataByteBuffer.remaining() == 0) {
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dataByteBuffer.array()));
            final Serializable ret = (Serializable) ois.readObject();
            // clean up
            dataByteBuffer = null;
            readLength = true;
            return ret;
        }
    }
    return null;
}
现在,这个物体可以预期是相当大的。在我的nio循环中,我不能简单地等待对象通过,我的所有其他连接(具有更小的工作负载)都将等待我


在连接通知我的nio通道它准备好之前,我怎么才能得到通知说连接已经拥有了整个对象?

您可以将对象写入ByteArrayOutputStream,允许您在发送对象之前给出长度。在接收端,在尝试解码之前读取所需的数据量

然而,您可能会发现在Object*Stream中使用阻塞IO(而不是NIO)更简单、更高效


编辑类似这样的内容

class ServerSideThread {

    ObjectInputStream in;
    ObjectOutputStream out;
    Engine engine;

    public ServerSideThread(Socket socket, Engine engine) {
        in = new ObjectInputStream(socket.getInputStream());
        out = new ObjectOutputStream(socket.getOutputStream());
        this.engine = engine;
    }

    public void sendMessage(Message m) {
        out.writeObject(m);
    }

    public void run() {
        while(true) {
            Message m = (Message)in.readObject();
            engine.queueMessage(m,this); // give the engine a message with this as a callback
        }
    }
}
public static void send(SocketChannel socket,  Serializable serializable) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for(int i=0;i<4;i++) baos.write(0);
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(serializable);
    oos.close();
    final ByteBuffer wrap = ByteBuffer.wrap(baos.toByteArray());
    wrap.putInt(0, baos.size()-4);
    socket.write(wrap);
}

private final ByteBuffer lengthByteBuffer = ByteBuffer.wrap(new byte[4]);
private ByteBuffer dataByteBuffer = null;
private boolean readLength = true;

public Serializable recv(SocketChannel socket) throws IOException, ClassNotFoundException {
    if (readLength) {
        socket.read(lengthByteBuffer);
        if (lengthByteBuffer.remaining() == 0) {
            readLength = false;
            dataByteBuffer = ByteBuffer.allocate(lengthByteBuffer.getInt(0));
            lengthByteBuffer.clear();
        }
    } else {
        socket.read(dataByteBuffer);
        if (dataByteBuffer.remaining() == 0) {
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dataByteBuffer.array()));
            final Serializable ret = (Serializable) ois.readObject();
            // clean up
            dataByteBuffer = null;
            readLength = true;
            return ret;
        }
    }
    return null;
}
publicstaticvoidsend(socketchannelsocket,Serializable-Serializable)抛出IOException{
ByteArrayOutputStream bas=新的ByteArrayOutputStream();

对于(inti=0;i,受上述代码的启发,我创建了一个()

它包括一个简单的单元测试:

SeriServer server = new SeriServer(6001, nthreads);
final SeriClient client[] = new SeriClient[nclients];

//write the data with multiple threads to flood the server

for (int cnt = 0; cnt < nclients; cnt++) {
    final int counterVal = cnt;
    client[cnt] = new SeriClient("localhost", 6001);
    Thread t = new Thread(new Runnable() {
         public void run() {
             try {
                for (int cnt2 = 0; cnt2 < nsends; cnt2++) {
                   String msg = "[" + counterVal + "]";                       
                   client[counterVal].send(msg);
                 }
             } catch (IOException e) {
                 e.printStackTrace();
                 fail();
             }
         }
         });
    t.start();
 }

 HashMap<String, Integer> counts = new HashMap<String, Integer>();
   int nullCounts = 0;
   for (int cnt = 0; cnt < nsends * nclients;) {
       //read the data from a vector (that the server pool automatically fills
       SeriDataPackage data = server.read();  
       if (data == null) {
              nullCounts++;
              System.out.println("NULL");
              continue;
       }

       if (counts.containsKey(data.getObject())) {
              Integer c = counts.get(data.getObject());
              counts.put((String) data.getObject(), c + 1);
        } else {
              counts.put((String) data.getObject(), 1);
        }
        cnt++;
        System.out.println("Received: " + data.getObject());
   }

   // asserts the results
   Collection<Integer> values = counts.values();
   for (Integer value : values) {
        int ivalue = value;
        assertEquals(nsends, ivalue);
        System.out.println(value);
   }
   assertEquals(counts.size(), nclients);
   System.out.println(counts.size());
   System.out.println("Finishing");
   server.shutdown();
SeriServer服务器=新的SeriServer(6001,nthreads);
最终服务客户[]=新服务客户[N];
//使用多个线程写入数据以使服务器满负荷运行
对于(int-cnt=0;cnt
阻塞IO不会扩展。因为整个JVM的堆栈线程大小是恒定的,所以每个连接线程的堆栈大小都将与我的引擎线程的堆栈大小相同(需要相当大)因此,即使只有3000个连接,我的连接容量也只有384Mb。这不会在1Gb的机箱上为系统、堆和数据库留下太多的空间。cpu需求将足够小,我可以轻松处理10000多个连接,这就限制了内存的使用。因此,如果我首先发送要读取的数据量,我如何知道我有多少数据e排队直到…我读到它?对每个对象(或对象组)使用
新对象输出流(new ByteArrayOutputStream
)这样您就没有数据排队。您将字节数组的长度发送为
int
,后跟字节数组。在读取端,您需要读取最小4字节的数据,这将为您提供对象数据的剩余长度。@glowcoder,使用NIO,您执行一次读取,它会告诉您读取了多少字节。然后,您获取ose字节,并将它们放在为每个连接维护的单独缓冲区中,直到积累了所需的字节数。我建议将此功能包装在您自己的一对输入和输出流中。注意:在使用
new ObjectOutputStream()时,有两个微妙的陷阱需要注意
在整个会话中,通过单个
ObjectOutput/InputStream
对每个对象执行以下操作:首先,只需要发送一次的数据,例如头和关于类型的各种元信息,在每次发送时都会重新发送。其次,发送方的公共实例在跨多个发送接收时会成为不同的引用。