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 #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); }Received on Mon Jan 10 2005 - 11:09:43 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:25 UTC