React Native: Peer-to-Peer direct connection with react-native-udp

Martin
6 min readMay 31, 2023

If you are looking for how to connect two devices to exchange information with a React Native app, this is your article. To achieve this, we will use the react-native-udp library, which provides us with socket support that allows interconnection between two devices with the only requirement of being connected to the same network.

Let’s get started!

We are going to develop an app that will allow us to act as both a server and a client. For this purpose, it will display the device’s IP address on the screen to act as a server, and it will allow entering an IP address to act as a client.

The first thing we will do is install the react-native-udp and react-native-network-info libraries. The latter will allow us to retrieve the assigned IP address of the device.

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

We import the libraries, retrieve the IP address using the getIPV4Address() method, and create a hook to store the IP address for later display on the screen.

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();
})
}

Now, inside the useEffect, we will create the socket taking into account a hook variable called “isServer” that indicates whether the socket will act as a server or a client. We will also store this socket in a hook so that we can call it in the return of the useEffect to close the socket when the application is closed.

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();
};

Code explanation: We check if we have selected to act as a server with the “isServer” variable, which is initially set to FALSE, so the app will initially run as a client. If we act as a server, we create the socket in the “server” variable and call the “on” method, first with ‘message’ to receive messages from the client, and then with ‘listening’ which will be executed when the socket is listening. Finally, we bind the socket to a port using the “bind” method, in this case, port 8888, and save the socket in the “setSocket” hook.

If we want to act as a client, in this part of the code, we simply create the socket, bind it to a port, and save it using the “setSocket” hook.

As mentioned before, in the return of the useEffect, we close the socket to ensure it is closed when the app is closed.

At this point, we will add the method that allows sending a message from the client to the server.

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');
}

};

We check if we are acting as a server to do nothing in that case. If we are acting as a client, we retrieve the socket using the hook and call the “send” method, specifying the message we want to send, as well as the server’s port and IP. By checking the “error” variable, we can determine if the message has been sent successfully or not, although this does not guarantee that the server receives it.

Now we will construct the screen where we will have a Text component to display the connection status of the server, a Button to start or stop the server, a Button to send the message in client mode, a TextInput to enter the server’s IP, a Text component to display the device’s IP, and another Text component to display the received message when we are in server mode.

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>
);

We are ready to test!

Open two terminals and connect two devices to your computer. Run the command “expo run:android — device” in the terminals to execute the application, selecting the device on which you want it to run.

Launch the application on both devices and click the “Start Server” button on device A.

Now, we configure the IP address of device A in the TextInput of device B.

Click on the “Send Message” button on device B, and you will see the message arriving on device A.

We got it!

Here is the complete code with an improvement: the server will respond to the client after the client sends the message.

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>
);
}

You already know that if you liked the article you can buy me a coffee ;-)

Remember to follow the next post for more articles on React Native & 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