4

I'm trying to create a unix domain socket server and client on Android in C++, using UDP. I need the client to send one message ("hi") to the server and from there on the server needs to send data to the client. I have successfully created the sockets on both sides, and I am able to receive a short message on the server from the client. However recvfrom(..) on the server does not populate the struct sockaddr *src_addr, socklen_t *addrlen arguments. And a subsequent sendto message using those src_addr and addrlen obviously fail (sendto returns -1).

Here's the code for the server :

int serverSocket = socket(PF_LOCAL, SOCK_DGRAM, 0);
int sendingBufferSize = OUTPUT_BUFFER_SIZE;
setsockopt(serverSocket, SOL_SOCKET, SO_SNDBUF, &sendingBufferSize, sizeof(sendingBufferSize));
struct sockaddr_un server;
memset(&server, 0, sizeof server);
server.sun_family = AF_LOCAL;

// the first byte of sun_path should be '\0' for abstract namespace
const char * socket_name = SOCKET_NAME;
server.sun_path[0] = '\0';
strcpy(server.sun_path + 1, socket_name);
socklen_t sockaddr_len = sizeof(server.sun_family) + strlen(socket_name) + 1;
if (bind(serverSocket, (struct sockaddr *) &server, sockaddr_len) < 0) {
    close(serverSocket);
    printf("Failed to bind the server socket\n");
    return -1;
}

printf("- Socket binding successful on socket path %s len %d\n", &server.sun_path[1], sockaddr_len);

_serverSocket = serverSocket;

And later I receive a message from the client:

int opcode;
_clientAddressLength = sizeof(struct sockaddr_un);
int numOfBytes = recvfrom(_serverSocket, _messageBuffer, INPUT_BUFFER, 0, (struct sockaddr *)&_clientAddress, &_clientAddressLength);
if (numOfBytes != 0)
{
    printf("Got a message from client\n");
    memcpy(&opcode, _messageBuffer, sizeof(opcode)); // I'm getting what I expected
}

However _clientAddress is empty and _clientAddressLength is set to 0.

And later

int result = sendto(_serverSocket
        , sendBuf
        , sendBufSize
        , 0
        , (const sockaddr*)&_clientAddress
        , _clientAddressLength);

returns -1

Thanks for your help guys!

EDIT:

Finally thanks to Nikolai N Fetissov's link and some digging I made it work. I'd like to post a fixed, 100% working example of bi-directional UDP communication over UNIX DOMAIN abstract namespace sockets. This is a copy of this http://www.toptip.ca/2013/01/unix-domain-socket-with-abstract-socket.html?m=1 link with a fixed bugs. For the future generations, as there aren't enough examples on this topic on the net.

server.cpp

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h> 
#include <string.h>

#include <unistd.h>`

#define SERVER_SOCKET "#UdsServer" // The # is replaced by 0 for abstract namespace

int main(void)
{
int socket_fd;
struct sockaddr_un server_address, client_address; 
int bytes_received, bytes_sent, integer_buffer;
socklen_t address_length = sizeof(server_address.sun_family)+strlen(SERVER_SOCKET );

if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
{
    perror("server: socket");
    return -1;
}

memset(&server_address, 0, sizeof(server_address));
server_address.sun_family = AF_UNIX;
memcpy(server_address.sun_path, SERVER_SOCKET,sizeof(SERVER_SOCKET)-1);// the path is not a C string, and is not NULL turminated
server_address.sun_path[0] = 0; 

if (bind(socket_fd,
         (const struct sockaddr *) &server_address,
         address_length) < 0)
{
    perror("server: bind");
    close(socket_fd);        
    return -1;
}

for (;;)
{
    bytes_received = recvfrom(socket_fd,
                              &integer_buffer,
                              sizeof(integer_buffer),
                              0, 
                              (struct sockaddr *) &client_address,
                              &address_length);

    if(bytes_received != sizeof(integer_buffer))
    {
        printf("Error: recvfrom - %d.\n", bytes_received);
    } else {
        printf("received: %d.\n", integer_buffer);

        integer_buffer += 10;

        bytes_sent = sendto(socket_fd,
                            &integer_buffer,
                            sizeof(integer_buffer),
                            0,
                            (struct sockaddr *) &client_address, 
                            address_length);
    }
}

close(socket_fd);

return 0;
}

client.cpp

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define SERVER_SOCKET "#UdsServer" 
#define CLIENT_SOCKET "#UdsClient"
int main(void)
{
int socket_fd;
struct sockaddr_un server_address, client_address; 
int bytes_received, bytes_sent, integer_buffer;
socklen_t address_length = sizeof(server_address.sun_family) + strlen(CLIENT_SOCKET);`

if((socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
{
    perror("client: socket");
    return -1;
}

memset(&client_address, 0, sizeof(client_address));
client_address.sun_family = AF_UNIX;
memcpy(client_address.sun_path, CLIENT_SOCKET,sizeof(CLIENT_SOCKET )-1 );
client_address.sun_path[0] = 0;

if(bind(socket_fd,
        (const struct sockaddr *) &client_address, 
        address_length) < 0)
{
    perror("client: bind");
    return -1;
}

memset(&server_address, 0, sizeof(server_address));
server_address.sun_family = AF_UNIX;
memcpy(server_address.sun_path, SERVER_SOCKET,sizeof(SERVER_SOCKET)-1);
server_address.sun_path[0] = 0;

integer_buffer = 1;

for (;;)
{
   bytes_sent = sendto(socket_fd, 
                       &integer_buffer,
                       sizeof(integer_buffer),
                       0,
                       (struct sockaddr *) &server_address, 
                       address_length);

   bytes_received = recvfrom(socket_fd,
                             &integer_buffer,
                             sizeof(integer_buffer),
                             0, 
                             (struct sockaddr *) &server_address,
                             &address_length);

   if (bytes_received != sizeof(integer_buffer))
   {
       printf("Error: recvfrom - %d.\n", bytes_received);
       return -1;
   }

   printf("received: %d\n", integer_buffer);

   integer_buffer += 1;

   sleep(10);
}

close(socket_fd);

return 0;
}

I hope it helps, and if you see more bugs please let me know

Michael P
  • 2,017
  • 3
  • 25
  • 33

1 Answers1

1

You are setting the address size argument to size of Internet address sockaddr_in, which is too small for Unix path.

Edit 0:

I believe the problem is that you are not bind(2)-ing the client socket, so it does not have a name, thus recvfrom(2) returns zero for address length.

Try code from here (the client needs a name different from the server name).

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • I have tried having the clientAddress of type sockaddr_un and set _clientAddressLength to sizeof(struct sockaddr_un). I still have the same result. (And I updated the question) – Michael P Jul 31 '15 at 22:10
  • What's the largest size you've tried? How big do you expect the structure to be? – David Schwartz Jul 31 '15 at 22:14
  • Michael, are you using *abstract* socket on purpose, or just by copy-paste from somewhere? – Nikolai Fetissov Jul 31 '15 at 22:40
  • David what size do you propose, I can try more, but I'm not sure that's it. Nikolai N Fetissov, what do you mean by abstract socket? – Michael P Jul 31 '15 at 22:57
  • I mean the first byte of name being zero. – Nikolai Fetissov Jul 31 '15 at 23:22
  • It's android specific – Michael P Aug 01 '15 at 00:38
  • Sorry, after reading about abstract sockets, I want to correct myself, it's not android specific, but I did take an example from Stackoverflow, and because of the permissions on Android, it's better to use an abstract socket namespace. I don't know if creating a regular local socket as a file name will work, I can try. But the issue here is definitely on the recvfrom, on the server. Because it does receive the data from the client, but it fails to populate the sender information fields – Michael P Aug 01 '15 at 18:35
  • 1
    @MichaelP - finally got to look deeper into this. See updated answer. – Nikolai Fetissov Aug 03 '15 at 21:21
  • @NikolaiNFetissov thank you, this is a progress. Now I am getting _clientAddressLength greater than zero. However the length is smaller than it should be, it's smaller than the socket namespace name + sizeof(sun_family). (First time I launch the server it's 3, and then it's 8).I tried modifying the _clientAddress and the length programmatically to force send to the address I am binding to on the client, and that fails too. – Michael P Aug 03 '15 at 22:20
  • Also following the example you sent to me, setting address_length as sizeof(struct sockaddr_un) doesn't work. I have to do this memcpy(server.sun_path, SOCKET_NAME, sizeof(SOCKET_NAME)-1); socklen_t sockaddr_len = offsetof(sockaddr_un, sun_path) + strlen(server.sun_path); server.sun_path[0] = 0; Instead, otherwise bind fails – Michael P Aug 03 '15 at 22:24
  • 2
    Finally found the problem strlen(server.sun_path) was wrong to use in this case to calculate the size of the address, as server.sun_path is NOT a C string and is NOT null terminated. I still can't make the server send to the client using the _clientAddress received from the recvfrom, however setting them manually ( I know the address of the client now) works. Thanks a lot @NikolaiNFetissov – Michael P Aug 03 '15 at 23:16