I. Unix domain Protocol
The Unix domain protocol is not an actual protocol family. It only communicates with a client-server on the same host, one method of using the same API (set interface or XTI) as communication between customers and servers on different hosts.
When the customer and the server are on the same host, Unix domain protocol is a substitute for IPC communication.
Unix provides two types of interfaces: byte stream set interface (similar to TCP) and datagram set interface (similar to UDP ).
1. Unix domain interface address Structure
struct sockaddr_un { sa_family_t sun_family; /* AF_LOCAL */ char sun_path[104]; /* null-terminated pathname */};
The path name in the sun_path array must end with an empty character.
2. socketpair Function
The socketpair function establishes a pair of interconnected sets. This function team uses Unix domain sets.
# Include <sys/socket. h> int socketpair (int family, int type, int protocol, int sockfd [2]); // return: 0 is returned for success, and-1 is returned for Error
The family must be AF_LOCAL, the protocol must be 0, and the type can be SOCK_STREAM or SOCK_DGRAM. The newly created two interface descriptions are returned as sockfd [0] And sockfd [1]
The two interfaces created have no names, that is, they do not involve implicit bind.
The result of the socketpair call by specifying the type parameter as SOCK_STREAM is called stream pipe, which is similar to the general Unix pipeline (generated by the pipe function), but the stream pipeline is full-duplex, that is, both descriptive words can be read and written.
3. descriptor Transmission
Generally, the method to pass the descriptor is as follows:
- After fork is called, the child process shares all the opened descriptions of the parent process.
- All description words remain open when exec is called.
In the first example, the process opens a description word, calls fork, and then the parent process closes the description word so that the child process can process the description word. In this way, an opened description is passed from the parent process to the child process.
Steps involved in the descriptor transfer between two processes:
1) create a Unix domain interface for a byte stream or Datagram
2) A process can use any Unix function that returns a descriptive word to open a descriptive word, such as open, pipe, mkfifo, socket, or accept.
3) The sending process establishes a msghdr structure, which contains the description to be passed.
4 ). the receiving process calls recvmsg to receive this descriptor on the Unix socket from step 1. Instead of passing the descriptive number, the receiving process creates a new descriptive word, the description in the file table pointing to the kernel is the same as that sent by the sending process.
4. program instance
#include "unp.h"intmy_open(const char *pathname, int mode){ int fd, sockfd[2], status; pid_t childpid; char c, argsockfd[10], argmode[10]; Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); if ( (childpid = Fork()) == 0) { /* child process */ Close(sockfd[0]); snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]); snprintf(argmode, sizeof(argmode), "%d", mode); execl("./openfile", "openfile", argsockfd, pathname, argmode, (char *) NULL); err_sys("execl error"); } /* parent process - wait for the child to terminate */ Close(sockfd[1]); /* close the end we don't use */ Waitpid(childpid, &status, 0); if (WIFEXITED(status) == 0) err_quit("child did not terminate"); if ( (status = WEXITSTATUS(status)) == 0) Read_fd(sockfd[0], &c, 1, &fd); else { errno = status; /* set errno value from child's status */ fd = -1; } Close(sockfd[0]); return (fd);}
Ii. Non-blocking I/O
1. Overview
1) input operations: read, readv, recv, recvfrom, and recvmsg functions.
2). Output operations: write, writev, send, sendto, and sendmsg functions.
3) receive external connections: accept Function
4). initialize outgoing connections: used for TCP connect Functions
2. Non-blocking read and write
We maintain two buffers: to accommodate data from the standard input to the server, and fr to accommodate data from the server to the standard output.
Here the program only provides the first half of the non-blocking part:
#include "unp.h"voidstr_cli(FILE *fp, int sockfd){ int maxfdp1, val, stdineof; ssize_t n, nwritten; fd_set rset, wset; char to[MAXLINE], fr[MAXLINE]; char *toiptr, *tooptr, *friptr, *froptr; val = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, val | O_NONBLOCK); val = Fcntl(STDIN_FILENO, F_GETFL, 0); Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK); val = Fcntl(STDOUT_FILENO, F_GETFL, 0); Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK); toiptr = tooptr = to; /* initialize buffer pointers */ friptr = froptr = fr; stdineof = 0; maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1; for ( ; ; ) { FD_ZERO(&rset); FD_ZERO(&wset); if (stdineof == 0 && toiptr < &to[MAXLINE]) FD_SET(STDIN_FILENO, &rset); /* read from stdin */ if (friptr < &fr[MAXLINE]) FD_SET(sockfd, &rset); /* read from socket */ if (tooptr != toiptr) FD_SET(sockfd, &wset); /* data to write to socket */ if (froptr != friptr) FD_SET(STDOUT_FILENO, &wset); /* data to write to stdout */ Select(maxfdp1, &rset, &wset, NULL, NULL);
3. Non-blocking connect
Non-blocking connect has three purposes:
1). We can perform other operations at the same time in the three-way handshake. It takes a round-trip time to complete a connect, and it can be anywhere, from a LAN within a few milliseconds to a WAN within several hundred milliseconds or seconds.
2). You can use this technology to establish multiple connections at the same time. This is common in Web browsers.
3). Because we use select to wait for the connection to complete, we can set a time limit for select to shorten the connect timeout time.
Although non-blocking connect sounds simple, it has some details that must be handled
1 ). even if the set of interfaces is non-blocking, if the Connected Server is on the same host, the connection is usually established immediately when you call connect to establish a connection. we must handle this situation;
2). The implementation from Berkeley (and Posix.1g) has two rules related to select and non-blocking IO:
- When the connection is established successfully, the socket descriptor becomes writable;
- When a connection error occurs, the set sub-Descriptor becomes readable and writable;
Note:: When an interface set has an error, it will be marked as readable and writable by the select call;
Program instance:
#include "unp.h"intconnect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec){ int flags, n, error; socklen_t len; fd_set rset, wset; struct timeval tval; flags = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); error = 0; if ( (n = connect(sockfd, saptr, salen)) < 0) if (errno != EINPROGRESS) return (-1); /* Do whatever we want while the connect is taking place. */ if (n == 0) goto done; /* connect completed immediately */ FD_ZERO(&rset); FD_SET(sockfd, &rset); wset = rset; tval.tv_sec = nsec; tval.tv_usec = 0; if ( (n = Select(sockfd + 1, &rset, &wset, NULL, nsec ? &tval : NULL)) == 0) { close(sockfd); /* timeout */ errno = ETIMEDOUT; return (-1); } if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { len = sizeof(error); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) return (-1); /* Solaris pending error */ } else err_quit("select error: sockfd not set"); done: Fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ if (error) { close(sockfd); /* just in case */ errno = error; return (-1); } return (0);}
4. Non-blocking accept
In blocking mode, the server will always block the accept call, knowing that another customer has established a connection, but during this period, the server simply blocks the accept call, unable to process any other ready Descriptors
Solution in non-blocking accept Mode
1). When you use select to learn when a listener socket has completed the connection preparation to be accept, always set this listener socket to non-blocking.
2 ). the following errors are ignored in subsequent accept calls: EWOULDBLOCK (when the customer terminates the connection), ECONNABORTED (when the customer terminates the connection), and EPROTO (when the customer terminates the connection) and EINTR (if any signal is captured)
Iii. ioctl operations
In network programs, ioctl is often used to obtain information about all interfaces on the host when the program starts: The interface address, whether the interface supports broadcasting, and whether multicast is supported.
# Include <unistd. h> int ioctl (int fd, int request,.../* void * arg */); // return: if the request is successful, error-1
The third parameter is always a pointer, but the pointer type depends on the request parameter. We can divide network-related requests into 6 types:
- Set interface operations
- File Operations
- Interface operation
- ARP high-speed cache operations
- Route table operations
- Stream System
For usage details, see UNP.