UNIX domain protocol
UNIX domain sockets are more efficient than TCP for UNIX domain sockets on the same host, almost twice times faster than TCP (because UNIX domain sockets do not need to go through the network protocol stack, do not need to package/unpacking, calculate checksums, maintain sequence numbers and responses, etc.). Just copy the application layer data from one process to another, and the UNIX domain protocol mechanism is essentially a reliable communication, and the network protocol is designed for unreliable communication.
UNIX domain sockets can pass file descriptors between processes on the same host computer;
The difference between UNIX domain sockets and traditional sockets is the description of the protocol family by the path name;
UNIX domain sockets also provide both stream-oriented and packet-oriented API interfaces, similar to TCP and UDP, but message-oriented UNIX sockets are also reliable, and messages are neither lost nor sequenced.
The process of using a UNIX domain socket is very similar to a network socket, and you must first call the socket to create a socket file descriptor, family specified as Af_unix, and type can choose Sock_dgram/sock_stream ;
UNIX Domain socket address structure:
#define UNIX_PATH_MAX 108struct sockaddr_un{ sa_family_t sun_family; /* Af_unix */ char Sun_path[unix_path_max]; /* pathname */};
/**server End **/void echoserver (int sockfd); int main () {signal (SIGCHLD, sighandlerforsigchild); int LISTENFD = socket (Af_unix, sock_stream, 0); if (LISTENFD = =-1) err_exit ("Socket error"); Char pathname[] = "/tmp/test_for_unix"; Unlink (pathname); struct Sockaddr_un servaddr; servaddr.sun_family = Af_unix; strcpy (Servaddr.sun_path, pathname); if (Bind (LISTENFD, (struct sockaddr *) &servaddr, sizeof (servaddr)) = =-1) err_exit ("Bind error"); if (listen (LISTENFD) = =-1) err_exit ("Listen error"); while (true) {int connfd = accept (LISTENFD, NULL, NULL); if (CONNFD = =-1) err_exit ("Accept error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); else if (PID > 0) Close (CONNFD); else if (PID = = 0) {close (LISTENFD); Echoserver (CONNFD); Close (CONNFD); Exit (exit_success); }}}void Echoserver (int sockfd) {char buf[bufsiz]; while (true) {memset (buf, 0, sizeof (BUF)); int recvbytes = Read (SOCKFD, buf, sizeof (BUF)); if (Recvbytes < 0) {if (errno = = eintr) continue; else Err_exit ("read socket error"); } else if (recvbytes = = 0) {cout << "client connect closed ..." << Endl; Break } cout << buf; if (Write (SOCKFD, buf, recvbytes) = =-1) err_exit ("Write socket error"); }}
/**client-Side code **/void echoclient (int sockfd); int main () {int SOCKFD = socket (Af_unix, sock_stream, 0); if (SOCKFD = =-1) err_exit ("Socket error"); Char pathname[] = "/tmp/test_for_unix"; struct Sockaddr_un servaddr; servaddr.sun_family = Af_unix; strcpy (Servaddr.sun_path, pathname); if (Connect (SOCKFD, (struct sockaddr *) &servaddr, sizeof (servaddr)) = =-1) err_exit ("Connect error"); Echoclient (SOCKFD);} void echoclient (int sockfd) {char Buf[bufsiz] = {0}; while (Fgets (buf, sizeof (BUF), stdin)! = NULL) {if (write (SOCKFD, buf, strlen (buf)) = = =-1) err_exit ( "Write socket error"); memset (buf, 0, sizeof (BUF)); int recvbytes = Read (SOCKFD, buf, sizeof (BUF)); if (recvbytes = =-1) {if (errno = = eintr) continue; else Err_exit ("read socket error"); } cout << buf; memset (buf, 0, sizeof (BUF)); }}
UNIX domain sockets programming note points
1.bind Success will create a file with permissions of 0777 & ~umask
2.sun_path It is best to use an absolute path to the file in the/tmp directory, and the server must first unlink the file before specifying it;
3.UNIX Domain protocol supports streaming socket interface (need to handle sticky packet problem) and report type socket interface (based on datagram)
4.UNIX domain streaming socket connect when the listener queue is full, a econnrefused is returned immediately, unlike TCP, where the incoming SYN is ignored if the listening queue is full, which causes the other party to retransmit the syn.
passing file descriptors
Socketpair
#include <sys/types.h> #include <sys/socket.h>int socketpair (int domain, int type, int protocol, int sv[2]);
Create a full-duplex flow pipeline
Parameters:
Domain: Protocol family, you can use Af_unix (af_local) UNIX domain protocol, and on Linux, the function will only support this kind of protocol;
Type: Socket type, you can use Sock_stream
Protocol: protocol type, general padding is 0;
SV: The returned socket pair;
The Socketpair function is similar to the pipe function: it can only communicate between processes that have affinity, but the anonymous pipe created by the pipe is half-duplex, and Socketpair can be thought of as creating a full-duplex pipeline.
You can use Socketpair to create a returned socket pair for parent-child process communication, as in the following example:
int main () { int sockfds[2]; if (Socketpair (Af_unix, Sock_stream, 0, Sockfds) = =-1) err_exit ("Socketpair error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); Parent process, only responsible for data printing else if (pid > 0) { close (sockfds[1]); int ival = 0; while (true) { cout << "value =" << ival << Endl; Write (Sockfds[0], &ival, sizeof (ival)); Read (Sockfds[0], &ival, sizeof (ival)); Sleep (1); } } Child process, only responsible for data changes (+1) else if (pid = = 0) { close (sockfds[0]); int ival = 0; while (read (sockfds[1], &ival, sizeof (ival)) > 0) { + + ival; Write (Sockfds[1], &ival, sizeof (ival));}}
Sendmsg/recvmsg
#include <sys/types.h> #include <sys/socket.h>ssize_t sendmsg (int sockfd, const struct MSGHDR *msg, int flags ); ssize_t recvmsg (int sockfd, struct MSGHDR *msg, int flags);
They are similar to the sendto/send and RECVFROM/RECV functions, except that they can transfer more complex data structures, not only to transmit general data, but also to transfer additional data, such as file descriptors.
MSGHDR structure struct msghdr{ void *msg_name; /* Optional Address */ socklen_t Msg_namelen; /* Size of Address */ struct Iovec *msg_iov; /* Scatter/gather Array */ size_t Msg_iovlen; /* # elements in Msg_iov */ void *msg_control; /* Ancillary data, see below */ size_t msg_controllen;/* Ancillary data buffer len */ int msg_flags; / * flags on received message */};struct Iovec/ * scatter/gather array items */{ void *iov_base;
/* starting address */ size_t Iov_len; /* Number of bytes to transfer */};
MSGHDR struct members explained:
1) Msg_name: That is, the peer's address pointer, do not care when set to null;
2) Msg_namelen: Address length, do not care when set to 0;
3) Msg_iov: A pointer to a struct IOVEC, pointing to the normal data that needs to be sent, see.
The member Iov_base can be considered as BUF when transmitting normal data;
The member Iov_len is the size of the BUF;
4) Msg_iovlen: When there are n Iovec structures, this value is n;
5) Msg_control: is a pointer to the CMSGHDR structure (see), when you need to send auxiliary data (such as control information/file descriptor), you need to set the field, when sending normal data, there is no need to care about the field, and Msg_ Controllen can be set to 0;
6) There may be more than one MSG_CONTROLLEN:CMSGHDR structure (see):
7) Flags: do not care;
CMSGHDR structure struct cmsghdr{ socklen_t cmsg_len; /* Data byte count, including header */ int cmsg_level; /* Originating protocol */ int cmsg_type; /* protocol-specific Type */ * followed by unsigned char cmsg_data[]; */};
In order to align, there may be some padding bytes (see), related to the implementation of the system, but we do not have to care about, you can get related values by some function macros, as follows:
#include <sys/socket.h>struct cmsghdr *cmsg_firsthdr (struct MSGHDR *msgh);//Get the first message of the secondary data struct CMSGHDR *cmsg_ NXTHDR (struct msghdr *msgh, struct CMSGHDR *cmsg);//Get the next message for auxiliary data size_t cmsg_align (size_t length); size_t Cmsg_space ( size_t length), size_t cmsg_len (size_t length),//length the amount of data used (actual), see (Two of the middle portion of the data being populated) unsigned char *cmsg_data (struct Cmsghdr *cmsg);
Pass-through file descriptors between processes
/** Example: Encapsulates two functions send_fd/recv_fd used to pass file descriptors between processes **/int send_fd (int sockfd, int sendfd) {//Populate the Name field struct MSGHDR msg; Msg.msg_name = NULL; Msg.msg_namelen = 0; Fill iov field struct Iovec Iov; Char Sendchar = ' + '; Iov.iov_base = &sendchar; Iov.iov_len = 1; Msg.msg_iov = &iov; Msg.msg_iovlen = 1; Fill cmsg field struct CMSGHDR cmsg; Cmsg.cmsg_len = Cmsg_len (sizeof (int)); Cmsg.cmsg_level = Sol_socket; Cmsg.cmsg_type = scm_rights; * (int *) Cmsg_data (&cmsg) = SENDFD; Msg.msg_control = &cmsg; Msg.msg_controllen = Cmsg_len (sizeof (int)); Send if (sendmsg (SOCKFD, &msg, 0) = =-1) return-1; return 0;}
int recv_fd ( int sockfd) {//Fill name field struct MSGHDR msg; Msg.msg_name = NULL; Msg.msg_namelen = 0; Fill iov field struct Iovec Iov; Char Recvchar; Iov.iov_base = &recvchar; Iov.iov_len = 1; Msg.msg_iov = &iov; Msg.msg_iovlen = 1; Fill cmsg field struct CMSGHDR cmsg; Msg.msg_control = &cmsg; Msg.msg_controllen = Cmsg_len (sizeof (int)); Receive if (Recvmsg (SOCKFD, &msg, 0) = =-1) return-1; return * (int *) Cmsg_data (&cmsg);}
int main () {int sockfds[2]; if (Socketpair (Af_unix, Sock_stream, 0, Sockfds) = =-1) err_exit ("Socketpair error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); The child process opens the file as read-only, character the file description to the child process else if (PID = = 0) {close (sockfds[1]); int fd = open ("Read.txt", o_rdonly); if (fd = =-1) err_exit ("Open error"); cout << "in child, FD =" << fd << Endl; SEND_FD (Sockfds[0], FD); }//parent process reads data from file descriptor else if (PID > 0) {close (sockfds[0]); int fd = RECV_FD (sockfds[1]); if (fd = =-1) err_exit ("RECV_FD error"); Cout << "in parent, FD =" << fd << Endl; Char Buf[bufsiz] = {0}; int readbytes = Read (fd, buf, sizeof (BUF)); if (readbytes = =-1) err_exit ("read FD error"); cout << buf; }}
Analysis:
We know that the parent process opens the file descriptor before fork, the child process can be shared, but the child process opens the file descriptor, the parent process is not shared, the above program is an example of a child process opened a file descriptor, and then through the SEND_FD The function passes the file descriptor to the parent process, and the parent process can receive the file descriptor through the RECV_FD function. First create a file read.txt after entering a few characters, and then run the program;
Attention:
(1) only UNIX domain protocols can pass file descriptors between native processes;
(2) transferring file descriptors between processes is not the value of passing file descriptors (in fact, the two values of SEND_FD/RECV_FD are different), but rather to create a new file descriptor in the receive process. The file descriptor and the file descriptor that is passed in the sending process point to the same file table entry in the kernel.
Socket Programming Practice (--unix) domain protocol