Search code examples
c++socketstcpnetwork-programmingtcpsocket

Failed TCP Socket Communication


I tried to communicate with my friend on terminal using TCP socket. But i failed.I think i can connect server but it cannot be. i gave ip address and port info when i run program, then i waited long time for entering message and i entered. After that there was nothing on server side.

server.cpp

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string> 
using namespace std;

int main(int argc, char* argv[]){
 if (argc != 3){
 cout << "Usage: ./server <CLIENT_IP_ADDRESS> <PORT>\n"; 
 return -1;
 }

char* client_ip_address = argv[1];
int port = stoi(argv[2]);

// Create a socket for server
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) { // If the socket creation is unsuccessful, it returns -1
    cout << "Socket creation has failed.\n";
    return -1;
}

// Address and Port informations
sockaddr_in serverAddress; //sockaddr_in: It is the data type that is used to store the address of the socket.
serverAddress.sin_family = AF_INET; 
serverAddress.sin_port = htons(port); //This function is used to convert the unsigned int from machine byte order to network byte order.
serverAddress.sin_addr.s_addr = inet_addr(client_ip_address);


// Socket binding
bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)); 
   

if (listen(serverSocket, 10) == -1) {
    cout << "Connection listening error: " <<endl;
    close(serverSocket);
    return -1;
}

std::cout << "The server has been started. Listening for connections...\n";

sockaddr_in clientAddr;
socklen_t clientSize = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientSize);

while (true) {
    
    char messageBuffer[1024] = {0};
    recv(clientSocket, messageBuffer, sizeof(messageBuffer), 0);
    cout << "Message from client: " << messageBuffer << endl;

    if (strcmp(messageBuffer, "Fin") == 0) {
        close(serverSocket);
        break;
    }
    // Sending a message to the client.
    char buffer[1024] = {0};
    cout << "Enter a message: ";
    cin.getline(buffer, sizeof(buffer));
    send(clientSocket, buffer, strlen(buffer), 0);
    if (strcmp(buffer, "Fin") == 0) {
        close(serverSocket);
        break;
    }
}

std::cout << "(Fin) message received. Server is shutting down...\n";

return 0;
}

client.cpp

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string> 
using namespace std;

int main(int argc, char* argv[]){
 if (argc != 3){
 cout << "Usage: ./client <SERVER_IP_ADRES> <PORT>\n"; 
 return -1;
 }


char* server_ip_address = argv[1];
int port = stoi(argv[2]);

// Create a socket for client
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
    cout << "Socket creation has failed.\n";
    return -1;
}

// Address and Port informations
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = inet_addr(server_ip_address);


// Connecting server
  connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address));

while (true) {
    // Kullanıcıdan bir mesaj al
    char messageBuffer[1024] = {0};
    std::cout << "Enter a message: ";
    cin.getline(messageBuffer, sizeof(messageBuffer));
    // Sending a message to the server.
    send(clientSocket, messageBuffer, strlen(messageBuffer), 0);
    if (string(messageBuffer)== "Fin"){
        close(clientSocket);
        break;
    }
    
    char buffer[1024] = {0};
    ssize_t bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
    cout << "Message from server: " << string(buffer, bytesReceived) << std::endl;
    if (string(buffer)== "Fin"){
        close(clientSocket);
        break;
    }

    }
    std::cout << "(Fin) message received. Client is shutting down...\n";


return 0;
}

I took turns waiting to communicate with my friend. I achieved this in my own local. I opened a new tab in my terminal and ran and tested both the server and the client from there, it worked without any problems. I can't do this with my friend from another city.


Solution

  • Since your friend is in another city, they are likely on a different network (unless you setup a private VPN between your two computers).

    When communicating with computers on different networks, there are additional steps you need to do outside of your code:

    • You need to make sure the ISP (Internet Service Provider) on the server side is not blocking incoming connections. Many ISPs do not allow customers to run servers on their networks unless they pay extra for that feature.

    • You need to make sure any firewall running on the server computer, or on either network, is not blocking the connection.

    • If the server computer is running behind a NAT router (which is typical for modern Wifi setups), the router must have a port forwarding rule configured in order to route incoming connections on the WAN side to the server computer on the LAN side. Clients can then connect to the router's public WAN IP/port and be routed to the LAN IP/port that the server socket is listening on.

      If the router supports uPNP, you can configure the port forwarding rule dynamically from your server code. Otherwise, you have to configure the router directly.


    As for your code itself, there are several problems with it.

    In the server:

    • Make sure to bind() your server socket to the local IP of the network interface that is connected to the router/Internet. Or, you can alternatively bind() the socket to INADDR_ANY (0.0.0.0) to bind to all network interfaces on the local machine. DO NOT bind to the client's IP, as your code suggests.

    • You are not doing any error checking on bind(), accept(), recv(), send(), or cin.getline().

    • recv() does not return a null-terminated string, and you are not terminating your messageBuffer manually. Your client is using the return value of recv() to know how many bytes were actually received. The server needs to do the same thing.

    • When calling send(), you are not sending any kind of message length or message terminator to let the client know when the message is finished being sent. For TCP, there is no 1:1 relationship between send() and recv(), both functions can report fewer bytes than requested, so you must call both functions in loops to make sure all expected data is sent/received.

    In the client:

    • You are not doing any error checking on connect(), recv(), send(), or cin.getline().

    • When calling send() and recv(), you are not handling message termination correctly, same as with the server.

    Try something more like this:

    server.cpp

    #include <iostream>
    #include <sys/socket.h>
    #include <netinet/in.h> 
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string> 
    #include <cstdint>
    using namespace std;
        
    bool readRaw(int sckt, void* buffer, size_t bufsize) {
        char *ptr = static_cast<char*>(buffer);
        while (bufsize > 0) {
            ssize_t numBytes = recv(sckt, ptr, bufsize, 0);
            if (numBytes < 0) {
                cout << "Read error: " << errno << '\n';
                return false;
            }
            if (numBytes == 0) {
                cout << "Client disconnected\n";
                return false;
            }
            ptr += numBytes;
            bufsize -= numBytes;
        }
        return true;
    }
    
    bool sendRaw(int sckt, const void* buffer, size_t bufsize) {
        const char *ptr = static_cast<const char*>(buffer);
        while (bufsize > 0) {
            ssize_t numBytes = send(sckt, ptr, bufsize, 0);
            if (numBytes < 0) {
                cout << "Send error: " << errno << '\n';
                return false;
            }
            ptr += numBytes;
            bufsize -= numBytes;
        }
        return true;
    }
    
    bool readUint32(int sckt, uint32_t &value) {
        if (!readRaw(sckt, &value, sizeof(value))) return false;        
        value = ntohl(value);
        return true;
    }
    
    bool sendUint32(int sckt, uint32_t value) {
        value = htonl(value);
        return sendRaw(sckt, &value, sizeof(value)));
    }
    
    bool readString(int sckt, string &message) {
        message.clear();
        uint32_t len;
        if (!readUint32(sckt, len)) return false;
        message.resize(len);
        return readRaw(sckt, message.data(), len);
    }   
    
    bool sendString(int sckt, const string &message) {
        uint32_t len = message.size();
        if (!sendUint32(len)) return false;
        return sendRaw(sckt, message.c_str(), len);
    }
    
    /* alternatively:
    
    bool readString(int sckt, string &message) {
        char buffer[1024], ch;
        size_t len = 0;
        message.clear();
        while (true) {
            if (!readRaw(sckt, &ch, 1)) return false;
            if (ch == '\0') {
                if (len > 0) message.append(buffer, len);
                break;
            }
            if (len == sizeof(buffer)) {
                message.append(buffer, len);
                len = 0;
            }
            buffer[len++] = ch;
        }
        return true;    
    }
    
    bool sendString(int sckt, const string &message) {
        return sendRaw(sckt, message.c_str(), message.size()+1);    
    }
    
    */
    
    int main(int argc, char* argv[]) {
        if (argc != 3) {
            cout << "Usage: ./server <LISTEN_IP_ADDRESS> <PORT>\n"; 
            return -1;
        }
        
        char* listen_ip_address = argv[1];
        int port = stoi(argv[2]);
        
        int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket < 0) {
            cout << "Socket creation error: " << errno << '\n';
            return -1;
        }
        
        sockaddr_in serverAddress; //sockaddr_in: It is the data type that is used to store the address of the socket.
        serverAddress.sin_family = AF_INET; 
        serverAddress.sin_port = htons(port); //This function is used to convert the unsigned int from machine byte order to network byte order.
        serverAddress.sin_addr.s_addr = inet_addr(listen_ip_address);
        
        if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
            cout << "Socket bind error: " << errno << '\n';
            close(serverSocket);
            return -1;
        }
    
        if (listen(serverSocket, 1) == -1) {
            cout << "Socket listen error: " << errno << '\n';
            close(serverSocket);
            return -1;
        }
        
        cout << "The server has been started. Listening for connection...\n";
        
        sockaddr_in clientAddr;
        socklen_t clientSize = sizeof(clientAddr);
    
        int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientSize);
        if (clientSocket < 0) {
            cout << "Connection accept error: " << errno << '\n';
            close(serverSocket);
            return -1;
        }
    
        cout << "Client connected\n";
    
        while (true) {
            
            std::string message;
            if (!readString(clientSocket, message))
                break;
    
            cout << "Message from client: " << message << '\n'; 
    
            if (message == "Fin")
                break;
    
            message.clear();
    
            cout << "Enter a message: ";
            if (!getline(cin, message))
                break;
    
            if (!sendString(clientSocket, message))
                break;
    
            if (message == "Fin")
                break;
        }
        
        cout << "Server is shutting down...\n";
        close(serverSocket);
        
        return 0;
    }
    

    client.cpp

    #include <iostream>
    #include <sys/socket.h>
    #include <netinet/in.h> 
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string> 
    #include <cstdint>
    using namespace std;
        
    bool readRaw(int sckt, void* buffer, size_t bufsize) {
        char *ptr = static_cast<char*>(buffer);
        while (bufsize > 0) {
            ssize_t numBytes = recv(sckt, ptr, bufsize, 0);
            if (numBytes < 0) {
                cout << "Read error: " << errno << '\n';
                return false;
            }
            if (numBytes == 0) {
                cout << "Server disconnected\n";
                return false;
            }
            ptr += numBytes;
            bufsize -= numBytes;
        }
        return true;
    }
    
    bool sendRaw(int sckt, const void* buffer, size_t bufsize) {
        const char *ptr = static_cast<const char*>(buffer);
        while (bufsize > 0) {
            ssize_t numBytes = send(sckt, ptr, bufsize, 0);
            if (numBytes < 0) {
                cout << "Send error: " << errno << '\n';
                return false;
            }
            ptr += numBytes;
            bufsize -= numBytes;
        }
        return true;
    }
    
    bool readUint32(int sckt, uint32_t &value) {
        if (!readRaw(sckt, &value, sizeof(value))) return false;        
        value = ntohl(value);
        return true;
    }
    
    bool sendUint32(int sckt, uint32_t value) {
        value = htonl(value);
        return sendRaw(sckt, &value, sizeof(value)));
    }
    
    bool readString(int sckt, string &message) {
        message.clear();
        uint32_t len;
        if (!readUint32(sckt, len)) return false;
        message.resize(len);
        return readRaw(sckt, message.data(), len);
    }
    
    bool sendString(int sckt, const string &message) {
        uint32_t len = message.size();
        if (!sendUint32(len)) return false;
        return sendRaw(sckt, message.c_str(), len);
    }
    
    /* alternatively:
    
    bool readString(int sckt, string &message) {
        char buffer[1024], ch;
        size_t len = 0;
        message.clear();
        while (true) {
            if (!readRaw(sckt, &ch, 1)) return false;
            if (ch == '\0') {
                if (len > 0) message.append(buffer, len);
                break;
            }
            if (len == sizeof(buffer)) {
                message.append(buffer, len);
                len = 0;
            }
            buffer[len++] = ch;
        }
        return true;    
    }
    
    bool sendString(int sckt, const string &message) {
        return sendRaw(sckt, message.c_str(), message.size()+1);    
    }
    
    */
    
    int main(int argc, char* argv[]){
        if (argc != 3){
            cout << "Usage: ./client <SERVER_IP_ADRES> <PORT>\n"; 
            return -1;
        }
    
        char* server_ip_address = argv[1];
        int port = stoi(argv[2]);
    
        int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (clientSocket < 0) {
            cout << "Socket creation error: " << errno << '\n';
            return -1;
        }
        
        sockaddr_in server_address;
        server_address.sin_family = AF_INET;
        server_address.sin_port = htons(port);
        server_address.sin_addr.s_addr = inet_addr(server_ip_address);
        
        if (connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) {
            cout << "Socket connect error: " << errno << '\n';
            close(clientSocket);
            return -1;
        }
        
        while (true) {
    
            cout << "Enter a message: ";
    
            string message;
            if (!getline(cin, message))
                break;
    
            if (!sendString(clientSocket, message))
                break;
            
            if (message == "Fin")
                break;
            
            if (!readString(clientSocket, message))
                break;
            
            cout << "Message from server: " << meessage << '\n';
    
            if (message == "Fin")
                break;
        }
        
        cout << "Client is shutting down...\n";
        close(clientSocket);
        
        return 0;
    }