domingo, 22 de marzo de 2026

Una comparativa entre C# y Beef

Como vimos en una entrega anterior, Beef es un lenguaje de programación similar a C#. Con la peculiaridad que al instalarlo nos provee de su propio IDE.

Observemos un ejemplo. El clásico programa "Hola, mundo" escrito en Beef(lang):

HolaMundo.bf

using System;

class Program
{
    public static void Main()
    {
        Console.WriteLine("Hola, mundo en Beef!");
    }
}

Cualquier despistado diría que está escrito en C#, pero no nos confiemos en lo que vemos. Ahora observemos el mismo programa escrito en C#.

Program.cs

using System;

class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Hola, mundo en C#!");
    }
}

La diferencia entre un programa y el otro es casi nula. Para hacer una comparativa entre estos dos lenguajes nos enfocaremos en:

  1. Paradigma que usan.
  2. Características más importantes.
  3. Tipos de datos que manejan.
  4. Sus estructuras de control.
  5. Funciones y procedimientos.
  6. Estructuras de datos.
  7. Manejo de errores.

Comparativa entre C# y Beef

1. Paradigma.

  • Ambos lenguajes son multiparadigma. 
  • Lo que sugiere que puedes crear programas con paradigma estructurado, funcional y orientado a objetos.

2. Principales características.

De C#:

  • Orientado a Objetos (POO). 
  • Seguridad de Tipos (Strongly Typed).
  • Multiplataforma.
  • Gestión Automática de Memoria.
  • Orientado a Componentes.
  • Integración con .NET. 
  • Sintaxis Moderna y Limpia. 
  • Interoperabilidad. 
  • Programación Asíncrona. 
  • LINQ (Language Integrated Query).

De Beef:

  • Sintaxis y Estructura. 
  • Alto Rendimiento. 
  • Productividad del Desarrollador. 
  • Gestión de Memoria. 
  • Características Modernas. 
  • Seguridad en Depuración. 
  • Compilación e IDE.

3. Tipos de datos.

Observemos una tabla comparativa.

Categoría C# Beef
Booleano bool bool
Enteros con signo sbyte, short, int, long, nint int8, int16, int32, int64, int
Enteros sin signo byte, ushort, uint, ulong, nuint uint8, uint16, uint32, uint64, uint
Punto flotante float, double, decimal float, double
Caracteres char char8, char16, char32
Cadenas string No hay tipo nativo, se usan arrays de char*
Referencia / Objeto object, dynamic, delegate structs (colecciones definidas por el usuario)
Especiales void, nullable types (ej. int?) void

C# es más rico que Beef en tipos de referencia y abstracciones de alto nivel.

Categoría C# Beef
Booleano
// C#
bool flag = true;
// Beef
bool flag = true;
Enteros con signo
// C#
int x = -42;
long y = 123456789;
// Beef
int32 x = -42;
int64 y = 123456789;
Enteros sin signo
// C#
uint a = 42;
byte b = 255;
// Beef
uint32 a = 42;
uint8 b = 255;
Punto flotante
// C#
float pi = 3.14f;
double e = 2.718;
decimal money = 99.99m;
// Beef
float pi = 3.14f;
double e = 2.718;
Caracteres
// C#
char letter = 'A';
// Beef
char16 letter = 'A';
Cadenas
// C#
string name = "Carlos";
// Beef
char8* name = "Carlos";
Referencia / Objeto
// C#
object obj = new object();
delegate void MyDelegate();
// Beef
struct Point { int x; int y; }
Point p = .(10, 20);
Especiales
// C#
void DoSomething() { }
int? maybe = null;
// Beef
void DoSomething() { }

4. Estructuras de control.

Observemos la siguiente tabla comparativa entre C# y Beef.

Estructura C# Beef
Condicional if/else
// C#
if (x > 10)
{
    Console.WriteLine("Mayor a 10");
}
else
{
    Console.WriteLine("Menor o igual a 10");
}
// Beef
if (x > 10)
{
    Console.WriteLine("Mayor a 10");
}
else
{
    Console.WriteLine("Menor o igual a 10");
}
switch / match
// C#
switch (day)
{
    case 1: Console.WriteLine("Lunes"); break;
    case 2: Console.WriteLine("Martes"); break;
    default: Console.WriteLine("Otro día"); break;
}
// Beef
switch (day)
{
    case 1: Console.WriteLine("Lunes"); break;
    case 2: Console.WriteLine("Martes"); break;
    default: Console.WriteLine("Otro día"); break;
}
Bucle for
// C#
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}
// Beef
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}
Bucle while
// C#
while (x < 10)
{
    x++;
}
// Beef
while (x < 10)
{
    x++;
}
Bucle do-while
// C#
do
{
    x++;
} while (x < 10);
// Beef
do
{
    x++;
} while (x < 10);
foreach
// C#
foreach (var item in list)
{
    Console.WriteLine(item);
}
// Beef
for (var item in list)
{
    Console.WriteLine(item);
}
break / continue
// C#
for (int i = 0; i < 10; i++)
{
    if (i == 5) break;
    if (i % 2 == 0) continue;
    Console.WriteLine(i);
}
// Beef
for (int i = 0; i < 10; i++)
{
    if (i == 5) break;
    if (i % 2 == 0) continue;
    Console.WriteLine(i);
}

La sintaxis de estructuras de control en Beef es prácticamente idéntica a la de C#, lo que facilita la transición entre ambos lenguajes.

La diferencia más notable está en el uso de foreach : en C# existe como palabra reservada, mientras que en Beef se usa un for con iteración directa sobre colecciones.

Ambos lenguajes soportan break y continue dentro de bucles, así como switch con casos y default.

5. Funciones y procedimientos.

Concepto C# Beef
Función simple
// C#
int Sumar(int a, int b)
{
    return a + b;
}
// Beef
int Sumar(int a, int b)
{
    return a + b;
}
Procedimiento (sin retorno)
// C#
void MostrarMensaje(string msg)
{
    Console.WriteLine(msg);
}
// Beef
void MostrarMensaje(String msg)
{
    Console.WriteLine(msg);
}
Parámetros por referencia
// C#
void Incrementar(ref int x)
{
    x++;
}
// Beef
void Incrementar(ref int x)
{
    x++;
}
Parámetros opcionales
// C#
void Saludar(string nombre = "Invitado")
{
    Console.WriteLine($"Hola {nombre}");
}
// Beef
void Saludar(String nombre = "Invitado")
{
    Console.WriteLine("Hola " + nombre);
}
Funciones anónimas / lambdas
// C#
Func cuadrado = x => x * x;
// Beef
function int(int) cuadrado = (x) => x * x;
Funciones dentro de estructuras/clases
// C#
class Persona
{
    public void Hablar()
    {
        Console.WriteLine("Hola!");
    }
}
// Beef
struct Persona
{
    public void Hablar()
    {
        Console.WriteLine("Hola!");
    }
}

6. Estructuras de datos.

Observemos la siguiente tabla comparativa.

Estructura C# Beef
Arreglos
// C#
int[] numeros = {1, 2, 3};
// Beef
int[] numeros = .(1, 2, 3);
Listas dinámicas
// C#
List lista = new List();
lista.Add(10);
// Beef
List lista = new List();
lista.Add(10);
Diccionarios / Mapas
// C#
Dictionary edades = new Dictionary();
edades["Ana"] = 25;
// Beef
Dictionary edades = new Dictionary();
edades["Ana"] = 25;
Conjuntos
// C#
HashSet conjunto = new HashSet();
conjunto.Add(5);
// Beef
HashSet conjunto = new HashSet();
conjunto.Add(5);
Pilas (Stack)
// C#
Stack pila = new Stack();
pila.Push(1);
// Beef
Stack pila = new Stack();
pila.Push(1);
Colas (Queue)
// C#
Queue cola = new Queue();
cola.Enqueue(2);
// Beef
Queue cola = new Queue();
cola.Enqueue(2);
Estructuras definidas por el usuario
// C#
struct Punto
{
    public int X;
    public int Y;
}
// Beef
struct Punto
{
    public int X;
    public int Y;
}

7. Manejo de errores.

Observemos la siguiente tabla comparativa.

Concepto C# Beef
Bloque básico try-catch
// C#
try
{
    int x = int.Parse("abc");
}
catch (FormatException ex)
{
    Console.WriteLine("Error de formato: " + ex.Message);
}
// Beef
try
{
    int x = int.Parse("abc");
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
try-catch-finally
// C#
try
{
    var file = File.Open("data.txt", FileMode.Open);
}
catch (IOException ex)
{
    Console.WriteLine("Error de E/S: " + ex.Message);
}
finally
{
    Console.WriteLine("Liberando recursos...");
}
// Beef
try
{
    var file = File.Open("data.txt", FileMode.Open);
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
finally
{
    Console.WriteLine("Liberando recursos...");
}
Lanzar excepciones
// C#
throw new InvalidOperationException("Operación inválida");
// Beef
throw new Exception("Operación inválida");
Excepciones personalizadas
// C#
class MiExcepcion : Exception
{
    public MiExcepcion(string msg) : base(msg) { }
}
// Beef
struct MiExcepcion : Exception
{
    public this(String msg) : base(msg) { }
}

Ejemplo. Crear un procedimiento donde se calcule el nuevo salario de una persona. Si el empleado es casado, tiene un salario menor a $30000.00 se le de un aumento del 0.05, en caso contrario solo será del 0.01.

En Beef:

        static void CalculoSalario()
	{
	        Console.WriteLine("\t [ Calculando nuevo salario en Beef  ]");
	        int edad = 0;
		String nombre = scope String();
		String edo_civil = scope String();
		double salario_bruto = 0;
		double salario_neto = 0;

		Console.WriteLine("Introduce tu nombre:");
		Console.ReadLine(nombre); // escribe en el buffer 'nombre'

		Console.WriteLine("Introduce tu edad:");
		String edadStr = scope String();
		Console.ReadLine(edadStr);
		edad = int.Parse(edadStr);

		Console.WriteLine("Introduce tu estado civil:");
		Console.ReadLine(edo_civil);

		Console.WriteLine("Introduce tu salario bruto:");
		String salarioStr = scope String();
		Console.ReadLine(salarioStr);
		salario_bruto = double.Parse(salarioStr);

		if (salario_bruto < 30000)
		{
		    if (edo_civil == "casado")
		    {
		        salario_bruto += salario_bruto * 0.05;
		    }
		    else
		    {
		        salario_bruto += salario_bruto * 0.01;
		    }

		    if (edad > 40)
		    {
		        salario_bruto += salario_bruto * 0.05;
		    }
		    else
		    {
		        salario_bruto += salario_bruto * 0.01;
		    }
		}

		salario_neto = salario_bruto;
		Console.WriteLine("Salario neto: {0}", salario_neto);
	}

En C#:

public static void CalculoSalario()
    {
        Console.WriteLine("\t [ Calculando nuevo salario en C# ]");
        int edad = 0;
        string nombre = "";
        string edo_civil = "";
        double salario_bruto = 0;
        double salario_neto = 0;

        Console.WriteLine("Introduce tu nombre:");
        nombre = Console.ReadLine();

        Console.WriteLine("Introduce tu edad:");
        string edadStr = Console.ReadLine();
        edad = int.Parse(edadStr);

        Console.WriteLine("Introduce tu estado civil:");
        edo_civil = Console.ReadLine();

        Console.WriteLine("Introduce tu salario bruto:");
        string salarioStr = Console.ReadLine();
        salario_bruto = double.Parse(salarioStr);

        if (salario_bruto < 30000)
        {
            if (edo_civil == "casado")
            {
                salario_bruto += salario_bruto * 0.05;
            }
            else
            {
                salario_bruto += salario_bruto * 0.01;
            }

            if (edad > 40)
            {
                salario_bruto += salario_bruto * 0.05;
            }
            else
            {
                salario_bruto += salario_bruto * 0.01;
            }
        }

        salario_neto = salario_bruto;
        Console.WriteLine("Salario neto: {0}", salario_neto);
      
    }

Salida:

         [ Calculando nuevo salario en C# ]
Introduce tu nombre:
Thomas Muller
Introduce tu edad:
34
Introduce tu estado civil:
casado
Introduce tu salario bruto:
29000
Salario neto: 30754.5

Como hemos visto, Beef es un lenguaje de programación que toma inspiración del lenguaje C#. Tanto su sitaxis como tipos de datos. Paradigma y esencia.

Sin embargo, C# es mucho más rico, más completo. Si tuvieras que elegir un lenguaje para crear un nuevo proyecto, C# sería mejor opción, pero Beef no se queda atrás, apenas comienza.

Continuaremos con esta serie de lenguajes de programación.

Enlaces:

https://www.beeflang.org/
https://dotnet.microsoft.com/es-es/languages/csharp
https://codemonkeyjunior.blogspot.com/2026/02/beef-y-c3-dos-lenguajes-de-programacion.html



lunes, 16 de marzo de 2026

WebSockets con Mojolicious

En esta ocasión veremos un ejemplo sencillo de WebSockets con Mojolicious.

Como vimos en entregas pasadas:

Mojolicious es un framework para Perl que es similar a Dancer2, pero más completo. Además nos permite trabajar con WebSockets, que es el tema que vamos a ver. Como vimos en una entrega anterior, los WebSockets son un protocolo que permite comunicación bidireccional en tiempo real entre cliente y servidor, a diferencia de HTTP que funciona en modo solicitud-respuesta.

Observemos el siguiente código.

web_socket.pl

use Mojolicious::Lite -signatures;

# Renderiza la plantilla "index.html.ep" desde la seccion 
get '/' => sub ($c) {
  $c->render(template => 'index');
};

# Servicio WebSocket usado por la plantilla para extraer el título del sitio web
websocket '/title' => sub ($c) {
  $c->on(message => sub ($c, $msg) {
    my $title = $c->ua->get($msg)->result->dom->at('title')->text;
    $c->send($title);
  });
};

app->start;
__DATA__

@@ index.html.ep
% my $url = url_for 'title';
<script>
  const ws = new WebSocket('<%= $url->to_abs %>');
  ws.onmessage = function (event) { document.body.innerHTML += event.data };
  ws.onopen    = function (event) { ws.send('https://www.wikipedia.org') };
</script>

En este programa Perl hacemos uso del Framework Mojolicious. Donde se mostrará una sencilla página HTML que obtendrá el nombre del sitio web definido dentro de la función ws.send("web_page"). En este caso obtendremos el título de la página de Wikipedia.

Lo ejecutaremos de la siguiente manera:

$ perl web_socket.pl daemon -l http://*:8080

Abrimos un navegador en la ruta: http://127.0.0.1:8080/

También podemos ejecutar de este modo:

$ morbo web_socket.pl

En este caso, abrimos un navegador en la ruta: http://127.0.0.1:3000/

Salida:

[2026-03-16 15:55:15.56279] [27716] [info] Listening at "http://*:8080"
Web application available at http://127.0.0.1:8080
[2026-03-16 15:55:50.81445] [27716] [trace] [1ODKO9_D-nZZ] GET "/"
[2026-03-16 15:55:50.81945] [27716] [trace] [1ODKO9_D-nZZ] Routing to a callback
[2026-03-16 15:55:50.82339] [27716] [trace] [1ODKO9_D-nZZ] Rendering template "index.html.ep" from DATA section
[2026-03-16 15:55:50.82998] [27716] [trace] [1ODKO9_D-nZZ] 200 OK (0.015422s, 64.842/s)
[2026-03-16 15:55:51.40533] [27716] [trace] [ThnOL6K5Pt-k] GET "/title"
[2026-03-16 15:55:51.40978] [27716] [trace] [ThnOL6K5Pt-k] Routing to a callback
[2026-03-16 15:55:51.41264] [27716] [trace] [ThnOL6K5Pt-k] 101 Switching Protocols (0.00707s, 141.443/s)

En la página se verá el nombre de "Wikipedia".

Hagamos algo más complicado. Crear una aplicación que reciba peticiones y responda dinámicamente. Esto con ayuda de los WebSockets.

web_sockets.pl

use Mojolicious::Lite -signatures;

# Ruta normal para probar
get '/' => sub ($c) {
  $c->render(template => 'index');
};

# Endpoint WebSocket
websocket '/echo' => sub ($c) {
  $c->on(message => sub ($c, $msg) {
    # Responde con el mismo mensaje recibido
    $c->send("Servidor recibió: $msg");
  });
};

app->start;

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
  <head><title>Codemonkey Junior WebSocket con Mojolicious</title></head>
  <body>
    <h1>WebSocket con Mojolicious</h1>
    <script>
      // Conectar al servidor WebSocket
      const ws = new WebSocket('<%= url_for("echo")->to_abs %>');
      
      ws.onopen = function() {
        ws.send("¡Hola desde el cliente!");
      };
      
      ws.onmessage = function(event) {
        document.body.innerHTML += "<p>" + event.data + "</p>";
      };
    </script>
  </body>
</html>

Ejecutamos:

$ morbo web_sockets.pl

Abrimos un navegador en la ruta: http://127.0.0.1:3000/

Salida:

Web application available at http://127.0.0.1:3000
[2026-03-16 16:16:20.73121] [-15804] [trace] [2NKo74iWOXU_] GET "/"
[2026-03-16 16:16:20.73267] [-15804] [trace] [2NKo74iWOXU_] Routing to a callback
[2026-03-16 16:16:20.73900] [-15804] [trace] [2NKo74iWOXU_] Rendering template "index.html.ep" from DATA section
[2026-03-16 16:16:20.74582] [-15804] [trace] [2NKo74iWOXU_] 200 OK (0.014579s, 68.592/s)
[2026-03-16 16:16:21.61504] [-15804] [trace] [u4UBFpSvNxLo] GET "/echo"
[2026-03-16 16:16:21.61669] [-15804] [trace] [u4UBFpSvNxLo] Routing to a callback
[2026-03-16 16:16:21.61922] [-15804] [trace] [u4UBFpSvNxLo] 101 Switching Protocols (0.004105s, 243.605/s)

Se abrirá una página web con el mensaje:

WebSocket con Mojolicious
Servidor recibió: ¡Hola desde el cliente!

Mojolicious permite construir aplicaciones más complejas: broadcasting a múltiples clientes, integración con Minion para tareas en segundo plano, o extracción de datos en tiempo real desde APIs externas.

Estos son solo ejemplos básicos de uso de WebSockets y Mojolicious.

En próximas entregas veremos esto más a fondo.

Enlaces:

https://mojolicious.org/
https://mojolicious.io/

NLog: una alternativa para Serilog en .Net

Como vimos en la entrega anterior, el proyecto NLog es una alternativa a Serilog.

"NLog es una plataforma de registro flexible y gratuita para diversas plataformas .NET, incluyendo .NET Standard. NLog facilita la escritura en múltiples destinos (consola, archivo, base de datos, etc.) y permite modificar la configuración de registro sobre la marcha".

Iniciando con NLog

1. Creamos un proyecto con dotnet:

$ dotnet new console -n DemoNLog
$ cd DemoNLog

2. Agregamos el paquete NLog en el archivo ``DemoNLog.csproj``:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

<ItemGroup>
   <PackageReference Include="NLog" Version="6.1.1" />
   <None Update="NLog.config">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </None>
</ItemGroup>

</Project>

En este archivo XML agregamos el paquete NLog y especificamos la versión compatible a .Net 10.

También lo podemos hacer desde la terminal:

$ dotnet add package NLog --version 6.1.1

3. Creamos y configuramos el archivo ``NLog.config``:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!-- Definir los targets -->
  <targets>
    <!-- Log en consola -->
    <target xsi:type="Console" name="console" layout="${longdate} | ${level:uppercase=true} | ${message}" />

    <!-- Log en archivo -->
    <target xsi:type="File" name="file" fileName="C:\\Users\\HP\\Documents\\pruebasCSharp\\pruebasNLog\\DemoNLog\\logfile.txt"
            layout="${longdate} | ${level:uppercase=true} | ${message} ${exception}" />
  </targets>

  <!-- Definir las reglas -->
  <rules>
    <!-- Todos los niveles a consola -->
    <logger name="*" minlevel="Debug" writeTo="console" />
    <!-- Solo Info y superior a archivo -->
    <logger name="*" minlevel="Info" writeTo="file" />
  </rules>
</nlog>

En este archivo configuramos el archivo y la ruta de la salida. Además los niveles de loggers. Similar a lo que se hace en Java (log4j).

4. Modificamos el programa principal ``Program.cs``

using NLog;

class Program
{
    private static readonly Logger logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {
        LogManager.Setup().LoadConfigurationFromFile();
        logger.Info("Aplicación iniciada");
        logger.Debug("Este es un mensaje de depuración");
        logger.Error("Ocurrió un error simulado");

        LogManager.Shutdown(); 
    }
}

En el programa importamos el paquete NLog y cargamos la configuración del archivo NLog.config. El programa solo mostrará algunos mensajes con los niveles Info, Debug y Error.

5. Construimos y ejecutamos la aplicación:

$ dotnet build
$ dotnet run

Salida:

2026-03-16 12:10:36.3512 | INFO | Aplicación iniciada
2026-03-16 12:10:36.4258 | DEBUG | Este es un mensaje de depuración
2026-03-16 12:10:36.4258 | ERROR | Ocurrió un error simulado

Como se puede observar es similar a lo que se hizo con el paquete Serilog. Aunque tiene algunos detalles como la configuración del archivo XML.

¡Hemos creado nuestro primer programa usando NLog!

Serilog es recomendable para proyectos modernos como microservicios y aplicaciones Cloud.

NLog está más enfocado en proyectos legados, aunque es una aletrnativa a Serilog.

Continuaremos con esta serie sobre C# en próximas entregas.

Enlaces:

https://nlog-project.org/
https://www.nuget.org/

sábado, 14 de marzo de 2026

Serilog: registrando eventos en .Net

Para quienes llevan tiempo programando saben que llevar un registro de eventos de nuestras aplicaciones (web, principalmente) es muy importante para dar soporte a ciertas incidencias que pudieran presentarse.

Los loggers son registros que contienen la fecha y hora de ciertos eventos. Estos son importantes cuando queremos saber qué acción disparó el usuario y, en ciertos casos, el resultado que arrojó.

El resultado puede ser satisfactorio o pueden presentarse errores que debemos atender.

.NET nos permite usar librerías como Serilog para realizar estas actividades. La documentación oficial nos dice:

"Serilog proporciona registro de diagnóstico en archivos, la consola y otros lugares".

Además promete ser fácil de configurar y usar.

"Es fácil de configurar, tiene una API limpia y es portable entre las plataformas .NET más recientes".
"Es útil incluso en las aplicaciones más sencillas, la compatibilidad de Serilog con el registro estructurado destaca al instrumentar aplicaciones y sistemas complejos, distribuidos y asíncronos".

Instalando Serilog

La instalación de Serilog la haremos gracias a los NuGets. Podemos abrir una terminal para hacerlo:

$ dotnet add package Serilog
$ dotnet add package Serilog.Sinks.Console

Esto siguiendo los pasos del sitio oficial de Serilog.

El sitio de Nuget nos da solo esta acción:

$ dotnet add package Serilog --version 4.3.1

Nos quedaremos con esta última opción de instalación.

Empezando con Serilog

1. Crearemos una aplicación de consola y nos ubicamos en el directorio creado:

$ dotnet new console -n DemoSerilog
$ cd DemoSerilog

2. Agregamos Serilog:

$ dotnet add package Serilog --version 4.3.1

3. Validamos la instalación:

$ dotnet list package

Nota: En caso de necesitar más paquetes deberás remover Serilog e instalar todos los siguientes paquetes:

$ dotnet remove package Serilog

$ dotnet add package Serilog
$ dotnet add package Serilog.Sinks.Console
$ dotnet add package Serilog.Sinks.File
$ dotnet add package Serilog.Settings.Configuration

4. Abrimos el Program.cs y lo editamos.

using System;
using Serilog;

/**
 * @author Codemonkey Junior
 * @date 2026/03/14
 *
 */
class Program
{
     public Program()
    {
        Console.WriteLine("Objeto 'Program' creado e inicializado.");
    }

    ~Program() { }

    public static void Main(string[] args)
    {
        using var log = new LoggerConfiguration()
        .WriteTo.Console()
        .WriteTo.File("log.txt")
        .CreateLogger();
        
        log.Information("Hola, Serilog!");
        Log.Information("Nuestra primera configuracion Serilog.");
    }
}

5. Construimos el proyecto y lo ejecutamos:

$ dotnet build
$ dotnet run

Salida:

[14:17:07 INF] Hola, Serilog!
[14:17:07 INF] Nuestra primera configuracion Serilog.

NLog: alternativa a Serilog

El proyecto https://nlog-project.org es una alternativa a Serilog. El cual es:

..."una plataforma de registro flexible y gratuita para diversas plataformas .NET, incluyendo .NET Standard. NLog facilita la escritura en múltiples destinos (consola, archivo, base de datos, etc.) y permite modificar la configuración de registro sobre la marcha".

Esta alternativa a Serilog la veremos en otra entrega.

Hemos visto que serilog es una biblioteca de registro de diagnóstico para aplicaciones .NET. El cual es fácil de configurar, tiene una API limpia y se ejecuta en todas las plataformas .NET recientes.

También hemos visto cómo instalar, listar y remover paquetes Nuget. En próximas entregas veremos más a fondo esta librería.

Enlaces:

https://serilog.net/
Top 13 Libraries in .NET With Alteratives
https://github.com/serilog/serilog/wiki/Getting-Started



viernes, 6 de marzo de 2026

WebSockets: un canal permanente para enviar y recibir datos

Los WebSockets son un protocolo que permite comunicación bidireccional en tiempo real entre cliente y servidor, a diferencia de HTTP que funciona en modo solicitud-respuesta.

Es como un canal permanente, donde ambos lados (cliente y servidor) pueden enviar datos en cualquier momento.

Son útiles en cualquier escenario donde se requiera interacción instantánea:

  • Chats y mensajería: WhatsApp Web, Slack, Discord. 
  • Juegos online: sincronización de movimientos en tiempo real. 
  • Colaboración en documentos: Google Docs, Notion, donde ves cambios de otros usuarios al instante.
  • Notificaciones en vivo: alertas de bolsa, actualizaciones deportivas, sistemas de monitoreo. 
  • Aplicaciones financieras: transmisión de precios de criptomonedas o acciones en tiempo real. 
  • IoT y dashboards: dispositivos que envían datos continuamente a una interfaz web .

Entre las ventajas principales encontramos:

  • Baja latencia: no hay que abrir y cerrar conexiones constantemente. 
  • Menor consumo de recursos: menos overhead que múltiples peticiones HTTP. 
  • Escalabilidad: ideal para aplicaciones con miles de usuarios conectados simultáneamente.  
  • Flexibilidad: permite enviar datos en ambos sentidos (cliente ↔ servidor).

Entre los riesgos y consideraciones a tomar tenemos:

  • Seguridad: al ser persistentes, requieren cifrado (wss://) para evitar ataques de interceptación. 
  • Escalabilidad: manejar miles de conexiones simultáneas exige servidores optimizados. 
  • Compatibilidad: aunque ampliamente soportados, algunos entornos antiguos pueden no tener soporte completo.

WebSockets

Iniciemos pues con los WebSockets. Haremos un sencillo ejemplo. Para ello necesitamos:

  • Tener Node instalado en nuestras máquinas. 
  • Tener conocimiento de Javascript y Node. 
  • Instalar Express y Socket.IO

¿Qué haremos? Una aplicación que nos muestre un ejemplo del uso de WebSockets, con ayuda de JS y Node.

1. Crearemos un directorio y nos ubicaremos en el:

$ mkdir websocket-demo
$ cd websocket-demo

2. Una vez dentro del directorio, crearemos un proyecto Node:

$ npm init -y

Esto nos generará el proyecto Node.

3. Instalar las dependencias (Express y Socket.IO) con ayuda de la herramienta ``npm``:

$ npm install express socket.io

Podemos validar la descarga con este comando:

$ npm list

Salida:

+-- express@5.2.1
`-- socket.io@4.8.3

4. Creando un servidor que escuche ``server.js``:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

app.use(express.static('public'));

io.on('connection', (socket) => {
  console.log('Un usuario se conectó');

  socket.on('chat message', (msg) => {
    console.log('Mensaje recibido: ' + msg);
    io.emit('chat message', msg);
  });

  socket.on('disconnect', () => {
    console.log('Un usuario se desconectó');
  });
});

server.listen(3000, () => {
  console.log('Servidor escuchando en http://localhost:3000');
});

5. Crearemos un cliente para hacer petciones al servidor:

public\index.html

<!DOCTYPE html>
<html>
<head>
  <title>Chat con Socket.IO</title>
</head>
<body>
  <h1>Chat sencillo</h1>
  <ul id="mensajes"></ul>
  <form id="form">
    <input id="input" autocomplete="off" /><button>Enviar</button>
  </form>
  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();

    const form = document.getElementById('form');
    const input = document.getElementById('input');
    const messages = document.getElementById('mensajes');
form.addEventListener('submit', (e) => { e.preventDefault(); if (input.value) { socket.emit('chat message', input.value); input.value = ''; } }); socket.on('chat message', (msg) => { const item = document.createElement('li'); item.textContent = msg; messages.appendChild(item); }); </script> </body> </html>

6. Ejecutamos la aplicación:

$ node server.js

Si todo va bien veremos este mensaje:

Servidor escuchando en http://localhost:3000

Abrimos un navegador en la ruta: http://localhost:3000/

Veremos lo siguiente:

Mandamos un mensaje y obtendremos una respuesta:

¡Hemos creado nuestra primera aplicación con WebSockets!

Continuaremos con estos temas en próximas entregas.

Enlaces:

https://en.wikipedia.org/wiki/WebSocket
https://socket.io/
https://expressjs.com/

Una comparativa entre C# y Beef

Como vimos en una entrega anterior , Beef es un lenguaje de programación similar a C# . Con la peculiaridad que al instalarlo nos provee...

Etiquetas

Archivo del blog