breve descripción
En Linux, socketpair
la función se puede usar para crear un par de AF_UNIX
sockets de dominio de comunicación interconectados, donde un socket se puede usar para leer y el otro socket se puede usar para escribir. Este par de sockets se puede utilizar para la comunicación entre procesos (IPC) dentro del mismo proceso.
Estos son socketpair
los pasos básicos para crear un par de sockets usando una función:
-
Incluir archivos de encabezado
sys/types.h
ysys/socket.h
.unistd.h
-
Declare una matriz de longitud 2 como parámetro del descriptor de socket.
-
Llame
socketpair
a la función , pasándole la matriz de descriptores de socket como argumento. El prototipo de función es:int socketpair(int domain, int type, int protocol, int sv[2]);
domain
El parámetro especifica el dominio de comunicación, normalmente establecido en AF_UNIX.type
El parámetro especifica el tipo de socket y se puede establecer enSOCK_STREAM
oSOCK_DGRAM
.protocol
El parámetro generalmente se establece en 0, lo que significa que se usa el protocolo predeterminado.sv
El parámetro es una matriz de enteros de longitud 2, que se utiliza para guardar el descriptor de socket creado.
-
sv
Comunicación entre procesos utilizando descriptores de socket en una matriz.
ejemplo de linux
Aquí hay socketpair
un ejemplo simple basado en pasar datos entre procesos padre e hijo:
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
int main() {
int sockfd[2];
char buf[1024];
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) {
perror("socketpair");
return 1;
}
if ((pid = fork()) == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
close(sockfd[0]);
char msg[] = "Hello, parent!";
if (write(sockfd[1], msg, strlen(msg)) < 0) {
perror("write");
return 1;
}
close(sockfd[1]);
} else { // 父进程
close(sockfd[1]);
int n = read(sockfd[0], buf, sizeof(buf));
if (n < 0) {
perror("read");
return 1;
}
printf("Parent: received message from child: %s\n", buf);
close(sockfd[0]);
}
return 0;
}
El programa primero llama socketpair
a la función para crear un par de sockets de dominio UNIX, que se almacenan sockfd
en la matriz. Luego, el programa llama fork
a la función para crear un proceso secundario. En el proceso hijo, cierra sockfd[0]
, luego escribe un mensaje sockfd[1]
y cierra sockfd[1]
. En el proceso principal, se cierra sockfd[1]
, luego lee un sockfd[0]
mensaje , lo imprime en la salida estándar y finalmente se cierra sockfd[0]
.
Cuando este programa se ejecuta, crea un proceso secundario, el proceso secundario envía un mensaje al proceso principal, el proceso principal recibe el mensaje y lo imprime:
Parent: received message from child: Hello, parent!
Ejemplo de marco de trabajo de Android
android-12.0.0_r28/frameworks/native/libs/sensor/BitTube.cpp
bittube
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// sine we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
mReceiveFd = sockets[0];
mSendFd = sockets[1];
} else {
mReceiveFd = -errno;
ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
}
}
BitTube.cpp
Es el archivo de implementación de BitTube
la clase .
EntradaTransporte
android-12.0.0_r28/frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
std::unique_ptr<InputChannel>& outServerChannel,
std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%s(%d)", name.c_str(),
strerror(errno), errno);
outServerChannel.reset();
outClientChannel.reset();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
sp<IBinder> token = new BBinder();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
En el sistema de entrada de Android, InputTransport
use socketpair
la función para crear un par de sockets locales full-duplex (sockets) para pasar eventos de entrada entre el programa de aplicación y el controlador. Uno de los zócalos se usa para enviar eventos de entrada al controlador y el otro zócalo se usa para recibir eventos de entrada del controlador. De esta forma, InputTransport
puede enviar eventos de entrada al controlador y recibir eventos de entrada generados por el controlador.