I. Concepts and usage
One problem:How do I create a full-duplex pipe?
The direct method is, of coursePipeCreate two groups of pipelines twice, but is it easier?
SocketpairMan socketpair:
Socketpair-create a pair of connected sockets, the two sockets are indistinguishableThat is to say,The two descriptors created with socketpair should be equivalent..
Ii. Usage
# Define daTA1 "test string 1"
# Define daTA2 "test string 2"
# Include <stdio. h>
# Include <string. h>
# Include <unistd. h>
# Include <sys/socket. h>
# Include <sys/UN. h>
# Include <sys/types. h>
# Include <sys/Wait. H>
# Include <errno. h>
Main ()
{
Int sockets [2], child;
Char Buf [1024];
/* Get the socket pair */
If (Socketpair(Af_unix, sock_stream, 0,Sockets) <0 ){
Printf ("error % d on socketpair", errno );
Exit (1 );
}
/* Create child process */
If (child =Fork() =-1 ){
Printf ("fork error % d", errno );
Exit (1 );
}
If (child! = 0 ){ /* This is the parent */
/* Close child's end of socket */
Close (Sockets [0]);
/* Read message from child */
If (Read(Sockets [1], Buf, sizeof (BUF) <0 ){
Printf ("error % d reading socket", errno );
Exit (1 );
}
Printf ("parent: --> % s \ n", Buf );
/* Write message to child */
If (Write(Sockets [1], daTA1, sizeof (DATA1) <0 ){
Printf ("error % d writing socket", errno );
Exit (1 );
}
/* Finished */
Close (Sockets [1]);
}
Else
{/* The child */
/* Close parent's end of socket */
Close (Sockets [1]);
/* Send message to parent */
If (write (Sockets [0], daTA2, sizeof (DATA1) <0 ){
Printf ("error % d writing socket", errno );
Exit (1 );
}
/* Get message from parent */
If (read (Sockets [0], Buf, sizeof (BUF) <0 ){
Printf ("error % d reading socket", errno );
Exit (1 );
}
Printf ("Child: --> % s \ n", Buf );
/* Finished */
Close (Sockets [0]);
}
}
From the code above, we can find that,The parent process reads and writes from sockets [1], and the child process reads and writes from sockets [0]. It is indeed in full duplex mode.
The only thing worth considering is why do I need to disable sockets [0] and sockets [1] In the Parent and Child processes?
3. One implementation
# Include <sys/types. h>
# Include <sys/socket. h>
# Include <netinet/in. h>
# Include <fcntl. h>
# Include <stdio. h>
Socketpair (AF, type, protocol, FD)
Int AF;
Int type;
Int protocol;
Int FD [2];
{
Int listen_socket;
Struct sockaddr_in sin [2];
Int Len;
/* The following is onLy valid if type = sock_stream */
If (type! = Sock_stream)
Return-1;
/* Create a temporary listen socket; temporary, so any port is good */
Listen_socket = socket (AF, type, Protocol );
If (listen_socket <0)
{
Perror ("creating listen_socket ");
Return-1;
}
Sin [0]. sin_family = AF;
Sin [0]. sin_port = 0;/* use any port number */
Sin [0]. sin_addr.s_addr = inet_makeaddr (inaddr_any, 0 );
If (BIND (listen_socket, & sin [0], sizeof (sin [0]) <0)
{
Perror ("bind ");
Return-1;
}
Len = sizeof (sin [0]);
/* Read the port number we got, so that our client can connect to it */
If (getsockname (listen_socket, & sin [0], & Len) <0)
{
Perror ("getsockname ");
Return-1;
}
/* Put the listen socket in listening mode */
If (Listen (listen_socket, 5) <0)
{
Perror ("listen ");
Return-1;
}
/* Create the client socket */
FD [1] = socket (AF, type, Protocol );
If (FD [1] <0)
{
Perror ("creating client_socket ");
Return-1;
}
/* Put the client socket in non-blocking connecting mode */
Fcntl (FD [1], f_setfl, fcntl (FD [1], f_getfl, 0) | o_ndelay );
If (connect (FD [1], & sin [0], sizeof (sin [0]) <0)
{
Perror ("Connect ");
Return-1;
}
/* At the listen-side, accept the incoming connection we generated */
Len = sizeof (sin [1]);
If (FD [0] = accept (listen_socket, & sin [1], & Len) <0)
{
Perror ("accept ");
Return-1;
}
/* Reset the client socket to blocking mode */
Fcntl (FD [1], f_setfl, fcntl (FD [1], f_getfl, 0 )&~ O_ndelay );
Close (listen_socket );
Return 0;
}
The Unix domain socket technology is used. First, a listener socket is created. Because it is temporary, the port and IP address bound to the socket can be arbitrary (specified by the system), and then the listen is executed; create a client socket. Its descriptor is fd [1]. Execute connect. Finally, return to the server and execute accept. The returned descriptor is the actual communication descriptor FD [0].
Iv. Supplement
Socketpair is also commonly used for processing descriptor transfer.
We know that the file descriptor opened by the parent process before the child process is fork can be inherited by the quilt process, but once the child process has been created, how can the file descriptor opened by the parent process be passed to the child process? UNIX provides technologies to meet this requirement. This is a wonderful and powerful technology for transferring file descriptors between processes on the same host.
Imagine that we are trying to implement a server and receive connections from multiple clients. We want to adopt concurrent sub-processes to process simultaneous connections from multiple clients. At this time, we may have two ideas:
1. Each time the client establishes a connection, we fork a sub-process to process the connection;
2. Create a process pool in advance. Each time the client establishes a link, the server selects an idle (idle) sub-process from the pool to process the connection.
The latter is obviously more efficient, because it reduces the performance loss of sub-process creation and greatly enhances the timeliness of response. The problem we mentioned above is that all sub-processes are fork before the server listen connects to a connection. That is to say, the new connection descriptor sub-process is unknown, the parent process must be passed to it. After receiving the corresponding connection descriptor, it can communicate with the corresponding client. Here we can use the 'transfer file descriptor 'method.