Attempt to invoke connect(2) on already connected unix domaindatagram
socket fails with ECONNRESET
Maxim Sobolev
sobomax at portaone.com
Mon Jan 10 04:09:43 PST 2005
Folks,
I've discovered very strange behaviour of the connect(2) system call
when it's called on already connected unix domain datagram socket. In
this case connect(2) fails with ECONNRESET, which is weird. ECONNRESET
is not even listed among possible return values of connect(2). I've
confirmed this behaviour at 4.10 and 5.3 systems. Linux doesn't exhibit
this (mis?)behaviour.
As long as I can tell, this behaviour contradicts documentation,
connect(2) manpage says:
Generally, stream sockets may successfully connect() only
once; datagram sockets may use connect() multiple times to change
their association.
Attached please find small test program which illustrates the problem.
It forks itsels at the start, child becomes a server, while parent a
client. After each transaction server closes unix domain socket and
opens its again, while the client attempts to re-connect() to that unix
domain socket using already created socket object.
This mimics real-world scenario in which I've encountered the problem.
In this scenarion, there are two distinct processes communicating using
unix domain socket. Client uses connect() on already connected socket
object for performance reasons to avoid calling socket(2) for each
transaction. Everything works just fine until server is restarted. After
that any attempts to send command from the client to the server fails
with ECONNRESET until the client is restarted as well.
-Maxim
-------------- next part --------------
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#define UDS_NAME "/tmp/uds_test.sock"
#define sstosa(ss) ((struct sockaddr *)(ss))
static pid_t pid_kill;
void
prepare_ifsun(struct sockaddr_un *ifsun)
{
static char ch = '1' * 2;
memset(ifsun, '\0', sizeof(*ifsun));
#if !defined(__linux__) && !defined(__solaris__)
ifsun->sun_len = strlen(UDS_NAME);
#endif
ifsun->sun_family = AF_LOCAL;
strcpy(ifsun->sun_path, UDS_NAME);
//ifsun->sun_path[ifsun->sun_len - 1] = ch / 2;
ch++;
}
int
create_uds_server(void)
{
struct sockaddr_un ifsun;
int sock;
prepare_ifsun(&ifsun);
unlink(ifsun.sun_path);
sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (sock == -1)
err(1, "server: can't create socket");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &sock, sizeof(sock));
if (bind(sock, sstosa(&ifsun), sizeof(ifsun)) < 0)
err(1, "server: can't bind to a socket");
return sock;
}
void
connect_uds_server(int sock)
{
struct sockaddr_un ifsun;
prepare_ifsun(&ifsun);
if (connect(sock, sstosa(&ifsun), sizeof(ifsun)) < 0)
err(1, "client: can't connect to a socket");
}
static void
cleanup(void)
{
kill(pid_kill, SIGKILL);
}
int
main()
{
int sock, len;
pid_t pid;
pid = fork();
if (pid < 0)
err(1, "can't fork");
pid_kill = getpid();
if (pid != 0) {
/* Parent */
pid_kill = pid;
atexit(cleanup);
sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (sock < 0)
err(1, "client: can't create socket");
for (;;) {
sleep(1);
connect_uds_server(sock);
len = write(sock, &pid, sizeof(pid));
if (len < 0)
err(1, "client: can't write to a socket");
printf("client: wrote %d bytes to the socket\n", len);
}
} else {
/* Child */
atexit(cleanup);
for (;;) {
sock = create_uds_server();
len = recvfrom(sock, &pid, sizeof(pid), 0, NULL, NULL);
if (len < 0)
err(1, "server: can't read from a socket");
printf("server: read %d bytes from the socket\n", len);
close(sock);
}
}
exit (1);
}
More information about the freebsd-hackers
mailing list