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
对每个对象执行以下操作:首先,只需要发送一次的数据,例如头和关于类型的各种元信息,在每次发送时都会重新发送。其次,发送方的公共实例在跨多个发送接收时会成为不同的引用。