Mikołaj Bajkowski
Mikołaj Bajkowski

Reputation: 11

Bluetooth socket can only read once, why?

I wanted to make a continuous connection between my computer and Raspberry Pi Zero W. Everything is working except when I want to interact with socket, the read() function works only once. I wished to send multiple messages via the socket.

This is happening no matter which device is a server/client. I wonder if there is some part of functionality that I don't yet understand and that's why I ran into problems.

What is happening:

  1. Start Pi server and listen
  2. Start PC client and connect
  3. Pi accepts and waits on read()
  4. PC sends a message via write()
  5. Pi receives a message via read(), prints the message and waits on read() again (I suppose)
  6. PC sends another message via write()
  7. Pi doesn't respond to that and is stuck on read() <- why?
  8. PC closes the socket and finishes execution.
  9. Pi respond to that and finishes too.

Code for server on Raspberry Pi:

#include <cstdio>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <cstdlib>

//server
int main(int argc, char **argv)
{
    struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
    char buf[1024] = { 0 };
    int s, client, bytes_read;
    socklen_t opt = sizeof(rem_addr);

    // allocate socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // bind socket to port 1 of the first available
    // local bluetooth adapter
    loc_addr.rc_family = AF_BLUETOOTH;
    loc_addr.rc_bdaddr = (bdaddr_t) {{0, 0, 0, 0, 0, 0}};
    loc_addr.rc_channel = (uint8_t) 1;
    bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

    // put socket into listening mode
    listen(s, 16);
    // accept connection
    client = accept(s, (struct sockaddr *) &rem_addr, &opt);
    ba2str(&rem_addr.rc_bdaddr, buf);
    fprintf(stderr, "accepted connection from %s\n", buf);

    while (true) {
        // read data from the client
        memset(buf, 0, sizeof(buf));
        printf("I want to receive\n");
        bytes_read = read(client, buf, sizeof(buf));

        if (bytes_read > 0) {
            printf("received [%s]\n", buf);
        } else exit(1);

        if (buf[0] == 'e' && buf[1] == 'd') break;
    }
    // close connection
    close(client);
    close(s);
    return 0;
}

Code for sender on a PC:

#include <cstdio>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <iostream>

//client
int main(int argc, char **argv)
{
    int s, status;

    do {
        struct sockaddr_rc addr = { 0 };
        char dest[18] = "B8:27:EB:46:9D:46";

        // allocate a socket
        s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

        // set the connection parameters (who to connect to)
        addr.rc_family = AF_BLUETOOTH;
        addr.rc_channel = (uint8_t) 1;
        str2ba( dest, &addr.rc_bdaddr );

        // connect to server
        status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

        // send a message
        while (true) {
            std::cout << "write: ";
            std::string buf;
            std::cin >> buf;

            if( status == 0 ) {
                status = write(s, buf.c_str(), buf.length());
            }

            if( status < 0 ) perror("error");

            if(buf == "ed") break;
            sleep(1);
        }

    } while(status < 0);

    close(s);

    return 0;
}

Output on Pi:

pi@zero:~/RpiBT/builds/r $ ./Pi2 
accepted connection from 60:D8:19:A9:42:50
I want to receive
received [lal]
I want to receive
pi@zero:~/RpiBT/builds/r $ ./Pi2 
accepted connection from 60:D8:19:A9:42:50
I want to receive
received [kra]
I want to receive
pi@zero:~/RpiBT/builds/r $ 

Output on PC:

[beton@fedorabeton cmake-build-debug]$ ./Pi 
write: lal
write: lal
write: ed
[beton@fedorabeton cmake-build-debug]$ ./Pi 
write: kra
write: ark
write: ^C
[beton@fedorabeton cmake-build-debug]$ 

Upvotes: 0

Views: 694

Answers (1)

Mikołaj Bajkowski
Mikołaj Bajkowski

Reputation: 11

Turns out there was one thing that I did wrong:

The client side...

if ( status == 0 ) {
    status = write(s, buf.c_str(), buf.length());
}

After that status variable is no longer equal to zero, so after a loop it couldn't reach the write() function. I now remember to reset status back to zero after a successful write().

So it was not that the server was stuck on read() for some weird reason. In fact, the client could reach write() only once.

I noticed it after I started simplifying my program to writing two write() functions with hard-coded strings. It's not the first time this has helped me so to anybody: when you run into problem, break it down, simplify and avoid assuming the wrong cause.

Upvotes: 0

Related Questions