React Native: Conexión directa peer to peer con react-native-udp

Martin
6 min readMay 31, 2023

Si estas buscando como conectar dos dispositivos para intercambiar información con una app React Native, este es tu artículo. Para lograrlo utilizaremos la biblioteca react-native-udp que nos ofrece un soporte de sockets que nos permitirá la interconexión entre dos dispositivos con el único requisito de que se encuentren conectados a la misma red.

¡Empecemos!

Vamos a desarrollar un app que nos permitirá actuar tanto servidor como cliente. Para ello mostrara por pantalla la ip del dispositivo para poder actuar como servidor y permitirá ingresar una ip para poder actuar como cliente.

Lo primero que haremos será instalar las librerías react-native-upd y react-native-network-info, esta ultima nos permitirá conocer la ip asignada al dispositivo.

npm install react-native-udp
npm install react-native-network-info

Importamos las librerías, averiguamos la ip con el método getIPV4Address() y creamos un hook donde guardar la ip para mostrarla después por pantalla.

import UdpSocket from 'react-native-udp'
import { NetworkInfo } from 'react-native-network-info'

export default function App() {
const [ipAddress, setIpAddress] = useState('');
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
};

fetchIpAddress();
})
}

Ahora dentro del useEffect crearemos el socket teniendo en cuenta una variable hook llamada isServer que nos indicara si el socket actuara como servidor o como cliente. Este socket lo guardaremos tambien en un hook, de este modo lo podremos llamar en el return del useEffect para cerrar el socket cuando se cierre la aplicación

 if (isServer) {
setConnectionStatus(`Servidor desconectado`)
const server = UdpSocket.createSocket('udp4');

server.on('message', (data, rinfo) => {
setMensaje(data.toString())
console.log('Mensaje recibido:', data.toString());
});

server.on('listening', () => {
console.log('Servidor escuchando en el puerto:', server.address().port);
setConnectionStatus(`Servidor escuchando en el puerto ${server.address().port}`);
});

server.bind(8888);

setSocket(server);
} else {
const client = UdpSocket.createSocket('udp4');
client.bind(8887);
setSocket(client);
}

return () => {
socket && socket.close();
};

Explicación del código: Comprobamos si hemos seleccionado actuar como servidor con la variable isServer, la cual se inicializa a FALSE por lo que la app se ejecutara como cliente en primera instancia. Si actuamos como servidor crearemos el socket en la variable server y llamaremos al metodo on, primero como ‘message’ para poder recibir los mensajes del cliente y lugo como ‘listening’ que se ejecutara cuando el socket ya este escuchando. Por ultimo le vinculamos un puerto con el metodo bind, en este caso el 8888 y guardamos el socket en el hook setSocket.

Si queremos actuar como cliente en esta parte del código colo crearemos el socket, vincularemos el puerto y lo guardaremos con el hook setSocket.

Como explicaba antes, en el return del useEffect cerramos el socket para cerrarlo cuando lo haga la app.

En este punto añadiremos el método que permitirá enviar un mensaje desde el cliente al servidor.

const sendMessage = () => {
if (isServer) return;

const client = socket;

client.send('¡Hola desde el cliente!', undefined, undefined, 8888, ipServer, (error) => {
if (error) {
console.log('Error al enviar el mensaje:', error);
} else {
console.log('Mensaje enviado correctamente');
}

};

Comprobamos si actuamos como servidor para que no haga nada en ese caso. Si actuamos como cliente, recuperamos el socket con el hook y llamamos al metodo send especificando el mensaje que enviamos y el puerto y ip del servidor. Si miramos la variable error podremos saber si el mensaje se ha enviado correctamente o no, aunque esto no nos asegura que el servidor lo reciba.

Ahora construiremos la pantalla donde tendremos un Text donde se visualizara el estado de conexión del servidor, un Button para iniciar o parar el servidor, un Button para enviar el mensaje en modo cliente, un TextInput para informar la ip del server, un Text donde aparecera la ip del dispositivo y un Text donde visualizaremos el mensaje recibido cuando estemos en modo servidor

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{connectionStatus}</Text>
<Button
title={isServer ? 'Detener Servidor' : 'Iniciar Servidor'}
onPress={() => setIsServer(!isServer)}
/>
<Button title="Enviar Mensaje" onPress={sendMessage} disabled={isServer} />
<TextInput
onChangeText={setIpServer}
value={ipServer}
/>
<Text>Dirección IP: {ipAddress}</Text>
<Text>Mensaje recibído: {mensaje}</Text>
</View>
);

¡Ya estamos listos para probar!

Abriremos dos terminales y conectaremos dos dispositivos a nuestro ordenador, ejecutaremos en los terminales el comando expo run:android — device para poder ejecutar la aplicación seleccionando el dispositivo en el que queramos que se ejecute.

Ejecutamos la aplicación en los dos dispositivos y le damos al botón de iniciar servidor en el dispositivo A

Ahora configuramos la ip del dispositivo A en el TextInput del dispositivo B

Clicamos en el botón enviar mensaje del dispositivo B y veremos como llega el mensaje al dispositivo A

¡Ya lo tenemos!

A continuación os dejo el código completo con una mejora, el servidor responderá al cliente después de que este le envié el mensaje.

import React, { useState, useEffect } from 'react';
import { View, Text, Button, TextInput } from 'react-native';
import UdpSocket from 'react-native-udp';
import { NetworkInfo } from 'react-native-network-info';
export default function App() {
const [isServer, setIsServer] = useState(false);
const [connectionStatus, setConnectionStatus] = useState('');
const [socket, setSocket] = useState('');
const [ipAddress, setIpAddress] = useState('');
const [mensaje, setMensaje] = useState('');
const [ipServer, setIpServer] = React.useState('IP Server');
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
};

fetchIpAddress();

if (isServer) {
// Configura la aplicación como servidor
const server = UdpSocket.createSocket('udp4');

server.on('message', (data, rinfo) => {
setMensaje(data.toString())
server.send('¡Hola desde el servidor!', undefined, undefined, rinfo?.port, rinfo?.address, (error) => {
if (error) {
console.log('Error al enviar el mensaje:', error);
} else {
console.log('Mensaje enviado correctamente');
}
});
console.log('Mensaje recibido:', data.toString());
});

server.on('listening', () => {
console.log('Servidor escuchando en el puerto:', server.address().port);
setConnectionStatus(`Servidor escuchando en el puerto ${server.address().port}`);
});

server.bind(8888);

setSocket(server);
} else {
setConnectionStatus(`Servidor desconectado`);
// Configura la aplicación como cliente
const client = UdpSocket.createSocket('udp4');
client.bind(8887);
setSocket(client);
}

return () => {
socket && socket.close();
};
}, [isServer]);

const sendMessage = () => {
if (isServer) return;

const client = socket;

client.send('¡Hola desde el cliente!', undefined, undefined, 8888, ipServer, (error) => {
if (error) {
console.log('Error al enviar el mensaje:', error);
} else {
console.log('Mensaje enviado correctamente');
}
});
client.on('message', async (message, remoteInfo) => {
setMensaje(message.toString())
});
};

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{connectionStatus}</Text>
<Button
title={isServer ? 'Detener Servidor' : 'Iniciar Servidor'}
onPress={() => setIsServer(!isServer)}
/>
<Button title="Enviar Mensaje" onPress={sendMessage} disabled={isServer} />
<TextInput
onChangeText={setIpServer}
value={ipServer}
/>
<Text>Dirección IP: {ipAddress}</Text>
<Text>Mensaje recibído: {mensaje}</Text>
</View>
);
}

Ya sabes que si te ha gustado el artículo me puedes invitar a un café ;-)

Recuerda seguir la siguiente publicación para más artículos sobre React Native con Expo

--

--

Martin

Experto en integración de aplicaciones con más de 5 años de experiencia con IBM WMB y IIB y en la creación de flujos para Mule ESB con Anypoint Studio