6 Commits

Author SHA1 Message Date
3b699556db Merge branch 'dev' into 10-create-network-communication-classes 2025-11-02 22:39:26 +00:00
Gaa56
d078808486 Update SocketConnection 2025-10-30 19:25:27 +00:00
Gaa56
98581b562d Merge pull request #27 from davidalves04/9-design-message-protocol-specification
#10 Req
2025-10-30 18:44:54 +00:00
David Alves
c6b710ac52 Merge pull request #25 from davidalves04/11-convert-intersection-to-standalone-process
11 convert intersection to standalone process
2025-10-30 16:00:05 +00:00
David Alves
d057adeab3 Revert "Enunciado uploaded"
This reverts commit be4e7f66d6.
2025-10-27 22:52:19 +00:00
David Alves
be4e7f66d6 Enunciado uploaded 2025-10-27 18:03:17 +00:00

View File

@@ -1,15 +1,23 @@
package sd.protocol; package sd.protocol;
import java.io.Closeable; import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import sd.serialization.MessageSerializer;
import sd.serialization.SerializationException;
import sd.serialization.SerializerFactory;
/** /**
* Wrapper class that simplifies communication via Sockets. * Wrapper class that simplifies communication via Sockets.
* Includes connection retry logic for robustness. * Includes connection retry logic for robustness.
@@ -17,8 +25,9 @@ import java.util.concurrent.TimeUnit;
public class SocketConnection implements Closeable { public class SocketConnection implements Closeable {
private final Socket socket; private final Socket socket;
private final ObjectOutputStream outputStream; private final OutputStream outputStream;
private final ObjectInputStream inputStream; private final InputStream inputStream;
private final MessageSerializer serializer;
// --- Configuration for Retry Logic --- // --- Configuration for Retry Logic ---
/** Maximum number of connection attempts. */ /** Maximum number of connection attempts. */
@@ -88,16 +97,11 @@ public class SocketConnection implements Closeable {
// If connection was successful, assign to final variable and create streams // If connection was successful, assign to final variable and create streams
this.socket = tempSocket; this.socket = tempSocket;
try {
// IMPORTANT: The order is crucial. OutputStream first. this.outputStream = socket.getOutputStream();
this.outputStream = new ObjectOutputStream(socket.getOutputStream()); this.inputStream = socket.getInputStream();
this.inputStream = new ObjectInputStream(socket.getInputStream()); this.serializer = SerializerFactory.createDefault();
} catch (IOException e) {
// If stream creation fails even after successful socket connection, clean up.
System.err.println("[SocketConnection] Failed to create streams after connection: " + e.getMessage());
try { socket.close(); } catch (IOException closeEx) { /* ignore */ }
throw e; // Re-throw the stream creation exception
}
} }
@@ -111,12 +115,10 @@ public class SocketConnection implements Closeable {
*/ */
public SocketConnection(Socket acceptedSocket) throws IOException { public SocketConnection(Socket acceptedSocket) throws IOException {
this.socket = acceptedSocket; this.socket = acceptedSocket;
this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream();
this.serializer = SerializerFactory.createDefault();
// IMPORTANT: The order is crucial. OutputStream first.
this.outputStream = new ObjectOutputStream(socket.getOutputStream());
this.inputStream = new ObjectInputStream(socket.getInputStream());
System.out.printf("[SocketConnection] Connection accepted from %s:%d.%n",
acceptedSocket.getInetAddress().getHostAddress(), acceptedSocket.getPort());
} }
/** /**
@@ -126,52 +128,66 @@ public class SocketConnection implements Closeable {
* @throws IOException If writing to the stream fails or socket is not connected. * @throws IOException If writing to the stream fails or socket is not connected.
*/ */
public void sendMessage(MessageProtocol message) throws IOException { public void sendMessage(MessageProtocol message) throws IOException {
if (!isConnected()) { if (socket == null || !socket.isConnected()) {
throw new IOException("Socket is not connected."); throw new IOException("Socket is not connected");
} }
synchronized (outputStream) {
outputStream.writeObject(message); try {
outputStream.flush(); // Ensures the message is sent immediately. // Serializa para bytes JSON
byte[] data = serializer.serialize(message);
// Write 4-byte length prefix
DataOutputStream dataOut = new DataOutputStream(outputStream);
dataOut.writeInt(data.length);
dataOut.write(data);
dataOut.flush();
} catch (SerializationException e) {
throw new IOException("Failed to serialize message", e);
} }
} }
/** /**
* Tries to read (deserialize) a MessageProtocol object from the socket. * Tries to read (deserialize) a MessageProtocol object from the socket.
* This call is "blocked" until an object is received.
* *
* @return The "envelope" (MessageProtocol) that was received. * @return The "envelope" (MessageProtocol) that was received.
* @throws IOException If the connection is lost, the stream is corrupted, or socket is not connected. * @throws IOException If the connection is lost, the stream is corrupted, or socket is not connected.
* @throws ClassNotFoundException If the received object is unknown. * @throws ClassNotFoundException If the received object is unknown.
*/ */
public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException { public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException {
if (!isConnected()) { if (socket == null || !socket.isConnected()) {
throw new IOException("Socket is not connected."); throw new IOException("Socket is not connected");
} }
synchronized (inputStream) {
return (MessageProtocol) inputStream.readObject(); try {
// Lê um prefixo de 4 bytes - indicador de tamanho
DataInputStream dataIn = new DataInputStream(inputStream);
int length = dataIn.readInt();
if (length <= 0 || length > 10_000_000) { // Sanity check (10MB max)
throw new IOException("Invalid message length: " + length);
}
// Ler dados da mensagem
byte[] data = new byte[length];
dataIn.readFully(data);
// Deserialize do JSON
return serializer.deserialize(data, MessageProtocol.class);
} catch (SerializationException e) {
throw new IOException("Failed to deserialize message", e);
} }
} }
/** /**
* Closes the socket and all streams (Input and Output). * Closes the socket and all streams (Input and Output).
* It is called automatically if you use 'try-with-resources'.
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
System.out.printf("[SocketConnection] Closing connection to %s:%d.%n", if (inputStream != null) inputStream.close();
socket != null ? socket.getInetAddress().getHostAddress() : "N/A", if (outputStream != null) outputStream.close();
socket != null ? socket.getPort() : -1); if (socket != null) socket.close();
try {
if (inputStream != null) inputStream.close();
} catch (IOException e) { /* ignore */ }
try {
if (outputStream != null) outputStream.close();
} catch (IOException e) { /* ignore */ }
if (socket != null && !socket.isClosed()) {
socket.close();
}
} }
/** /**