2:44 0 1
Usando WebSocket directamente en Blazor WebAssembly

Usando WebSocket directamente en Blazor WebAssembly

  DrUalcman |  octubre 192022

Hoy voy a hablar de algo que no es muy común hoy en día, pero que a mi me salvo la vida hace poco. Como utiliar websocket desde Blazor WebAssembly y sobre todo el porqué de hacer esto. Pongamonos un poco en contexto. Yo tengo una utilidad, como podeis ver en este post, para imprimir de forma desantendia desde una página web, utilizando una comunicación HTTP de un pequeño servidor local. Pero hay veces que este sistema se puede volver un poco lento, sobre todo, por cómo hay que trabajar con los archivos. Es por eso que me decidí por hacer una nueva implementación utilizando WebSocket, de esta manera puedo enviar el archivo directamente a la impresora, sin que el usuario tenga que estar seleccionando la impresora desde el cuadro de diálogo.

En éstas líneas no voy a hablar de cómo hice esa implementación, ya que creo que cada uno puedo hacerla cambiando un poco el web print server de mi otro post. Pero os mostraré cómo trabajar directamnete con WebSocket desde Blazor WebAssembly de una forma genérica y así podrás implementarlo como más lo necesites. Lo que he escrito hasta ahora es sólo un ejemplo de cómo lo utilicé yo en la vida real de un projecto. Bueno en realidad también lo usamos para comunicar con una máquina de PIN PAD para cobrar con tarjetas de crédito, pero bueno eso ya es otra historia.

No es complicado usar WebSockets directamente en Blazor WebAssembly

Pero requerirá un poco de trabajo. Para usar WebSockets directamente en Blazor WebAssembly, puede seguir estos pasos:

1. Agregar una referencia al paquete NuGet "System.Net.WebSockets.Client".

2. Crear una instancia de la clase ClientWebSocket en el código de Blazor.

using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

var webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri("ws://localhost:5000/ws"), CancellationToken.None);

En el ejemplo anterior, "ws://localhost:5000/ws" es la URL del servidor WebSocket al que se está conectando. ws:// es el indicativo de que es un WebSocket y normalmente se usa el puerto 5000 pero puedes pueder cualquier puerto que este disponible, recuerda no usar un número de puerto ya en uso en tu máquina ya que estará bloqueado. 

3. Escucha eventos en la instancia de ClientWebSocket utilizando el método ReceiveAsync.

var buffer = new byte[1024];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);

if (result.MessageType == WebSocketMessageType.Text)
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Mensaje recibido: {message}");
}
}

En el ejemplo anterior, se está escuchando el evento WebSocketMessageType.Text, que se activará cada vez que se reciba un mensaje de texto.

4. Enviar mensajes al servidor WebSocket utilizando el método SendAsync.

var messageBytes = Encoding.UTF8.GetBytes(message);
await webSocket.SendAsync(new ArraySegment(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None);

En el ejemplo anterior, se está enviando un mensaje de texto al servidor WebSocket.

Estos son los pasos básicos para usar WebSockets directamente en Blazor WebAssembly. Como puede ver, hay un poco más de trabajo involucrado que con SignalR, pero si está familiarizado con los conceptos de WebSocket, no debería ser demasiado complicado.

Enviando archivos completos

Es posible enviar y recibir archivos utilizando WebSockets. Para hacerlo, se debe codificar y decodificar los datos del archivo en formato binario, y luego enviarlos y recibirlos a través de la conexión WebSocket.

Aquí hay un ejemplo básico de cómo enviar y recibir archivos utilizando WebSockets en Blazor WebAssembly:

Del lado del cliente (Blazor WebAssembly)

using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

private async Task SendFileAsync(WebSocket webSocket, Stream fileStream, string fileName)
{
var buffer = new byte[1024];
var fileLength = (int)fileStream.Length;
var message = new byte[4 + Encoding.UTF8.GetByteCount(fileName) + fileLength];

// Escribir el nombre de archivo en los primeros bytes del mensaje
var fileNameBytes = Encoding.UTF8.GetBytes(fileName);
Buffer.BlockCopy(fileNameBytes, 0, message, 4, fileNameBytes.Length);

// Escribir el archivo en los bytes restantes del mensaje
int bytesRead;
int messageOffset = 4 + fileNameBytes.Length;
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
Buffer.BlockCopy(buffer, 0, message, messageOffset, bytesRead);
messageOffset += bytesRead;
}

// Escribir la longitud del archivo en los primeros 4 bytes del mensaje
var fileLengthBytes = BitConverter.GetBytes(fileLength);
Buffer.BlockCopy(fileLengthBytes, 0, message, 0, 4);

// Enviar el mensaje WebSocket
await webSocket.SendAsync(new ArraySegment(message), WebSocketMessageType.Binary, true, CancellationToken.None);
}

private async Task ReceiveFileAsync(WebSocket webSocket)
{
var buffer = new byte[1024];
var message = new byte[1024];
var messageOffset = 0;
var fileLength = -1;
var fileName = "";

WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);

if (result.MessageType == WebSocketMessageType.Binary)
{
Buffer.BlockCopy(buffer, 0, message, messageOffset, result.Count);
messageOffset += result.Count;

if (fileLength == -1 && messageOffset >= 4)
{
// Leer la longitud del archivo desde los primeros 4 bytes del mensaje
fileLength = BitConverter.ToInt32(message, 0);

// Leer el nombre de archivo desde los bytes restantes del encabezado del mensaje
var fileNameBytes = new byte[messageOffset - 4];
Buffer.BlockCopy(message, 4, fileNameBytes, 0, fileNameBytes.Length);
fileName = Encoding.UTF8.GetString(fileNameBytes);

// Preparar el buffer de archivo con la longitud correcta
message = new byte[fileLength];
messageOffset = 0;
}

if (fileLength != -1 && messageOffset == fileLength)
{
// Escribir el archivo en el disco
using var fileStream = new FileStream(fileName, FileMode.Create);
await fileStream.WriteAsync(message, 0, message.Length);

// Reiniciar el estado de la lectura del archivo
message = new byte[1024];
messageOffset = 0;
fileLength = -1;
fileName = "";
}
}
} while (!result.CloseStatus.HasValue);
}

En este ejemplo, el método SendFileAsync toma una instancia de WebSocket, un flujo de disco para el archivo y un nombre de archivo. Este método codifica los datos del archivo en un mensaje WebSocket y lo envía al servidor a través de la conexión WebSocket.

El método ReceiveFileAsync recibe los mensajes WebSocket del servidor y los decodifica en un archivo en disco.

Del lado del servidor WebSocket

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public static async Task AcceptWebSocketAsync(HttpListenerContext context)
{
if (!context.Request.IsWebSocketRequest)
{
context.Response.StatusCode = 400;
context.Response.Close();
return;
}

var webSocketContext = await context.AcceptWebSocketAsync(null);

var webSocket = webSocketContext.WebSocket;

await ReceiveFileAsync(webSocket);

await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}

private static async Task ReceiveFileAsync(WebSocket webSocket)
{
var buffer = new byte[1024];
var message = new byte[1024];
var messageOffset = 0;
var fileLength = -1;
var fileName = "";

WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);

if (result.MessageType == WebSocketMessageType.Binary)
{
Buffer.BlockCopy(buffer, 0, message, messageOffset, result.Count);
messageOffset += result.Count;

if (fileLength == -1 && messageOffset >= 4)
{
// Leer la longitud del archivo desde los primeros 4 bytes del mensaje
fileLength = BitConverter.ToInt32(message, 0);

// Leer el nombre de archivo desde los bytes restantes del encabezado del mensaje
var fileNameBytes = new byte[messageOffset - 4];
Buffer.BlockCopy(message, 4, fileNameBytes, 0, fileNameBytes.Length);
fileName = Encoding.UTF8.GetString(fileNameBytes);

// Preparar el buffer de archivo con la longitud correcta
message = new byte[fileLength];
messageOffset = 0;
}

if (fileLength != -1 && messageOffset == fileLength)
{
// Escribir el archivo en el disco
using var fileStream = new FileStream(fileName, FileMode.Create);
await fileStream.WriteAsync(message, 0, message.Length);

// Reiniciar el estado de la lectura del archivo
message = new byte[1024];
messageOffset = 0;
fileLength = -1;
fileName = "";
}
}
} while (!result.CloseStatus.HasValue);
}

En este ejemplo, el método AcceptWebSocketAsync se utiliza para aceptar conexiones WebSocket entrantes en un servidor HTTP. Una vez que se acepta la conexión WebSocket, el método ReceiveFileAsync recibe los mensajes WebSocket del cliente y los decodifica en un archivo en disco.

Nota: Este ejemplo es solo un ejemplo básico para enviar y recibir archivos utilizando WebSockets en Blazor WebAssembly. Es posible que necesite ajustar y optimizar este código para su caso de uso específico.

Conclusiones

Los WebSockets permiten enviar y recibir datos binarios y de texto. Esto significa que puede enviar cualquier tipo de datos binarios, como imágenes, audio, videos, documentos, etc. Además, también puede enviar y recibir datos de texto, como mensajes de chat, actualizaciones de estado, eventos de aplicaciones, etc.

En general, cualquier tipo de información que necesite enviar o recibir en tiempo real puede ser transmitido a través de una conexión WebSocket. Esto hace que los WebSockets sean una herramienta muy útil para aplicaciones en tiempo real y de alta interactividad, como juegos en línea, aplicaciones de chat, sistemas de control en tiempo real, y muchas otras.

Espero que esto os sea de interés y haz un comentario de para qué lo has utilizado tu.

Happy coding.

1 Comentarios

    • johannes
      miércoles, 26 de julio de 2023 0:10

      hola! amigo podrias compartir la parte de impresion del archivo, debo imprimir un txt en una impresora de red especifica desde el celular y soy muy nuevo en este tema. Gracias de antemano

 
 
 

Archivo