I have this server application that sets up a local communication stream socket for clients to connect to. If a second instance of the server is launched and it tries to bind to the same address (file name), bind()
should fail with EADDRINUSE
. But it does not. Why?
Here is pared-down code that showcases the problem:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
int make_socket(const char *filename, int style) {
struct sockaddr_un name;
int sock;
size_t size;
sock = socket(PF_LOCAL, style, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
if ((!filename) || (filename[0] == '\0')) {
return sock;
}
name.sun_family = AF_LOCAL;
strncpy(name.sun_path, filename, sizeof(name.sun_path));
name.sun_path[sizeof(name.sun_path) - 1] = '\0';
size = SUN_LEN(&name);
if (bind(sock, (struct sockaddr *) &name, size) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
return sock;
}
int make_stream_socket(const char *filename) {
return make_socket(filename, SOCK_STREAM);
}
#define TS_SERVER "/tmp/socket-server"
int main(int argc, char *argv[]) {
int sock_server, sock_client;
struct sockaddr_un name_client;
size_t size_name_client;
int i;
fd_set client_set, selected_set;
int quitting = 0;
fprintf(stderr, "Server: starting up\n");
unlink(TS_SERVER);
sock_server = make_stream_socket(TS_SERVER);
fprintf(stderr, "Server: accepting connections on %d\n", sock_server);
if (0 > listen(sock_server, 1)) {
perror("listen(connection requests)");
exit(EXIT_FAILURE);
}
FD_ZERO(&client_set);
FD_SET(sock_server, &client_set);
while (!quitting) {
fprintf(stderr, "Server: waiting for connections\n");
selected_set = client_set; //shallow-clone client_set
if (0 > select(FD_SETSIZE, &selected_set, NULL, NULL, NULL)) {
perror("select(for readability)");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Server: connection(s) received\n");
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET(i, &selected_set)) {
if (i == sock_server) {
fprintf(stderr, "Server: accepting connection\n");
size_name_client = sizeof(name_client);
sock_client = accept(sock_server,
(struct sockaddr *) &name_client,
(socklen_t *) &size_name_client);
//Ubuntu Launchpad bug 1463553: accept() returns
// an off-by-one size_name_client
size_name_client = SUN_LEN(&name_client);
if (sock_client < 0) {
perror("accept(connection request)");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Server: accepted connection request from '%s' on %d\n",
name_client.sun_path, sock_client);
quitting = 1;
close(sock_client);
break; //out of the for
} //if (i == sock_server)
} //if (FD_ISSET(i, &selected_set))
} //for
} //while
fprintf(stderr, "Server: shutting down\n");
close(sock_server);
unlink(TS_SERVER);
exit(EXIT_SUCCESS);
}
After compilation, run a first instance from the command line, then run a second instance from another command line. The second instance should fail on bind()
, but does not.
Because:
You remove the existing socket file, which allows a new one to be created in its place.