The socket API was originally designed for network communication, but later an IPC Mechanism was developed on the socket framework, namely the Unix domain socket. Although the network socket can also be used for inter-process communication between the same host (through loopback address 127.0.0.1), the Unix domain socket is more efficient for IPC: No network protocol stack is required, you do not need to package, unpack, calculate, and verify the checksum, maintain the serial number, and respond. Instead, you only need to copy application layer data from one process to another. This is because the IPC Mechanism is essentially reliable communication, and the network protocol is designed for unreliable communication. UNIX domain socket also provides two API interfaces, namely stream-oriented and data packet-oriented, similar to TCP and UDP. However, message-oriented Unix domain socket is reliable, and messages are neither lost nor disordered.
The Unix domain socket is full-duplex and has rich API interface semantics. Compared with other IPC Mechanisms, it has become the most widely used IPC Mechanism, for example, the X Window Server communicates with the GUI program through the Unix domain socket.
The process of using Unix domain socket is very similar to that of network socket. You must first call socket () to create a socket file descriptor. The address family is specified as af_unix, And the type can be sock_dgram or sock_stream, the Protocol parameter is still set to 0.
The most obvious difference between Unix domain socket and network socket programming is that the address format is different. It is represented by the addr_un struct. The socket address of network programming is the IP address and port number, the Unix domain socket address is the path of a socket-type file in the file system. This socket file is created by the BIND () call. If the file already exists when BIND () is called, then the BIND () error is returned.
The following program binds a Unix domain socket to an address.
# Include
# Include
# Include
# Include
# Include
Int main (void)
{
Int FD, size;
Struct sockaddr_un;
Memset (& UN, 0, sizeof (un ));
Un. sun_family = af_unix;
Strcpy (UN. sun_path, "foo. socket ");
If (FD = socket (af_unix, sock_stream, 0) perror ("socket error ");
Exit (1 );
}
Size = offsetof (struct sockaddr_un, sun_path) + strlen (UN. sun_path );
If (BIND (FD, (struct sockaddr *) & UN, size) perror ("BIND error ");
Exit (1 );
}
Printf ("Unix domain socket bound/N ");
Exit (0 );
}
Note that the offsetof macro in the program is defined in the stddef. h header file:
# Define offsetof (type, member) (INT) & (type *) 0)-> Member)
Offsetof (struct sockaddr_un, sun_path) is the offset of the sun_path Member of the sockaddr_un struct In the struct, that is, it is the sun_path member starting from the first few bytes of the struct. Think about how this macro implements this function?
The running result of this program is as follows.
$./A. Out
UNIX domain socket bound
$ LS-l Foo. Socket
Srwxrwxr-x 1 user 0 Aug 22 :43 Foo. Socket
$./A. Out
BIND error: address already in use
$ RM Foo. Socket
$./A. Out
UNIX domain socket bound
The following is the listen module of the server. Similar to network socket programming, listen is required after bind to provide services through the Bind Address (that is, the socket file.
# Include
# Include
# Include
# Include
# Define qlen 10
/*
* Create a server endpoint of a connection.
* Returns FD if all OK ,*/
Int serv_listen (const char * name)
{
Int FD, Len, err, rval;
Struct sockaddr_un;
/* Create a Unix domain stream socket */
If (FD = socket (af_unix, sock_stream, 0) Return (-1 );
Unlink (name);/* in case it already exists */
/* Fill in socket address structure */
Memset (& UN, 0, sizeof (un ));
Un. sun_family = af_unix;
Strcpy (UN. sun_path, name );
Len = offsetof (struct sockaddr_un, sun_path) + strlen (name );
/* Bind the name to the descriptor */
If (BIND (FD, (struct sockaddr *) & UN, Len) rval =-2;
Goto errout;
}
If (Listen (FD, qlen) rval =-3;
Goto errout;
}
Return (FD );
Errout:
Err = errno;
Close (FD );
Errno = err;
Return (rval );
}
The following is the server's accept module. The client address obtained through accept should also be a socket file. If it is not a socket file, an error code will be returned. If it is a socket file, after the connection is established, the file is useless. Call unlink to delete it and return the user ID of the client program through the uidptr parameter.
# Include
# Include
# Include
# Include
# Include
Int serv_accept (INT listenfd, uid_t * uidptr)
{
Int clifd, Len, err, rval;
Time_t staletime;
Struct sockaddr_un;
Struct stat statbuf;
Len = sizeof (un );
If (clifd = accept (listenfd, (struct sockaddr *) & UN, & Len) Return (-1);/* often errno = eintr, IF signal caught */
/* Obtain the client's uid from its calling address */
Len-= offsetof (struct sockaddr_un, sun_path);/* Len of pathname */
Un. sun_path [Len] = 0;/* null terminate */
If (STAT (UN. sun_path, & statbuf) rval =-2;
Goto errout;
}
If (s_issock (statbuf. st_mode) = 0 ){
Rval =-3;/* not a socket */
Goto errout;
}
If (uidptr! = NULL)
* Uidptr = statbuf. st_uid;/* return uid of caller */
Unlink (UN. sun_path);/* we're done with pathname now */
Return (clifd );
Errout:
Err = errno;
Close (clifd );
Errno = err;
Return (rval );
}
The following is the connect module of the client. Different from network socket programming, the Unix domain Socket Client generally explicitly calls the BIND function, instead of relying on the address automatically allocated by the system. The advantage of a socket file name specified by the client BIND is that the file name can contain the client PID so that the server can distinguish different clients.
# Include
# Include
# Include
# Include
# Include
# Include
# Define cli_path "/var/tmp/"/* + 5 for PID = 14 chars */
/*
* Create a client endpoint and connect to a server.
* Returns FD if all OK ,*/
Int cli_conn (const char * name)
{
Int FD, Len, err, rval;
Struct sockaddr_un;
/* Create a Unix domain stream socket */
If (FD = socket (af_unix, sock_stream, 0) Return (-1 );
/* Fill socket address structure with our address */
Memset (& UN, 0, sizeof (un ));
Un. sun_family = af_unix;
Sprintf (UN. sun_path, "% S % 05d", cli_path, getpid ());
Len = offsetof (struct sockaddr_un, sun_path) + strlen (UN. sun_path );
Unlink (UN. sun_path);/* in case it already exists */
If (BIND (FD, (struct sockaddr *) & UN, Len) rval =-2;
Goto errout;
}
/* Fill socket address structure with server's address */
Memset (& UN, 0, sizeof (un ));
Un. sun_family = af_unix;
Strcpy (UN. sun_path, name );
Len = offsetof (struct sockaddr_un, sun_path) + strlen (name );
If (connect (FD, (struct sockaddr *) & UN, Len) rval =-4;
Goto errout;
}
Return (FD );
Errout:
Err = errno;
Close (FD );
Errno = err;
Return (rval );
}
The following is your own time. Use the above modules to write a complete client/server communication program.