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