#include "SimpleClient.h" #include #include // 1. THE CONSTRUCTOR (Birth) // We initialize the socket to INVALID. We haven't bought a telephone yet. SimpleClient::SimpleClient() : clientSocket(INVALID_SOCKET) {} // 2. THE DESTRUCTOR (Cleanup) // When the Client object is destroyed, we must hang up the phone. SimpleClient::~SimpleClient() { if (clientSocket != INVALID_SOCKET) { CLOSE_SOCKET(clientSocket); } } // 3. THE CONNECT METHOD (The Dialing Algorithm) bool SimpleClient::connectTo(const std::string& ip, int port) { // ========================================================================= // STEP 1: CREATE THE SOCKET (Buying the physical telephone) // ========================================================================= // Just like the server, we need a physical socket to communicate. // AF_INET = IPv4, SOCK_STREAM = Reliable TCP connection. clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (clientSocket == INVALID_SOCKET) { std::cerr << "❌ Failed to create client socket!\n"; return false; } // ========================================================================= // STEP 2: SETUP THE ADDRESS (Looking up the phone number) // ========================================================================= // We need to tell the OS *who* we want to call. sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; // IPv4 serverAddr.sin_port = htons(port); // The port number (e.g., 8080) // inet_pton = "Internet Presentation to Network" // It converts the human-readable text "127.0.0.1" into the 32-bit binary // number that the computer's network card actually understands. if (inet_pton(AF_INET, ip.c_str(), &serverAddr.sin_addr) <= 0) { std::cerr << "❌ Invalid IP address format!\n"; return false; } // ========================================================================= // STEP 3: CONNECT (Dialing the number and waiting for an answer) // ========================================================================= // This function BLOCKS (pauses) the program here until the server picks up. // If the server isn't running, or the port is wrong, this will fail. if (connect(clientSocket, reinterpret_cast(&serverAddr), sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "❌ Failed to connect to server at " << ip << ":" << port << "\n"; return false; // We couldn't get through, so we return false. } // If we reach here, the connection is successful! The line is open. return true; } // 4. THE TALK METHOD (The Conversation Algorithm) void SimpleClient::talk(const std::string& message) const { // Safety check: Don't try to talk if we aren't connected! if (clientSocket == INVALID_SOCKET) { std::cerr << "⚠️ Cannot talk: Not connected to a server.\n"; return; } std::cout << "📤 Sending: " << message; // ========================================================================= // STEP 4: SEND (Speaking into the microphone) // ========================================================================= // We push our text message out through the open connection. // We cast the length to 'int' to satisfy Windows, and it's perfectly safe // because our messages are small. send(clientSocket, message.c_str(), static_cast(message.length()), 0); // ========================================================================= // STEP 5: RECV (Listening for the echo) // ========================================================================= char buffer[1025]; // Our listening box (1024 data + 1 for '\0') // We BLOCK (wait) here until the server sends data back to us. // We read up to 1024 bytes, leaving 1 byte for the stop-sign. auto bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived > 0) { // Convert to safe size_t for array indexing const auto len = static_cast(bytesReceived); // Add the invisible C-string stop-sign buffer[len] = '\0'; std::cout << "📥 Server echoed back: " << buffer << "\n"; } else { // If bytesReceived is 0, the server hung up on us politely. // If < 0, the connection broke. std::cerr << "⚠️ Server disconnected or error occurred while waiting for echo.\n"; } } // Note: We do NOT call CLOSE_SOCKET() here. // We want to keep the line open so the user can call talk() multiple times! // The destructor (~EchoClient) will handle the final hang-up.