Re: AF_UNIX sockets: strange behaviour

From: Stephen Hemminger
Date: Wed Nov 17 2004 - 13:15:14 EST


On Wed, 17 Nov 2004 16:29:14 +0100 (CET)
Catalin Drula <Catalin.Drula@xxxxxxx> wrote:

> Hi,
>
> I have a small application that communicates over Bluetooth. I use
> connection-oriented UNIX domain sockets (AF_UNIX, SOCK_STREAM) to
> communicate between the applications's threads. When reading from
> one of these sockets, I get a strange behaviour: if I read all the
> bytes that are available (13, in this case) all at once, it's fine;
> however, if I try to read in two smaller batches (say, first time
> 6, and second time 7), the first read returns (with the 6 bytes), but
> the second read never returns.
>
> As far as I know, this is not expected behaviour for SOCK_STREAM sockets.
> I tried looking into the problem so I instrumented net/unix/af_unix.c to
> see what is going on. More specifically, I was focusing on the function
> unix_stream_recvmsg. Here is what I noticed:
>
> - there is a skb in the sk_receive_queue with a len of 13
> - 6 bytes are read from it
> - a skb with the remaining 7 bytes is requeued in sk_receive_queue
> - on the next call to unix_stream_recvmsg, the sk_receive_queue is
> empty (!)
>
> Thus, this confirms the behaviour observed from userspace. Is this a bug?
> Who could be removing the skb from the receive_queue?
>
> Thanks for any ideas/suggestions,
>
> Catalin
>

See the following, it doesn't seem to be a problem (this is on 2.6.10-rc2)

/* Unix domain socket buffer test */
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>

static void client(const char *path, const char *msg) {
int s, len = strlen(msg);
static struct sockaddr_un sun = { .sun_family = AF_UNIX };

s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) { perror("socket"); return; }

strncpy(sun.sun_path, path, sizeof(sun.sun_path));
if (connect(s, (struct sockadr *)&sun, sizeof(sun)) < 0) {
perror("connect");
return;
}

if (write(s, msg, len) != len)
perror("write");
close(s);
}


static void server(int s) {
struct sockaddr_un nsun;
int len = sizeof(nsun);
int ns = accept(s, (struct sockaddr *) &nsun, &len);
int cc;
char buf[3];

if (ns < 0) {
perror("accept");
return;
}

while ((cc = read(ns, buf, sizeof(buf))) > 0)
write(1, buf, cc);

putchar('\n');

if (cc < 0)
perror("read request");

if (write(ns, "done", 4) != 4)
perror("write response");
}

int main(int argc, char **argv) {
int s;
static struct sockaddr_un sun = { .sun_family = AF_UNIX };

if (argc != 3) { fprintf(stderr, "Usage: udtest name message\n"); return 1; }

s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) { perror("socket"); return 1; }

strncpy(sun.sun_path, argv[1], sizeof(sun.sun_path));
if (bind(s, (struct sockaddr *) &sun, sizeof(sun)) < 0) {
perror("bind"); return 1;
}

listen(s, 1);

if (fork() == 0) {
close(s);
client(sun.sun_path, argv[2]);
} else {
server(s);
close(s);
unlink(sun.sun_path);
}
return 0;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/