Fundamentos de dart:io para sistemas nativos

Dominio de dart:io para desarrollo nativo

dart:io es la librería fundamental para realizar operaciones de entrada y salida (I/O) en entornos nativos. Es importante entender que esta librería no está disponible en la web (ni en DartPad) porque el modelo de seguridad de los navegadores prohíbe el acceso directo a sockets de bajo nivel o al sistema de archivos local. Su diseño está orientado a la arquitectura de la Dart VM, permitiendo la comunicación con el sistema operativo a través de subsistemas de archivos, red (Socket, HttpServer), procesos y los flujos estándar (stdin, stdout, stderr).

La lógica interna de dart:io está construida para proteger el Event Loop de Dart. Dado que las operaciones de disco o de red son órdenes de magnitud más lentas que la ejecución de código en la CPU, la librería está diseñada para ser mayoritariamente asíncrona mediante el uso de Future y Stream. Esto permite que el hilo principal siga procesando otros eventos (como otras peticiones HTTP en un servidor) mientras el sistema operativo gestiona la I/O en segundo plano.

Debes usar esta librería cuando necesites manipular el sistema de archivos, gestionar conexiones TCP/UDP, levantar un servidor HTTP o leer la entrada de un usuario por la terminal.

Si intentas compilar código que dependa de dart:io para un target web, el compilador fallará al no encontrar las dependencias de la VM. Por otro lado, si utilizas los métodos con sufijo Sync (síncronos) en un entorno de servidor con alta concurrencia, bloquearás el Event Loop, lo que detendrá toda la ejecución de la aplicación hasta que la operación de I/O termine, degradando severamente el rendimiento.

import 'dart:io';

Future<void> main() async {
  // 1. Información del sistema a través de la clase Platform
  print('Sistema operativo: ${Platform.operatingSystem}');
  print('Versión de la plataforma: ${Platform.version}');

  final nombreArchivo = 'registro_sistema.log';
  final archivo = File(nombreArchivo);

  try {
    // 2. Operación de escritura asíncrona para no bloquear el Event Loop
    final contenido = 'Evento de ejecución: ${DateTime.now()}\n';
    await archivo.writeAsString(contenido, mode: FileMode.append);
    print('Log escrito en: ${archivo.path}');

    // 3. Lectura asíncrona del archivo
    final contenidoLeido = await archivo.readAsString();
    print('Contenido del log:\n$contenidoLeido');
  } catch (e) {
    // Uso de stderr para separar errores de la salida estándar
    stderr.writeln('Error crítico al acceder al archivo: $e');
  }

  // 4. Interacción con el entorno y entrada de usuario
  final variableEnv = Platform.environment['USER'] ?? 'Desconocido';
  print('Usuario actual: $variableEnv');

  print('Escribe un comando para finalizar (o presiona Enter):');
  // Nota: readLineSync() es síncrono; en aplicaciones de alto rendimiento 
  // se preferiría trabajar con Streams de stdin.
  final entrada = stdin.readLineSync();
  print('Comando recibido: ${entrada ?? 'vacio'}');
}

En el ejemplo anterior, utilizamos la clase Platform para obtener metadatos del sistema operativo sin necesidad de llamadas externas complejas. Al trabajar con File, optamos por usar writeAsString con el prefijo await. Esto es crucial: al usar la versión asíncrona, la VM suspende la ejecución de main pero libera el Event Loop para que otros procesos (si los hubiera) continúen. Si hubiéramos usado writeAsStringSync, el programa se quedaría “congelado” en esa línea hasta que el disco responda.

La gestión de errores se canaliza a través de stderr.writeln. En aplicaciones CLI profesionales, es una práctica estándar enviar logs de error a la salida de error estándar (stderr) y los resultados útiles a la salida estándar (stdout), permitiendo que otros procesos redirijan los errores de forma independiente.

Finalmente, notarás que para la lectura de la consola usamos stdin.readLineSync(). Aunque es la forma más sencilla de capturar entrada en scripts rápidos, en un servidor de alta carga nunca debes usar métodos Sync, ya que detendrías todas las peticiones entrantes mientras esperas a que el usuario presione una tecla.

El error frecuente
Un error común al escalar aplicaciones de Dart es el uso inadvertido de APIs síncronas dentro de contextos asíncronos.

// ERROR: Bloqueo del Event Loop en un servidor
void handleRequest(HttpRequest request) async {
  final archivoPesado = File('archivo_gigante.bin');
  
  // ESTO ES UN ERROR: Bloquea todo el servidor para todos los usuarios
  final datos = await archivoPesado.readAsBytesSync(); 
  
  request.response.write('Leído ${datos.length} bytes');
  await request.response.close();
}

En este escenario, si el archivo es grande, la ejecución de readAsBytesSync() detiene el hilo principal. Si tienes 100 usuarios intentando entrar al servidor al mismo tiempo, todos verán una latencia masiva porque el Event Loop no puede procesar los nuevos paquetes de red hasta que la lectura del disco termine. Usa siempre la versión asíncrona (readAsBytes()) para delegar la espera al sistema operativo.

75

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio