Ho scritto server e client con il long polling ma mi sono accorto che comunque la comunicazione rimaneva unidirezionale. Nel caso del LongPolling è il server che invia dati al client ma il viceversa non può avvenire perchè il client è in attesa della risposta dal server.
Ho quindi indagato le websocket ed ho realizzato questo websocket server in .NET Framework e un semplice frontend di test.
A questo punto però mi sono accorto che la comunicazione era sì effettivamente bidirezionale ma non ero più in grado di caricare altre pagine html perchè il server aveva commutato sul protocollo ws://
Quindi ho fatto una cosa di questo tipo che però non so se sia proprio ortodossa:
- Quando il client invia sulla websocket un comando (nel mio esempio ‘html’) il server chiude la websocket e riapre l'httplistner tornando in ascolto per una connessione http.
- Quando il client riceve l'evento websocket.onclose() richiama una nuova pagina html (window.location.assign(window.location.origin + "/pagina2.html");
Questo metodo funziona egregiamente ma non so se è l'approccio migliore.
Cosa mi consigliate?
SERVER WEBSOCKET in C# .NET framework
using System;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketNet
{
public class WebSocketServer : IDisposable
{
public static HttpListener httplistener;
public static string url = "http://+:8400/";
public static int pageViews = 0;
public static int requestCount = 0;
public static WebSocket webSocket;
public WebSocketServer()
{
//------------------------- ASCOLTO SULLA PORTA HTTP -----------------------------------
//
// ---> IMPORTANTE
//Prenotazione url per gli utenti non amministratori
//Comando Per inserire la prenotazione: netsh http add urlacl url=http://+:8400/ user=everyone
//Comando per verificare la prenotazione: netsh http show urlacl http://+:8400/
//Altrimenti ritorna errore se non si avvia in modalità amministratore
// Create a Http server and start listening for incoming connections
httplistener = new HttpListener();
httplistener.Prefixes.Add("http://+:8400/");
httplistener.Start();
Console.WriteLine("Listening for connections");
Task listenTask = HandleIncomingConnections();
}
public static async Task HandleIncomingConnections()
{
bool runServer = true;
while (runServer)
{
HttpListenerContext context = await httplistener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(subProtocol: null);
webSocket = webSocketContext.WebSocket;
Console.WriteLine("Client connected");
Task listenWs = ListenWs();
break;
}
else
{
// Peel out the requests and response objects
HttpListenerRequest req = context.Request;
HttpListenerResponse resp = context.Response;
// Print out some info about the request
Console.WriteLine("Request #: {0}", ++requestCount);
Console.WriteLine(req.Url.ToString());
Console.WriteLine(req.HttpMethod);
Console.WriteLine(req.UserHostName);
Console.WriteLine(req.UserAgent);
Console.WriteLine();
string risposta = "";
if ((req.HttpMethod == "GET"))
{
if (req.Url.Segments.Length == 1)
{
risposta = ReadFile(@".\index.html");
resp.ContentType = "text/html";
}
else
{
if (req.Url.Segments[1] != "favicon.ico")
{
risposta = ReadFile(req.Url.Segments[1]);
if (req.Url.Segments[1] == "main.css") resp.ContentType = "text/css";
else if (req.Url.Segments[1] == "app.js") resp.ContentType = "text/js";
else resp.ContentType = "text/html";
}
}
}
// Write the response info
byte[] data;
data = Encoding.UTF8.GetBytes(risposta);
resp.ContentEncoding = Encoding.UTF8;
resp.ContentLength64 = data.LongLength;
// Write out to the response stream (asynchronously), then close it
await resp.OutputStream.WriteAsync(data, 0, data.Length);
resp.Close();
}
}
}
public static async Task ListenWs()
{
while (true)
{
byte[] receiveBuffer = new byte[1024];
if (webSocket.State == WebSocketState.Open)
{
var receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
if (receiveResult.MessageType == WebSocketMessageType.Text)
{
string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, receiveResult.Count);
Console.WriteLine($"Received message: {receivedMessage}");
if (receivedMessage == "html")
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
Console.WriteLine("WebSocket closed.");
Task listenTask = HandleIncomingConnections();
break;
}
}
else if (receiveResult.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
Console.WriteLine("WebSocket closed.");
}
}
}
}
public void SendToClient(string text)
{
Task listenWs = Send(text);
}
private static async Task Send(string text)
{
//byte[] buffer = Encoding.UTF8.GetBytes($"Hello from server");
byte[] buffer = Encoding.UTF8.GetBytes(text);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
private static string ReadFile(string path)
{
string stringfile;
using (StreamReader IndexFile = new StreamReader(path))
{
stringfile = IndexFile.ReadToEnd();
}
return stringfile;
}
public void Dispose()
{
httplistener.Close();
}
}
}
FrontEnd inviato al client
<!doctype html>
<html lang="en">
<style>
textarea {
vertical-align: bottom;
}
#output {
overflow: auto;
}
#output > p {
overflow-wrap: break-word;
}
#output span {
color: blue;
}
#output span.error {
color: red;
}
</style>
<body>
<h2>WebSocket Test Pagina 0</h2>
<textarea cols="60" rows="6"></textarea>
<button>send</button>
<div id="output"></div>
</body>
<script>
// http://www.websocket.org/echo.html
const button = document.querySelector("button");
const output = document.querySelector("#output");
const textarea = document.querySelector("textarea");
const origin = window.location.origin;
//const wsUri = "ws://127.0.0.1:8400/";
//const wsUri = "ws://" + window.location.hostname; // ws://localhost
//const wsUri = "ws://" + window.location.origin; // ws://http://localhost:8400
const wsUri = "ws://" + window.location.hostname + ":" + window.location.port;
const websocket = new WebSocket(wsUri);
let text = textarea.value;
button.addEventListener("click", onClickButton);
websocket.onopen = (e) => {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
};
websocket.onclose = (e) => {
writeToScreen("DISCONNECTED");
if (text == "html"){
//window.location.reload(); //Ricarica la stessa Pagina
window.location.assign(origin + "/page1.html");
//fetch('http://127.0.0.1:8400/', { mode: 'no-cors'});
}
};
websocket.onmessage = (e) => {
writeToScreen(`<span>RESPONSE: ${e.data}</span>`);
};
websocket.onerror = (e) => {
writeToScreen(`<span class="error">ERROR:</span> ${e.data}`);
};
function doSend(message) {
writeToScreen(`SENT: ${message}`);
websocket.send(message);
}
function writeToScreen(message) {
output.insertAdjacentHTML("afterbegin", `<p>${message}</p>`);
}
function onClickButton() {
text = textarea.value;
text && doSend(text);
textarea.value = "";
textarea.focus();
}
</script>
</html>