I work with unix sockets for the first time, and I stumbled upon a problem with unix socket file not getting automatically removed. It seems, after I create a socket, I can't just exit a program, instead I should remove the socket file as well.
But why the socket file doesn't get destroyed after the program that created it quits? It looks like there should be a way to connect somehow an application to this remaining file, or why is it still there?
Below is a code to illustrate (it creates a unix socket and then just exits):
#include <cstdio>
#include <sys/socket.h>
#include <sys/un.h>
int CreateSocket(const char* filename) {
if (strlen(filename) > sizeof(sockaddr_un::sun_path)) {
puts("Too long filename!");
return -1;
}
int fdSock = socket(AF_UNIX, SOCK_DGRAM, 0);
if(fdSock == -1){
perror("socket");
return -1;
}
sockaddr_un server;
server.sun_family = AF_UNIX;
strcpy(server.sun_path, filename);
if(bind(fdSock, (sockaddr*)&server, sizeof(sockaddr_un)) == -1){
perror("socket");
return -1;
}
return 0;
}
You can remove the socket file with a simple: unlink(path); Wherever you program exit your program, check if it exists, and remove it.
The traditional UNIX system calls are blocking. For example: accept() blocks the caller until a connection is present. If no messages space is available at the socket to hold the message to be transmitted, then send() normally blocks.
Unix sockets are bidirectional. This means that every side can perform both read and write operations. While, FIFOs are unidirectional: it has a writer peer and a reader peer. Unix sockets create less overhead and communication is faster, than by localhost IP sockets.
Valid socket types in the UNIX domain are: SOCK_STREAM, for a stream-oriented socket; SOCK_DGRAM, for a datagram-oriented socket that preserves message boundaries (as on most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder datagrams); and (since Linux 2.6.
Short quote from the awesome book "The Sockets Networking API: UNIX® Network Programming Volume 1, Third Edition" by W. Richard Stevens; Bill Fenner; Andrew M. Rudoff.
Chapter 15. Example: bind of Unix Domain Socket.
Remove pathname first. The pathname that we bind to the socket is the command-line argument. But the bind will fail if the pathname already exists in the filesystem. Therefore, we call unlink to delete the pathname, in case it already exists. If it does not exist, unlink returns an error, which we ignore.
Actually, you can use the path to detect the server exists. When server terminating it have to remove a file to let the client to understand there is no server that can read incoming connections. For example Docker works this way: if the daemon not spawned there is no docker.sock
file in your system.
I cannot fully understand your questions. I think you do not understand the socket()
mechanism.
I will explain. Socket here is similar to pointer or file descriptor.
There are two different programs: the server and the client. They are not dependent. Server opens a socket and waits for a connection. Client opens its OWN socket, connects to a remote address. Server handles the connection. Data exchange between these two programs occurs, then (if necessary) connections are terminated.
Here are examples of simplest client and server programs for TCP/IP, they show how the socket()
mechanism works.
Server code:
void * server (void)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
socklen_t lenkeepalive;
struct net_pack recvline;
int i,n;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
int keepalive =1;
lenkeepalive = sizeof(keepalive);
if(setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
{
perror("server: setsockopt()");
shutdown(listenfd,SHUT_RDWR);
close(listenfd);
exit(EXIT_FAILURE);
}
pthread_create (NULL, NULL, (void *)server_send, NULL);
for(;;)
{
if(connfd = accept(listenfd, (SA *) NULL, NULL)>0){
bzero(&recvline, sizeof(recvline));
//try get msg from connected client
while ( (recv(connfd, &recvline, MAXLINE, 0)) > 0)
{
printf("server: recvline.msg - %s\n",recvline.msg);
bzero(&recvline, sizeof(recvline));
/*
...
*/
// send answ
encode(&recvline,"hello2",H); //make a msg
if(send(connfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL); < 0)
{
printf("server_send: send error %d (%s)\n",errno, strerror(errno));
}
//discard client connection
shutdown(connfd,SHUT_RDWR);
close(connfd);
}
}
}
// some error occurs
perror("server: accept()");
shutdown(listenfd,SHUT_RDWR);
close(listenfd);
return 0;
}
Client code
void * client (void)
{
socklen_t lenkeepalive;
int sockfd, keepalive, n;
struct net_pack recvline; //pack for msg
struct sockaddr_in servaddr;
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{printf("client: socket error %d ( %s )\n ",errno, strerror(errno));}
keepalive = 1;
lenkeepalive = sizeof(keepalive);
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
{
perror("client: setsockopt()");
shutdown(sockfd,SHUT_RDWR);
close(sockfd);
exit(EXIT_FAILURE);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if (inet_pton(AF_INET, servip, &servaddr.sin_addr) <= 0)
printf("client: inet_pton error for %s\r\n", servip);
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("client: connect to %s error %d ( %s )\r\n",servip, errno, strerror(errno));
}
else
{
// send a msg
encode(&recvline,"hello",H); //make a msg
n = send(sockfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL);
if(n < 0)
{printf("client: i cannot send, error %d (%s)\n",errno, strerror(errno));}
bzero(&recvline, sizeof(recvline));
//recv data from server
while( (n = recv(sockfd, &recvline, MAXLINE, 0)) >= 0)
{
printf("client: recvline.msg - %s\n",recvline.msg);
bzero(&recvline, sizeof(recvline));
}
}
// some error occurs
perror("client: recv()");
shutdown(sockfd,SHUT_RDWR);
close(sockfd);
return 0;
}
In your case you should use sockaddr_un instead of sockaddr_in, and some necessary flags like AF_UNIX
, SOCK_DGRAM
and other.
Unix domain sockets operate on files so you can use all the file permission features on them etc.
However if you do not care for these you actually want to use abstract unix domain sockets
, by setting sun_path[0]
to '\0'
:
abstract: an abstract socket address is distinguished (from a pathname socket) by the fact that sun_path[0] is a null byte ('\0'). The socket's address in this namespace is given by the additional bytes in sun_path that are covered by the specified length of the address structure. (Null bytes in the name have no special significance.) The name has no connection with filesystem pathnames. When the address of an abstract socket is returned, the returned addrlen is greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of the socket is contained in the first (addrlen - sizeof(sa_family_t)) bytes of sun_path.
As claimed, they do not respect the file permission operations:
Socket permissions have no meaning for abstract sockets: the process umask(2) has no effect when binding an abstract socket, and changing the ownership and permissions of the object (via fchown(2) and fchmod(2)) has no effect on the accessibility of the socket.
When you process exists you can safely reuse the identifier used in bind
:
Abstract sockets automatically disappear when all open references to the socket are closed.
However mind you that this is a non-portable linux extension:
The abstract socket namespace is a nonportable Linux extension.
Source
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With