Using
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();
}
}
and
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);
}
}
}
This P2P Server/Client is supposed to have 1 client send a message to the server, then relay that message to the other client. When a message is sent from one client, the server outputs [Server] P2 sent 'hello'
, but after relaying the message to the other client, the other client gets this exception:
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$1.run(Client.java:25)
at java.lang.Thread.run(Unknown Source)
Line 25 is Object o = ois.readObject();
How does the object get corrupted after being relayed from the server? What can I do to prevent the object from being corrupted?
Basically the problem is, you shouldn't create TWO ObjectOutputStream-s using the same underlying OutputStream
. You should only create one object-output-stream for each socket, and reuse it.
So the error is caused by rows ObjectOutputStream p1out = ..
and ObjectOutputStream p2out = ..
.
The AC
code comes from a "serialzation header". If we look at the constructor public ObjectOutputStream(OutputStream out)
:
public ObjectOutputStream(OutputStream out) throws IOException {
...
writeStreamHeader();
...
}
and writeStreamHeader
is
/**
* 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);
}
So if two object-output-streams are created, the header will be written twice, causing the de-serialization error on the client side.
A slightly modified version of the Server (it handles disconnection differently, for illustration purpose).
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();
}
}