简单的 P2P 服务器,对象从服务器中继后损坏
Simple P2P server, object gets corrupted after being relayed from the server
正在使用
public class Server {
private static ServerSocket server;
private static int port = 9876;
private static Socket p1 = null;
private static Socket p2 = null;
public static void main(String args[]) {
System.out.println("[Server] Attempting to bind port " + port);
try {
server = new ServerSocket(port);
} catch (IOException err) {
err.printStackTrace();
System.exit(1);
}
new Thread(new Runnable() {
@Override
public void run() {
while(p1 == null) {
try {
System.out.println("[Server] Waiting for P1 to connect");
p1 = server.accept();
System.out.println("[Server] P1 has connected");
ObjectInputStream ois = new ObjectInputStream(p1.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(p1.getOutputStream());
while (p1.isConnected()) {
String message = (String) ois.readObject();
System.out.println("[Server] P1 sent '" + message + "'");
if (p2.isConnected()) {
ObjectOutputStream p2out = new ObjectOutputStream(p2.getOutputStream());
p2out.writeObject("[P1] " + message);
} else {
oos.writeObject("[Server] P2 is not connected");
}
}
System.out.println("[Server] P1 has disconnected");
p1 = null;
} catch (Exception err) {
if(err instanceof SocketException) {
System.out.println("[Server] P1 has disconnected");
p1 = null;
} else {
err.printStackTrace();
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(p2 == null) {
if(p1 != null) {
try {
System.out.println("[Server] Waiting for P2 to connect");
p2 = server.accept();
System.out.println("[Server] P2 has connected");
ObjectInputStream ois = new ObjectInputStream(p2.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(p2.getOutputStream());
while (p2.isConnected()) {
String message = (String) ois.readObject();
System.out.println("[Server] P2 sent '" + message + "'");
if (p1.isConnected()) {
ObjectOutputStream p1out = new ObjectOutputStream(p1.getOutputStream());
p1out.writeObject("[P2] " + message);
} else {
oos.writeObject("[Server] P1 is not connected");
}
}
System.out.println("[Server] P2 has disconnected");
p2 = null;
} catch (Exception err) {
if(err instanceof SocketException) {
System.out.println("[Server] P2 has disconnected");
p2 = null;
} else {
err.printStackTrace();
}
}
}
}
}
}).start();
}
}
和
public class Client {
static ObjectOutputStream oos = null;
static ObjectInputStream ois = null;
static int port = 9876;
public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException, InterruptedException{
InetAddress host = InetAddress.getLocalHost();
Socket socket = new Socket(host.getHostName(), port);
System.out.println("[Client] Connection established with port " + port);
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
new Thread(new Runnable() {
@Override
public void run() {
try {
Object o = ois.readObject();
if(o instanceof String) {
String msg = (String) o;
System.out.println(msg);
}
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
}).start();
Scanner s = new Scanner(System.in);
while(true) {
String msg = s.nextLine();
oos.writeObject(msg);
}
}
}
此 P2P Server/Client 应该让 1 个客户端向服务器发送消息,然后将该消息转发给另一个客户端。当一条消息从一个客户端发送时,服务器输出[Server] P2 sent 'hello'
,但是在将消息中继到另一个客户端后,另一个客户端得到这个异常:
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at me.aj4real.tutorial.p2pChatServer.Client.run(Client.java:25)
at java.lang.Thread.run(Unknown Source)
第 25 行是 Object o = ois.readObject();
对象从服务器转发后如何损坏?
我能做些什么来防止对象被损坏?
基本上问题是,您不应该使用相同的底层 OutputStream
创建两个 ObjectOutputStream-s。您应该只为每个套接字创建一个对象输出流,然后重复使用它。
所以错误是由行 ObjectOutputStream p1out = ..
和 ObjectOutputStream p2out = ..
.
引起的
AC
代码来自 "serialzation header"。如果我们看一下构造函数 public ObjectOutputStream(OutputStream out)
:
public ObjectOutputStream(OutputStream out) throws IOException {
...
writeStreamHeader();
...
}
并且writeStreamHeader
是
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
...
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
所以如果创建了两个object-output-streams,header会被写入两次,导致客户端反序列化错误
服务器的略微修改版本(出于说明目的,它以不同方式处理断开连接)。
package clientserver;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static ServerSocket server;
private static int port = 9876;
private static Socket p1 = null;
private static Socket p2 = null;
static ObjectInputStream ois1, ois2;
static ObjectOutputStream oos1, oos2;
public static void main(String args[]) throws Exception {
System.out.println("server listening on port=" + port);
server = new ServerSocket(port);
p1 = server.accept();
System.out.println("[Server] P1 has connected");
p2 = server.accept();
System.out.println("[Server] P2 has connected");
ois1 = new ObjectInputStream(p1.getInputStream());
oos1 = new ObjectOutputStream(p1.getOutputStream());
ois2 = new ObjectInputStream(p2.getInputStream());
oos2 = new ObjectOutputStream(p2.getOutputStream());
new Thread(new Runnable() {
@Override
public void run() {
try {
while (p1.isConnected()) {
String message = (String) ois1.readObject();
System.out.printf("[Server] P1 sent msg=[%s]%n", message);
if (p2.isConnected()) {
ObjectOutputStream p2out = oos2;
p2out.writeObject("[P1] " + message);
} else {
oos1.writeObject("[Server] P2 is not connected");
}
}
System.out.println("[Server] P1 has disconnected");
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
while (p2.isConnected()) {
String message = (String) ois2.readObject();
System.out.printf("[Server] P2 sent msg=[%s]%n", message);
if (p1.isConnected()) {
ObjectOutputStream p1out = oos1;
p1out.writeObject("[P2] " + message);
} else {
oos2.writeObject("[Server] P1 is not connected");
}
}
System.out.println("[Server] P2 has disconnected");
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}).start();
}
}
正在使用
public class Server {
private static ServerSocket server;
private static int port = 9876;
private static Socket p1 = null;
private static Socket p2 = null;
public static void main(String args[]) {
System.out.println("[Server] Attempting to bind port " + port);
try {
server = new ServerSocket(port);
} catch (IOException err) {
err.printStackTrace();
System.exit(1);
}
new Thread(new Runnable() {
@Override
public void run() {
while(p1 == null) {
try {
System.out.println("[Server] Waiting for P1 to connect");
p1 = server.accept();
System.out.println("[Server] P1 has connected");
ObjectInputStream ois = new ObjectInputStream(p1.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(p1.getOutputStream());
while (p1.isConnected()) {
String message = (String) ois.readObject();
System.out.println("[Server] P1 sent '" + message + "'");
if (p2.isConnected()) {
ObjectOutputStream p2out = new ObjectOutputStream(p2.getOutputStream());
p2out.writeObject("[P1] " + message);
} else {
oos.writeObject("[Server] P2 is not connected");
}
}
System.out.println("[Server] P1 has disconnected");
p1 = null;
} catch (Exception err) {
if(err instanceof SocketException) {
System.out.println("[Server] P1 has disconnected");
p1 = null;
} else {
err.printStackTrace();
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(p2 == null) {
if(p1 != null) {
try {
System.out.println("[Server] Waiting for P2 to connect");
p2 = server.accept();
System.out.println("[Server] P2 has connected");
ObjectInputStream ois = new ObjectInputStream(p2.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(p2.getOutputStream());
while (p2.isConnected()) {
String message = (String) ois.readObject();
System.out.println("[Server] P2 sent '" + message + "'");
if (p1.isConnected()) {
ObjectOutputStream p1out = new ObjectOutputStream(p1.getOutputStream());
p1out.writeObject("[P2] " + message);
} else {
oos.writeObject("[Server] P1 is not connected");
}
}
System.out.println("[Server] P2 has disconnected");
p2 = null;
} catch (Exception err) {
if(err instanceof SocketException) {
System.out.println("[Server] P2 has disconnected");
p2 = null;
} else {
err.printStackTrace();
}
}
}
}
}
}).start();
}
}
和
public class Client {
static ObjectOutputStream oos = null;
static ObjectInputStream ois = null;
static int port = 9876;
public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException, InterruptedException{
InetAddress host = InetAddress.getLocalHost();
Socket socket = new Socket(host.getHostName(), port);
System.out.println("[Client] Connection established with port " + port);
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
new Thread(new Runnable() {
@Override
public void run() {
try {
Object o = ois.readObject();
if(o instanceof String) {
String msg = (String) o;
System.out.println(msg);
}
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
}).start();
Scanner s = new Scanner(System.in);
while(true) {
String msg = s.nextLine();
oos.writeObject(msg);
}
}
}
此 P2P Server/Client 应该让 1 个客户端向服务器发送消息,然后将该消息转发给另一个客户端。当一条消息从一个客户端发送时,服务器输出[Server] P2 sent 'hello'
,但是在将消息中继到另一个客户端后,另一个客户端得到这个异常:
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at me.aj4real.tutorial.p2pChatServer.Client.run(Client.java:25)
at java.lang.Thread.run(Unknown Source)
第 25 行是 Object o = ois.readObject();
对象从服务器转发后如何损坏? 我能做些什么来防止对象被损坏?
基本上问题是,您不应该使用相同的底层 OutputStream
创建两个 ObjectOutputStream-s。您应该只为每个套接字创建一个对象输出流,然后重复使用它。
所以错误是由行 ObjectOutputStream p1out = ..
和 ObjectOutputStream p2out = ..
.
AC
代码来自 "serialzation header"。如果我们看一下构造函数 public ObjectOutputStream(OutputStream out)
:
public ObjectOutputStream(OutputStream out) throws IOException {
...
writeStreamHeader();
...
}
并且writeStreamHeader
是
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
...
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
所以如果创建了两个object-output-streams,header会被写入两次,导致客户端反序列化错误
服务器的略微修改版本(出于说明目的,它以不同方式处理断开连接)。
package clientserver;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static ServerSocket server;
private static int port = 9876;
private static Socket p1 = null;
private static Socket p2 = null;
static ObjectInputStream ois1, ois2;
static ObjectOutputStream oos1, oos2;
public static void main(String args[]) throws Exception {
System.out.println("server listening on port=" + port);
server = new ServerSocket(port);
p1 = server.accept();
System.out.println("[Server] P1 has connected");
p2 = server.accept();
System.out.println("[Server] P2 has connected");
ois1 = new ObjectInputStream(p1.getInputStream());
oos1 = new ObjectOutputStream(p1.getOutputStream());
ois2 = new ObjectInputStream(p2.getInputStream());
oos2 = new ObjectOutputStream(p2.getOutputStream());
new Thread(new Runnable() {
@Override
public void run() {
try {
while (p1.isConnected()) {
String message = (String) ois1.readObject();
System.out.printf("[Server] P1 sent msg=[%s]%n", message);
if (p2.isConnected()) {
ObjectOutputStream p2out = oos2;
p2out.writeObject("[P1] " + message);
} else {
oos1.writeObject("[Server] P2 is not connected");
}
}
System.out.println("[Server] P1 has disconnected");
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
while (p2.isConnected()) {
String message = (String) ois2.readObject();
System.out.printf("[Server] P2 sent msg=[%s]%n", message);
if (p1.isConnected()) {
ObjectOutputStream p1out = oos1;
p1out.writeObject("[P2] " + message);
} else {
oos2.writeObject("[Server] P1 is not connected");
}
}
System.out.println("[Server] P2 has disconnected");
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}).start();
}
}