En este tutorial, vamos a configurar la comunicación utilizando el protocolo UDP en una aplicación React Native. La aplicación React Native puede actuar como Servidor o Cliente UDP. Usamos un ordenador con un script Python como compañero de comunicación.
Configuración de React Native
Primero, crea un proyecto React Native UDPTerminalApp
Instalar las bibliotecas udp y netinfo
npm install react-native-udp npm install react-native-network-info
Descripción del código de la aplicación
Importar
Para utilizar las bibliotecas, primero importamos su código.
import dgram from 'react-native-udp'
import UdpSocket from 'react-native-udp/lib/types/UdpSocket.js';
import { NetworkInfo } from 'react-native-network-info'
Componente principal
A continuación creamos el componente funcional que nos permitirá gestionar la comunicación del Servidor
const UDPTerminal = () => {
const [isServer, setIsServer] = useState(false);
const [connectionStatus, setConnectionStatus] = useState('');
const [socket, setSocket] = useState<UdpSocket>();
const [ipAddress, setIpAddress] = useState('');
const [ipServer, setIpServer] = React.useState('');
const [messageText, setMessageText] = useState("");
const [messageToSend, setMessageToSend] = useState("Hello from server");
const [receivedMessage, setReceivedMessage] = useState("");
- isServer es True si la aplicación se comporta como servidor y false si se comporta como cliente
- connectionStatus muestra el estado de la conexión
- socket contiene la variable socket para la comunicación
- ipAddress contiene la dirección IP del dispositivo Android en la red
- ipServer contiene la dirección IP introducida en la interfaz
- messageText contiene el texto escrito en la interfaz
- messageToSend contiene el mensaje que se enviará al cliente o al servidor
- receivedMessage contiene los mensajes recibidos del cliente o del servidor
Gestión de la comunicación UDP
El código utilizado para gestionar la comunicación UDP, ya sea en modo servidor o cliente, está completamente contenido en un hook useEffect.
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
console.log("ip adresses ; ", ip)
};
fetchIpAddress();
if (isServer) {
// Configure app as server
const server = dgram.createSocket('udp4');
server.on('message', (data, rinfo) => {
server.send(messageToSend, undefined, undefined, rinfo?.port, rinfo?.address, (error) => {
if (error) {
console.log('Error sending message:', error);
} else {
console.log('Message sent correctly');
}
});
console.log('Received message:', data.toString());
setReceivedMessage(receivedMessage => receivedMessage+data.toString()+"\n")
});
server.on('listening', () => {
console.log('Server listening on port:', server.address().port);
setConnectionStatus(`Server listening on port ${server.address().port}`);
});
try{
setReceivedMessage("");
server.bind(8888);
setSocket(server);
}catch(error){
console.log("error binding server",error);
}
} else {
setConnectionStatus(`Server disconnected`);
// Configure app as client
const client = dgram.createSocket('udp4');
try{
setReceivedMessage("");
client.bind(8887);
setSocket(client);
}catch(error){
console.log("error binding client",error);
}
}
return () => {
socket && socket.close();
};
}, [isServer, messageToSend, messageText]);
La función fetchIpAdress se utiliza para obtener la dirección IP del dispositivo.
En modo servidor
- creamos el socket dgram.createSocket
- cuando se recibe un mensaje: enviamos messageToSend al cliente
- A continuación, mostramos el mensaje recibido
- conectamos el servidor al puerto 8888
En modo cliente
- creamos el socket dgram.createSocket
- vinculamos al cliente al puerto 8887
En la siguiente función
- enviamos el mensaje messageToSend al servidor
- entonces esperamos la respuesta, que mostramos en receivedMessage
Función de envío de mensajes
También definimos una función que actualiza el mensaje enviado en modo Servidor y envía el mensaje en modo Cliente.
const sendMessage = () => {
setMessageToSend(messageText);
if (isServer) return;
const client = socket;
console.log("send message to "+ipServer)
client.send(messageToSend, undefined, undefined, 8888, ipServer, (error) => {
if (error) {
console.log('Error sending message:', error);
} else {
console.log('Message sent correctly');
}
});
client.on('message', async (message: { toString: () => string; }) => {
setReceivedMessage(receivedMessage+message.toString()+"\n")
});
};
Definición de la pantalla
Por último, la interfaz gráfica de la aplicación se define del siguiente modo:
- El título de la solicitud
- La dirección IP del dispositivo
- Un botón para seleccionar el modo cliente o servidor
- Una inserción para editar el mensaje que se va a enviar
- Un botón de envío
- Un inserto para especificar la dirección IP del servidor en modo cliente
- Un cuadro de texto para mostrar los mensajes recibidos
return (
<View style={styles.mainBody}>
<Text
style={styles.mainTitle}>
AC UDP Terminal
</Text>
<ScrollView>
<View style={styles.deviceItem}>
<View>
<Text style={styles.deviceName}>{ipAddress}</Text>
<Text style={styles.deviceInfo}>{connectionStatus}</Text>
</View>
<TouchableOpacity
onPress={() => setIsServer(!isServer)}
style={styles.deviceButton}>
<Text
style={styles.buttonText}>
{isServer ? 'Server' : 'Client'}
</Text>
</TouchableOpacity>
</View>
<View
style={styles.inputBar}>
<TextInput
style={styles.textInput}
placeholder="Enter a message"
value={messageText}
onChangeText={(text) => setMessageText(text)
}
/>
<TouchableOpacity
onPress={() => sendMessage()
}
style={[styles.sendButton]}>
<Text
style={styles.buttonText}>
SEND
</Text>
</TouchableOpacity>
</View>
<TextInput
placeholder="Server IP"
onChangeText={setIpServer}
value={ipServer}
/>
<Text>Received Message:</Text>
<TextInput
editable = {false}
multiline
numberOfLines={20}
maxLength={300}
style={styles.textOutput} >
{receivedMessage}
</TextInput>
</ScrollView>
</View>
);
N.B.: el estilo de la interfaz se define en la sección
Cliente UDP React-Native – Servidor UDP Python
En este ejemplo, configuramos la aplicación como cliente y el ordenador como servidor. Enviaremos un mensaje desde la aplicación al servidor, que devolverá una respuesta al cliente.
Código del servidor UDP Python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
localIP = "0.0.0.0" #"127.0.0.1"
localPort = 8888
bufferSize = 1024
msgFromServer = "Hello UDP Client"
bytesToSend = str.encode(msgFromServer)
# Create a datagram socket
UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# Bind to address and ip
UDPServerSocket.bind((localIP, localPort))
print("UDP server up and listening on port {}".format(localPort))
# Listen for incoming datagrams
while(True):
data,addr = UDPServerSocket.recvfrom(bufferSize)
print("{} > {}".format(addr,data))
# Sending a reply to client
UDPServerSocket.sendto(bytesToSend, addr)
N.B.: Para escuchar en todas las direcciones locales utilizamos 0.0.0.0. Si desea probar la comunicación entre el cliente y el servidor en el mismo ordenador, puede utilizar la dirección 127.0.0.1.
En la aplicación, en la casilla ServerIp, introduzca la dirección IP del ordenador (aquí: 192.168.1.70). A continuación, modifique el mensaje que desea enviar y pulse el botón ENVIAR.
Terminal Python
UDP server up and listening on port 8888
('192.168.1.5', 8887) > b'Hello server'
('192.168.1.5', 8887) > b'Hello server'
En el terminal Python, recibimos el mensaje «Hola servidor» enviado desde la aplicación. El servidor UDP devuelve el mensaje «Hello UDP Client».
Servidor UDP React-Native – Cliente UDP Python
Aquí configuramos la aplicación como servidor y el ordenador como cliente. En el código Python, introducimos la dirección IP del servidor.
Cliente UDP Python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import time
HOST = "192.168.1.5" #Server IP #"127.0.0.1" local address
PORT = 8888
bufferSize = 1024
counter=0
while True:
# Create a UDP socket at client side
with socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) as client:
# Send to server using created UDP socket
client.sendto(str.encode("client counter : "+str(counter)), (HOST,PORT))
data,addr= client.recvfrom(bufferSize)
msg = "{} > {}".format(addr,data)
print(msg)
counter+=1
time.sleep(1)
Terminal Python
('192.168.1.5', 8888) > b'Hello client '
('192.168.1.5', 8888) > b'Hello client '
Código completo de la aplicación
App.tsx
/**
* npm install react-native-udp
* npm install react-native-network-info
*
*/
import React, {useState, useEffect} from 'react';
import {
View,
ScrollView,
Text,
TextInput,
PermissionsAndroid,
Platform,
TouchableOpacity } from 'react-native';
import dgram from 'react-native-udp'
import UdpSocket from 'react-native-udp/lib/types/UdpSocket.js';
import { NetworkInfo } from 'react-native-network-info'
import {styles} from "./src/styles/styles.jsx"
const UDPTerminal = () => {
const [isServer, setIsServer] = useState(false);
const [connectionStatus, setConnectionStatus] = useState('');
const [socket, setSocket] = useState<UdpSocket>();
const [ipAddress, setIpAddress] = useState('');
const [ipServer, setIpServer] = React.useState('');
const [messageText, setMessageText] = useState("");
const [messageToSend, setMessageToSend] = useState("Hello from server");
const [receivedMessage, setReceivedMessage] = useState("");
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
console.log("ip adresses ; ", ip)
};
fetchIpAddress();
if (isServer) {
// Configure app as server
const server = dgram.createSocket('udp4');
server.on('message', (data, rinfo) => {
server.send(messageToSend, undefined, undefined, rinfo?.port, rinfo?.address, (error) => {
if (error) {
console.log('Error sending message:', error);
} else {
console.log('Message sent correctly');
}
});
console.log('Received message:', data.toString());
if(receivedMessage.length>300){
setReceivedMessage("");
}
setReceivedMessage(receivedMessage => receivedMessage+data.toString()+"\n")
});
server.on('listening', () => {
console.log('Server listening on port:', server.address().port);
setConnectionStatus(`Server listening on port ${server.address().port}`);
});
try{
setReceivedMessage("");
server.bind(8888);
setSocket(server);
}catch(error){
console.log("error binding server",error);
}
} else {
setConnectionStatus(`Server disconnected`);
// Configure app as client
const client = dgram.createSocket('udp4');
try{
setReceivedMessage("");
client.bind(8887);
setSocket(client);
}catch(error){
console.log("error binding client",error);
}
}
return () => {
socket && socket.close();
};
}, [isServer, messageToSend, messageText]);
const sendMessage = () => {
setMessageToSend(messageText);
if (isServer) return;
const client = socket;
console.log("send message to "+ipServer)
client.send(messageToSend, undefined, undefined, 8888, ipServer, (error) => {
if (error) {
console.log('Error sending message:', error);
} else {
console.log('Message sent correctly');
}
});
client.on('message', async (message: { toString: () => string; }) => {
if(receivedMessage.length>300){
setReceivedMessage("");
}
setReceivedMessage(receivedMessage+message.toString()+"\n")
});
};
return (
<View style={styles.mainBody}>
<Text
style={styles.mainTitle}>
AC UDP Terminal
</Text>
<ScrollView>
<View style={styles.deviceItem}>
<View>
<Text style={styles.deviceName}>{ipAddress}</Text>
<Text style={styles.deviceInfo}>{connectionStatus}</Text>
</View>
<TouchableOpacity
onPress={() => setIsServer(!isServer)}
style={styles.deviceButton}>
<Text
style={styles.buttonText}>
{isServer ? 'Server' : 'Client'}
</Text>
</TouchableOpacity>
</View>
<View
style={styles.inputBar}>
<TextInput
style={styles.textInput}
placeholder="Enter a message"
value={messageText}
onChangeText={(text) => setMessageText(text)
}
/>
<TouchableOpacity
onPress={() => sendMessage()
}
style={[styles.sendButton]}>
<Text
style={styles.buttonText}>
SEND
</Text>
</TouchableOpacity>
</View>
<TextInput
placeholder="Server IP"
onChangeText={setIpServer}
value={ipServer}
/>
<Text>Received Message:</Text>
<TextInput
editable = {false}
multiline
numberOfLines={20}
maxLength={300}
style={styles.textOutput} >
{receivedMessage}
</TextInput>
</ScrollView>
</View>
);
}
export default UDPTerminal;
style.jsx
/* ./src/styles/styles.jsx */
import {StyleSheet, Dimensions} from 'react-native';
/**
* links to palette
* https://colorhunt.co/palette/161616346751c84b31ecdbba
*
*/
//parameters
let BACKGROUND_COLOR = "#161616"; //191A19
let BUTTON_COLOR = "#346751"; //1E5128
let ERROR_COLOR = "#C84B31"; //4E9F3D
let TEXT_COLOR = "#ECDBBA"; //D8E9A8
const windowHeight = Dimensions.get('window').height;
export const styles = StyleSheet.create({
mainBody: {
flex: 1,
justifyContent: 'center',
height: windowHeight,
color:TEXT_COLOR,
backgroundColor: BACKGROUND_COLOR,
},
mainTitle:{
color: TEXT_COLOR,
fontSize: 30,
textAlign: 'center',
borderBottomWidth: 2,
borderBottomColor: ERROR_COLOR,
},
scanButton: {
backgroundColor: BUTTON_COLOR,
paddingBottom: 10,
paddingTop: 10,
borderRadius: 10,
margin: 2,
marginBottom: 10,
marginTop: 10,
paddingHorizontal: 20,
fontWeight: 'bold',
fontSize: 12,
},
buttonText: {
color: TEXT_COLOR,
fontWeight: 'bold',
fontSize: 12,
textAlign: 'center',
},
noDevicesText: {
color: TEXT_COLOR,
textAlign: 'center',
marginTop: 10,
fontStyle: 'italic',
},
deviceItem: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 2,
},
deviceName: {
color: TEXT_COLOR,
fontSize: 14,
fontWeight: 'bold',
},
deviceInfo: {
color: TEXT_COLOR,
fontSize: 10,
},
deviceButton: {
backgroundColor: BUTTON_COLOR,
padding: 10,
borderRadius: 10,
margin: 2,
paddingHorizontal: 20,
fontWeight: 'bold',
fontSize: 12,
},
inputBar:{
flexDirection: 'row',
justifyContent: 'space-between',
margin: 5,
},
textInput:{
backgroundColor: '#888888',
margin: 2,
borderRadius: 15,
flex:3,
},
sendButton: {
backgroundColor: BUTTON_COLOR,
padding: 15,
borderRadius: 15,
margin: 2,
paddingHorizontal: 20,
},
textOutput:{
backgroundColor: '#333333',
margin: 10,
borderRadius: 2,
borderWidth: 1,
borderColor: '#EEEEEE',
textAlignVertical: 'top',
}
});
Aplicaciones
- Controla tu proyecto Arduino, ESP32 o Raspberry Pi con una aplicación

