Avoid blocking the program while using TCP in C/C++

Rocco Galati
Rocco Galati used Ask the Experts™
on
I'm using Ubuntu and I have a C++ application which uses WxWidgets and sends a string over TCP several times. It works pretty fine only if the router and the connection are OK, otherwise it gets stuck and I need to manually quit the application.
I would like to send the data without blocking the application flow in case of errors.

I call the function in this way:

   
 SendCommand ptr;
    if ( ptr.sendcommand(16,16,1) ){
    printf("Porta Aperta\n");
    } else {
    printf("Errore di comunicazione - porta non aperta\n");
    }

Open in new window


and this is the function:

int SendCommand::sendcommand(int relay_on, int relay_off, int stop){
    printf("eseguo la funzione nella classe\n");

    std::array<uint8_t, 8> command1;
    std::array<uint8_t, 8> command2;

    switch(relay_on){

    case 16: command1 = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x10, 0x7B}; // switch on the relay 16
    break;

    }

    switch(relay_off){

    case 16: command2 = {0x58, 0x01, 0x11, 0x00, 0x00, 0x00, 0x10, 0x7A}; // switch off the relay 16
    break;

    }

    int sockfd, n;
    struct sockaddr_in servaddr;

    std::string serveraddr = "192.168.1.4";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if ( sockfd < 0 )
    {
        cerr << "Error creating socket! " << strerror(errno) << endl;
        return -1;

    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
    servaddr.sin_port = htons(3000);

    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        cerr << "Error connecting socket!" << strerror(errno) << endl;
        close(sockfd);
        return -1;
    }

    //Relay #14 per porta bilancia
    printf("Apro la porta\n");

    int bytes_to_send1 = sizeof(command1);
    int bytes_to_send2 = sizeof(command2);
    int bytes_sent1 = 0;
    int bytes_sent2 = 0;

    do
    {
        n = send(sockfd, command1.data() + bytes_sent1, bytes_to_send1 - bytes_sent1, 0);
        if ( n < 0 )
        {
            cerr << "Error writing to socket!" << strerror(errno) << endl;
            close(sockfd);
        }
        bytes_sent1 += n;
    }
    while (bytes_sent1 < bytes_to_send1);

n=0;
sleep(stop);
do
    {
        n = send(sockfd, command2.data() + bytes_sent2, bytes_to_send2 - bytes_sent2, 0);
        if ( n < 0 )
        {
            cerr << "Error writing to socket!" << strerror(errno) << endl;
            close(sockfd);
        }
        bytes_sent2 += n;
    }
    while (bytes_sent2 < bytes_to_send2);
    close(sockfd);
n=0;


return 1;
}

Open in new window


I tried to use threads or fork() and it works, but is there any other easier solution for this?

P.S. When it get stuck because there is not internet connection, it blocks here:

if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {

        cerr << "Error connecting socket!" << strerror(errno) << endl;
        close(sockfd);
        return -1;
    }

Open in new window


it never enters the if condition.

I see that there is the BOOST ASIO library, but it seems to be too hard to understand for me since I do not know how to adapt any sample (like this https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp) to my case.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
David FavorFractional CTO
Distinguished Expert 2018

Commented:
Likely what you're targeting is async I/O.

Search for code related to calling select() on multiple sockets, then only processing a socket when it becomes ready to process.

Another simple approach is to just use an ALARM signal handler.

Note: However you do this, you may have to retool your entire code flow, as serialized code flow generally has to change to work with async data flows.
Rocco GalatiR&D Engineer

Author

Commented:
thank you for your support! is it possible to have a practical example based on my case please? I'm a beginner so it would be better for me to have a sample to study.. thank you
Rocco GalatiR&D Engineer

Author

Commented:
Is select() only available on the server side?

I can't modify the server behavior because I have no access to it.
I can only change the the client side code.
Rocco GalatiR&D Engineer

Author

Commented:
I solved it by using boost asio libraries for non-blocking TCP clients.
Top Expert 2016
Commented:
the point is that the client needs an asynchronous thread whcih connects to the server and does the communication. the main thread is running the gui and therefore your program is still responsive. when the thread has got a result from server, it would send a message to the main thread that results are available. best is to use a shared container where both the main thread and the worker thread have access to. by sending the success message the worker thread would pass ownership of the container to the main thread which then - for example - could fetch data from the container and display them in the gui. finally the container was deleted and the thread quits (or resets and waits for another request).

you may use pthread library for this.

Sara

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial