Commit b7dc25ca authored by Jose Hugo Torres's avatar Jose Hugo Torres
Browse files

Configuracion para POSBC

Adición de conexión cliente hacia POSBC - modo de prueba transferencia directa mensaje CHEC a POSBC y de retorno.
parent 11b7efd5
...@@ -5,6 +5,10 @@ VisualStudioVersion = 17.6.33815.320 ...@@ -5,6 +5,10 @@ VisualStudioVersion = 17.6.33815.320
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gatewayGK", "gatewayGK\gatewayGK.csproj", "{3E81A0D0-DF98-4680-89D2-B1FA801A42E2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gatewayGK", "gatewayGK\gatewayGK.csproj", "{3E81A0D0-DF98-4680-89D2-B1FA801A42E2}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gatewayPruebaECO", "gatewayPruebaECO\gatewayPruebaECO.csproj", "{AE8C6130-DC0D-41EA-9F61-88DAC3D4A712}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gatewayPruebaECO_POSBC", "gatewayPruebaECO_POSBC\gatewayPruebaECO_POSBC.csproj", "{4A774AA8-BB85-4836-A011-D99952744E2B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -15,6 +19,14 @@ Global ...@@ -15,6 +19,14 @@ Global
{3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Release|Any CPU.Build.0 = Release|Any CPU {3E81A0D0-DF98-4680-89D2-B1FA801A42E2}.Release|Any CPU.Build.0 = Release|Any CPU
{AE8C6130-DC0D-41EA-9F61-88DAC3D4A712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE8C6130-DC0D-41EA-9F61-88DAC3D4A712}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE8C6130-DC0D-41EA-9F61-88DAC3D4A712}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE8C6130-DC0D-41EA-9F61-88DAC3D4A712}.Release|Any CPU.Build.0 = Release|Any CPU
{4A774AA8-BB85-4836-A011-D99952744E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A774AA8-BB85-4836-A011-D99952744E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A774AA8-BB85-4836-A011-D99952744E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A774AA8-BB85-4836-A011-D99952744E2B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
......
...@@ -16,13 +16,25 @@ La aplicación espera el archivo de configuación con los parámetros de operaci ...@@ -16,13 +16,25 @@ La aplicación espera el archivo de configuación con los parámetros de operaci
### Parámetros de configuración ### Parámetros de configuración
En la siguiente lista se presentan los parámetros de configuración definidos. En la siguiente lista, los parámetros y sus valores. En la siguiente lista se presentan los parámetros de configuración definidos.
- Indicar tipos de POS, según el tipo se activan los comandos adecuados. En la siguiente lista, los parámetros y sus valores.
- 'POS' valores: 'pruebas', 'gk', 'evapos'.
- Ubicación del SCO. Sección **GatewayConfig**:
- 'IpSCO': string, dirección IP del SCO.
- 'PortSCO': string, puerto IP del SCO. - **POS** - Indica el tipo de POS al que se conecta el gateway. Según el tipo de POS se activan los comandos adecuados. Valores aceptados:
- 'ECO': usado para pruebas, retorna el mismo mensaje que recibe.
- 'ECO-POSBC': usado para pruebas, remite el mensaje que recibe a POSBC de pruebas y retorna respuesta sin cambios.
- 'POSBC': conecta con Toshiba POSBC.
- 'EVAPOS': conecta con pos EvaPOS.
- 'GK': conecta a pos GK Ommichannel.
- **IpSCO** string, dirección IP del SCO.
- **PortSCO** string, puerto IP del SCO.
Sección **POSBC**:
- **Ip**: IP del servidor POSBC.
- **Port**: Puerto del servidor POSBC.
### Programación de la configuración ### Programación de la configuración
...@@ -45,7 +57,6 @@ La siguiente es la secuencia de pasos que sigue el programa para procesar una so ...@@ -45,7 +57,6 @@ La siguiente es la secuencia de pasos que sigue el programa para procesar una so
- Según el tipo de mensaje se obtiene el comando adecuado y su adaptador. El factory de creación del directorio de comandos se encargga de inicializar los comandos adecuados al tipo de POS. - Según el tipo de mensaje se obtiene el comando adecuado y su adaptador. El factory de creación del directorio de comandos se encargga de inicializar los comandos adecuados al tipo de POS.
- Se ejecuta el comando. - Se ejecuta el comando.
## Agregar soporte a una nueva POS ## Agregar soporte a una nueva POS
Para soportar un nuevo tipo de POS, los pasos son: Para soportar un nuevo tipo de POS, los pasos son:
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
/// </summary> /// </summary>
public sealed class Config public sealed class Config
{ {
public required string POS { get; set; } = "pruebas"; public required string POS { get; set; } = "ECO";
public required string IpSCO { get; set; } = "127.0.0.1"; public required string IpGateway { get; set; } = "127.0.0.1";
public required int PortSCO { get; set; } = 6697; public required int PortGateway { get; set; } = 6697;
public required string Language { get; set; } = "en"; public required string Language { get; set; } = "en";
public override string ToString() public override string ToString()
{ {
return base.ToString() + " - " + $"POS: '{POS}', IpSCO: '{IpSCO}', PortSCO: '{PortSCO}', Language: '{Language}'"; return base.ToString() + " - " + $"POS: '{POS}', IpGateway: '{IpGateway}', PortGateway: '{PortGateway}', Language: '{Language}'";
} }
} }
\ No newline at end of file
/// <summary>
/// Clase que representa los parámetros de configuración de la aplicación
/// registrados en el archivo appsettings.json asociados a POSBC.
/// </summary>
public sealed class ConfigPOSBC
{
public required string IpPOSBC { get; set; } = "127.0.0.1";
public required int PortPOSBC { get; set; } = 6698;
public override string ToString()
{
return base.ToString() + $" - IpPOSBC: '{IpPOSBC}', PortPOSBC: '{PortPOSBC}' ";
}
}
\ No newline at end of file
...@@ -8,18 +8,16 @@ public class DirectorioCmdsFactory ...@@ -8,18 +8,16 @@ public class DirectorioCmdsFactory
{ {
public static CreaDirectorioCmds CreaDirectorio(string tipoPOS) public static CreaDirectorioCmds CreaDirectorio(string tipoPOS)
{ {
switch (tipoPOS) return tipoPOS switch
{ {
case "pruebas": "ECO" => DispensaDirectorioCmdsPruebas.Dispensa(),
return DispensaDirectorioCmdsPruebas.Dispensa(); //"ECO_POSBC" => throw new NotImplementedException(),
case "evapos": "ECO_POSBC" => DispensaDirectorioCmdsGKPruebas.Dispensa(),
return DispensaDirectorioCmdsEvaPOS.Dispensa(); "POSBC" => throw new NotImplementedException(),
case "gk": "evapos" => DispensaDirectorioCmdsEvaPOS.Dispensa(),
return DispensaDirectorioCmdsGK.Dispensa(); "gk" => DispensaDirectorioCmdsGK.Dispensa(),
case "gk_test": "gk_test" => DispensaDirectorioCmdsGKPruebas.Dispensa(),
return DispensaDirectorioCmdsGKPruebas.Dispensa(); _ => throw new ArgumentException("Valor no reconocido en archivo configuración, parámetro 'POS', valor encontrado {pos}.", tipoPOS),
default: };
throw new ArgumentException("TipoPOS no válido", "tipoPOS");
}
} }
} }
...@@ -14,7 +14,7 @@ public class DispensaDirectorioCmdsGKPruebas : IDispensaDirectorioCmds ...@@ -14,7 +14,7 @@ public class DispensaDirectorioCmdsGKPruebas : IDispensaDirectorioCmds
/// </summary> /// </summary>
public static CreaDirectorioCmds Dispensa() public static CreaDirectorioCmds Dispensa()
{ {
Log.Information("Instancia comandos de GK de prueba."); Log.Information("Instancia comandos de GK/ECO_POSBC de prueba.");
return new IniciaDirectorioCmds() return new IniciaDirectorioCmds()
.AgregaCmd(new Gk.InitializeRequestCmd()) .AgregaCmd(new Gk.InitializeRequestCmd())
......
...@@ -10,3 +10,17 @@ ...@@ -10,3 +10,17 @@
- Manejar errores servicios web, ejemplo: - Manejar errores servicios web, ejemplo:
{"errorCode":{"errorCode":"GKR-POS-000001","message":"Invalid session","messageKey":"com.gk_software.pos.utils.error.ErrorCodeMessages.MSG_INVALID_SESSION","arguments":[]},"timestamp":"2023-08-04T07:16:57.066","additionalContextInfoMap":{}} {"errorCode":{"errorCode":"GKR-POS-000001","message":"Invalid session","messageKey":"com.gk_software.pos.utils.error.ErrorCodeMessages.MSG_INVALID_SESSION","arguments":[]},"timestamp":"2023-08-04T07:16:57.066","additionalContextInfoMap":{}}
- El manejo de headers, la cookie no es obligatoria, para pasar la sessión este es obligatorio: request.AddHeader("_pos_session_", sessionId); - El manejo de headers, la cookie no es obligatoria, para pasar la sessión este es obligatorio: request.AddHeader("_pos_session_", sessionId);
## 16 junio 24
- Cambios en archivo de configuración para facilitar pruebas con Toshiba POSBC.
- Target framework del proyecto cambiado a net8.0 (gatewayGK.csproj).
- public class DirectorioCmdsFactory modificada para aceptar nuevos tipos de POS en archivo de configuración.
- Creado proyecto getewayPruebaECO, emula un CHEC que interactura con el gateway
- Se agrega referencia del proyecto gateway al proyencto pruebasECO:
PS C:\jht\ApiGatewayCHEC\api-gateway-chec\gatewayPruebaECO> dotnet add ./gatewayPruebaECO.csproj reference ../gatewayGK/gatewayGK.csproj
Se ha agregado la referencia "..\gatewayGK\gatewayGK.csproj" al proyecto.
- Creado proyecto gatewayECO_POSBC, simulando servidor POSBC que retorna mensajes iguales a los que les llegan.
- Se agrega referencia del proyecto gateway el proyecto PruebaECO_POSBC:
PS C:\jht\ApiGatewayCHEC\api-gateway-chec\gatewayPruebaECO_POSBC> dotnet add ./gatewayPruebaECO_POSBC.csproj reference ../gatewayGK/gatewayGK.csproj
Se ha agregado la referencia "..\gatewayGK\gatewayGK.csproj" al proyecto.
using System.Net;
using System.Net.Sockets;
using System.Text;
using EvaPosSCOSrv;
using Serilog;
namespace gatewaySCO.POSBC;
// Maneja conexión al POSBC.
public class ClienteServidorPOSBC(string ip, int pto)
{
ILogger log = Log.ForContext<ClienteServidorPOSBC>();
int _pto = pto;
string _ip = ip;
Socket _socket = null;
public bool ConexionActiva { get; private set; } = false;
int _contadorMensajesEnviados = 0;
int _contadorMensajesRecibidos = 0;
public void AbreConexion()
{
_socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Conectarse al servidor
try
{
_socket.Connect(new IPEndPoint(IPAddress.Parse(_ip), _pto));
ConexionActiva = true;
Log.Information("Conectado a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected);
}
catch (SocketException ex)
{
Log.Error("Excepción socket conexión a POSBC {ip}:{pto} - código error socket {codigo} - {e}", _ip, ex.SocketErrorCode, _pto, ex);
}
catch (Exception e)
{
Log.Error("Excepción en conexión a POSBC {ip}:{pto} - {e}", _ip, _pto, e);
}
}
public void CierraConexion()
{
// Cerrar el socket
ConexionActiva = false;
try
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
catch (Exception e)
{
Log.Warning("Excepción cerrando conexión a POSBC {ip}:{pto} - {e}", _ip, _pto, e);
}
Log.Information("Conexión cerrada a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected);
}
// Convierte el string del parámetro a un arreglo de bytes,
// anteponiendo 4 bytes con la longitud del mensaje.
public static byte[] ConvertirEnBytes(string mensaje)
{
// Codifica longitud del mensaje en un entero sin signo en los 4 primeros bytes.
var longitud = Convert.ToUInt32(mensaje.Length);
byte[] bytesConLongMensaje = BitConverter.GetBytes(longitud);
// Bytes mas significativos deben ir primero, usa 'big-endian'.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesConLongMensaje);
// Codifica en bytes texto del mensaje.
byte[] bytesConMensaje = Encoding.UTF8.GetBytes(mensaje);
// Copia los 2 arreglos de bytes en un arreglo unificado.
byte[] bytes = new byte[bytesConLongMensaje.Length + bytesConMensaje.Length];
Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length);
Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length);
return bytes;
}
// Envia mensaje, espera respuesta y la retorna como un arreglo de bytes.
// El mensaje es un string, convertido a arreglo de bytes, y le antepone
// 4 bytes con la longitud del mensaje.
// TODO : validar que la respuesta sean mas de 1 mensaje!!!
public byte[] EnviaRecibe(byte[] bytesMensaje)
{
// Remite mensaje.
_socket.Send(bytesMensaje);
_contadorMensajesEnviados++;
Log.Information("Mensaje #{contador} para POSBIC {ip} - {msj}", _contadorMensajesEnviados, _ip, bytesMensaje.ToString);
Log.Information("Esperando respuesta..");
// Leer la respuesta del servidor
byte[] buffer = new byte[1024]; // Tamaño del buffer inicial
int totalBytesRead = 0;
int bytesRead;
byte[] bufferEntrada;
// Usar un MemoryStream para manejar datos de longitud arbitraria
using (var ms = new System.IO.MemoryStream(1024))
{
while ((bytesRead = _socket.Receive(buffer)) > 0)
{
ms.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
// Romper el ciclo si se ha leído todo el mensaje
if (_socket.Available == 0) break;
}
// Obtener todos los datos recibidos como un arreglo de bytes
bufferEntrada = ms.ToArray();
}
_contadorMensajesRecibidos++;
Log.Debug("Respuesta #{contador} de POSBC {ip} - {msj}", _contadorMensajesRecibidos, _ip, bufferEntrada.ToString);
return bufferEntrada;
}
}
\ No newline at end of file
using System.Net.Sockets;
namespace gatewaySCO.POSBC;
/// <summary>
/// Esta clase almacena valores requeridos
/// para la operación del gateway conectado a POSBC.
/// </summary>
public class EntornoPOSBC
{
public int PortPOSBC { get; set; } = 6697;
public string IpPOSBC { get; set; } = "127.0.0.1";
/// <summary>
/// Socket en uso de conexión al POSBC.
/// </summary>
public ClienteServidorPOSBC ClientePOSBC { get; set; }
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration; ...@@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration;
using EvaPosSCOSrv; using EvaPosSCOSrv;
using EvaPosSrvAplicacionImp; using EvaPosSrvAplicacionImp;
using EvaPosSrvRespImp; using EvaPosSrvRespImp;
using gatewaySCO.POSBC;
namespace GatewaySCO namespace GatewaySCO
{ {
...@@ -14,11 +15,12 @@ namespace GatewaySCO ...@@ -14,11 +15,12 @@ namespace GatewaySCO
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
Program program = new Program(); Console.WriteLine("*** Gateway SCO - Servidor: procesa peticiones de Toshiba CHEC ***");
program.ActivaServidor(args); Program program = new();
LeeActivaConfiguracion(args);
} }
public Config LeeConfiguracion(string[] args) public static void LeeActivaConfiguracion(string[] args)
{ {
// TODO - opción de incluir la activación en la cadena de configuración. // TODO - opción de incluir la activación en la cadena de configuración.
...@@ -41,6 +43,7 @@ namespace GatewaySCO ...@@ -41,6 +43,7 @@ namespace GatewaySCO
Config config = configBuilder.GetRequiredSection("GatewayConfig").Get<Config>() Config config = configBuilder.GetRequiredSection("GatewayConfig").Get<Config>()
?? throw new ApplicationException("Archivo de configuración sin sección 'GatewayConfig'."); ?? throw new ApplicationException("Archivo de configuración sin sección 'GatewayConfig'.");
Log.Information(config.ToString()); Log.Information(config.ToString());
Entorno<Config>.Instancia.set(config);
// Lee del archivo de configuración sección POS Gk si aplica. // Lee del archivo de configuración sección POS Gk si aplica.
// TODO - valor de parámetro POS de archivo de configuración en ENUM? // TODO - valor de parámetro POS de archivo de configuración en ENUM?
...@@ -55,22 +58,41 @@ namespace GatewaySCO ...@@ -55,22 +58,41 @@ namespace GatewaySCO
Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk; Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk;
Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}"); Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}");
} }
return config; // Configuración para pruebas de emulación del POSBC.
if (config.POS == "ECO_POSBC")
{
ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>()
?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'.");
Log.Information(configPOSBC.ToString());
Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC());
Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC;
Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC;
// Se activa lógica de cliente POSBC
// La conexión con el POSBC se almacena en el entorno para uso
// posterior en la emisión y recepción de mensajes.
Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC);
Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion();
if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false)
{
throw new ApplicationException("Error en conexión al POSBC.");
}
}
// Activa servidor Gateway.
// En este punto, el programa se bloquea esperando conexión y mensajes desde CHEC.
ActivaServidor(config.IpGateway, config.PortGateway, config.POS);
} }
public void ActivaServidor(string[] args) // Servidor sockets: acepta peticiones de SCO CHEC.
public static void ActivaServidor(string ip, int pto, string tipoPOS)
{ {
Console.WriteLine("*** Gateway SCO ***");
Config config = LeeConfiguracion(args);
try try
{ {
Console.WriteLine("*** Gateway SCO ***");
// TODO - aunque hace uso de una "invocación fluida" no parece ser el tipo de api que se beneficia de ese esquema. // TODO - aunque hace uso de una "invocación fluida" no parece ser el tipo de api que se beneficia de ese esquema.
ServidorSocket servidor = new ServidorSocket() ServidorSocket servidor = new ServidorSocket()
.ConIp(config.IpSCO) .ConIp(ip)
.EnPuerto(config.PortSCO) .EnPuerto(pto)
.AgregaDispensadorAdaptadores(IniciaDirectorioAdaptadores.Cargar().DirectorioAdaptadores) .AgregaDispensadorAdaptadores(IniciaDirectorioAdaptadores.Cargar().DirectorioAdaptadores)
.AgregaDirectorioCmds(DirectorioCmdsFactory.CreaDirectorio(config.POS)) .AgregaDirectorioCmds(DirectorioCmdsFactory.CreaDirectorio(tipoPOS))
.AgregaProcesadorAplicacion(new Aplicacion()) .AgregaProcesadorAplicacion(new Aplicacion())
.Activa(); .Activa();
} }
......
using System;
using System.Collections.Generic;
using System.Xml; using System.Xml;
using EvaPosSrvDTO; using EvaPosSrvDTO;
......
...@@ -11,8 +11,9 @@ using GatewaySCO; ...@@ -11,8 +11,9 @@ using GatewaySCO;
using EvaPosSrvDTO; using EvaPosSrvDTO;
using EvaPosSrvResp; using EvaPosSrvResp;
using EvaPosSrvAplicacion; using EvaPosSrvAplicacion;
using EvaPOS_API_FRAME.RespuestasXML;
using EvaPOS_API_FRAME.Comandos; using EvaPOS_API_FRAME.Comandos;
using gatewaySCO.POSBC;
namespace EvaPosSCOSrv namespace EvaPosSCOSrv
{ {
/// <summary> /// <summary>
...@@ -28,7 +29,7 @@ namespace EvaPosSCOSrv ...@@ -28,7 +29,7 @@ namespace EvaPosSCOSrv
/// <summary> /// <summary>
/// Longitud máxima de mensaje de entrada. /// Longitud máxima de mensaje de entrada.
/// </summary> /// </summary>
public const Int32 LongMaxMensaje = 1_024 * 8; public const Int32 LongMaxMensaje = 1_024 * 32;
/// <summary> /// <summary>
/// Dirección ip para vincular el socket. /// Dirección ip para vincular el socket.
/// </summary> /// </summary>
...@@ -204,6 +205,10 @@ namespace EvaPosSCOSrv ...@@ -204,6 +205,10 @@ namespace EvaPosSCOSrv
socketEntrada = tcpSocket.Accept(); socketEntrada = tcpSocket.Accept();
continuar = ProcesaConexion(socketEntrada, _numeroConexionesEntrantes++); continuar = ProcesaConexion(socketEntrada, _numeroConexionesEntrantes++);
// TODO - Validar lo siguiente.
// Manejo de situación en la cual CHEC mantiene abierta la conexión pero
// remite un comando TERMINATE, con lo cual, hay que reciclar la conexión:
// se cierra el socket y se abre nuevamente.
while (!continuar) while (!continuar)
{ {
tcpSocket.Close(); tcpSocket.Close();
...@@ -215,7 +220,6 @@ namespace EvaPosSCOSrv ...@@ -215,7 +220,6 @@ namespace EvaPosSCOSrv
SocketType.Stream, SocketType.Stream,
ProtocolType.Tcp); ProtocolType.Tcp);
tcpSocket2.LingerState = new LingerOption(true, 3); tcpSocket2.LingerState = new LingerOption(true, 3);
tcpSocket2.NoDelay = true; tcpSocket2.NoDelay = true;
tcpSocket2.ReceiveTimeout = 0; tcpSocket2.ReceiveTimeout = 0;
...@@ -241,17 +245,27 @@ namespace EvaPosSCOSrv ...@@ -241,17 +245,27 @@ namespace EvaPosSCOSrv
} }
/// <summary> /// <summary>
/// Retorna longitud del mensaje representada en los 4 primeros bytes del arreglo bytes de argumento. /// Longitud del mensaje, retorna entero representado en 4 bytes, sin signo.
/// </summary> /// </summary>
private uint LongitudMensaje(byte[] arregloBytes) public static uint LongitudMensaje(byte[] bytesMensaje)
{ {
// Extrae longitud. Primeros 4 bytes del arreglo. // Extrae longitud. Primeros 4 bytes del arreglo.
byte[] bytesConLongMensaje = new byte[4]; byte[] bytesLongitudMensaje = new byte[4];
Buffer.BlockCopy(arregloBytes, 0, bytesConLongMensaje, 0, 4); Buffer.BlockCopy(bytesMensaje, 0, bytesLongitudMensaje, 0, 4);
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
Array.Reverse(bytesConLongMensaje); Array.Reverse(bytesLongitudMensaje);
var longitud = BitConverter.ToUInt32(bytesConLongMensaje, 0); return BitConverter.ToUInt32(bytesLongitudMensaje, 0);
return longitud; }
/// <summary>
/// Concatena dos arreglos de bytes en un tercero que retorna como respuesta.
/// </summary>
public static byte[] ConcatenateArrays(byte[] array1, byte[] array2)
{
byte[] result = new byte[array1.Length + array2.Length];
Buffer.BlockCopy(array1, 0, result, 0, array1.Length);
Buffer.BlockCopy(array2, 0, result, array1.Length, array2.Length);
return result;
} }
/// <summary> /// <summary>
...@@ -259,7 +273,6 @@ namespace EvaPosSCOSrv ...@@ -259,7 +273,6 @@ namespace EvaPosSCOSrv
/// </summary> /// </summary>
private bool ProcesaConexion(Socket socket, long nroConexion) private bool ProcesaConexion(Socket socket, long nroConexion)
{ {
bool result = true;
Respuestas respuestas = null; Respuestas respuestas = null;
int contIngreso = 0; int contIngreso = 0;
if (_isDebug) if (_isDebug)
...@@ -270,7 +283,8 @@ namespace EvaPosSCOSrv ...@@ -270,7 +283,8 @@ namespace EvaPosSCOSrv
((IPEndPoint)socket.RemoteEndPoint).Port.ToString()); ((IPEndPoint)socket.RemoteEndPoint).Port.ToString());
contIngreso++; contIngreso++;
} }
while (true) bool continua = true;
while (continua)
{ {
try try
{ {
...@@ -284,94 +298,94 @@ namespace EvaPosSCOSrv ...@@ -284,94 +298,94 @@ namespace EvaPosSCOSrv
bytesLeidos += socket.Receive(bufferLongitud, bytesLeidos, bufferLongitud.Length, SocketFlags.None); bytesLeidos += socket.Receive(bufferLongitud, bytesLeidos, bufferLongitud.Length, SocketFlags.None);
} }
Log.Debug("Arriba un mensaje.");
// Lee porción de datos del mensaje, hasta la longitud indicada en los 4 primeros bytes. // Lee porción de datos del mensaje, hasta la longitud indicada en los 4 primeros bytes.
var longitudMensaje = LongitudMensaje(bufferLongitud); uint longitudMensaje = LongitudMensaje(bufferLongitud);
Log.Debug("Longitud mensaje {long} - buffer longitud mensaje >>{buffer}<<", longitudMensaje, bufferLongitud); if (longitudMensaje > LongMaxMensaje) throw new Exception($"Mensaje {longitudMensaje} bytes supera máximo permitido de {LongMaxMensaje} bytes.");
if (longitudMensaje > LongMaxMensaje) throw new Exception($"Mensaje recibido de {longitudMensaje} bytes supera máximo permitido de {LongMaxMensaje} bytes.");
Log.Debug("Leyendo bytes con mensaje...");
var bufferEntrada = new byte[longitudMensaje]; var bufferEntrada = new byte[longitudMensaje];
bytesLeidos = 0; bytesLeidos = 0;
while (bytesLeidos < longitudMensaje) while (bytesLeidos < longitudMensaje)
{ {
bytesLeidos += socket.Receive(bufferEntrada, bytesLeidos, bufferEntrada.Length, SocketFlags.None); bytesLeidos += socket.Receive(bufferEntrada, bytesLeidos, bufferEntrada.Length, SocketFlags.None);
} }
Log.Information("Bytes recibidos {bytes}", bytesLeidos); Log.Information("Nuevo mensaje {bytes} bytes.", bytesLeidos);
// Procesando entrada: se obtiene mensaje, con el cual se string tipoPOS = Entorno<Config>.Instancia.get().POS;
// identifica comando que lo procesa, se ejecuta el comando
// y se retornarn respuestas al cliente que ha emitido el mensaje.
TramaSCO msj = _sesion.Entrada(bufferEntrada, bytesLeidos);
IComando cmd = _presentacion.Entrada(msj);
//Enviamos la respuesta del pinpad primero del datafono antes de que entre al comando procesar // -------------------------------------------------------------------------------
if (cmd.Referencia == "scsns:AddTender") // Procesa mensajes desde CHEC según la modalida de configuración del Gateway
{ // -------------------------------------------------------------------------------
Respuestas respuestaDatafono = EjecutarProcesarDatafonoDirecto();
foreach (var respuesta in respuestaDatafono) // --- Caso de pruebas mdalidad ECO: lo que recibe,
// lo transmite a POSBC y retorna a CHEC sus respuestas sin cambios.
if (tipoPOS == "ECO_POSBC")
{ {
Log.Debug("Respuesta del pinpad del datafono"); // Modo "past-throught": lo que entra se remite sin cambios al POSBC y
var bufferSalida = _sesion.Salida(respuesta.TramaSCO); // su respeusta se remite sin cambios a CHEC.
byte[] mensajeEntrada = ConcatenateArrays(bufferLongitud, bufferEntrada);
// Enviar mensaje de entrada a POSBC y retornar respuestas.
byte[] bufferSalida = Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.EnviaRecibe(mensajeEntrada);
// Remitir respuestas sin cambio a CHEC.
socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None); socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None);
Log.Information("Bytes enviados {bytes}", bufferSalida.Length); Log.Information("Respuesta remitida: {bytes}", bufferSalida);
}
// Procesando entrada: se obtiene mensaje, con el cual se
// identifica comando que lo procesa.
TramaSCO msj = Sesion.Entrada(bufferEntrada, bytesLeidos);
IComando cmd = _presentacion.Entrada(msj);
if (cmd.Referencia == "scsns:Terminate")
{
continua = false;
} }
respuestas = _aplicacion.Procesar(cmd); }
Log.Debug("Respuestas de cmd ref '{cmd}' : {nroRespuestas}", cmd.Referencia, respuestas.Count); else
{
// --- Caso de operación normal, convierte mensajes en comandos para procesamiento.
// Enviando respuestas. // Procesando entrada: se obtiene mensaje, con el cual se
// identifica comando que lo procesa, se ejecuta el comando
// y se retornarn respuestas al cliente que ha emitido el mensaje.
TramaSCO msj = Sesion.Entrada(bufferEntrada, bytesLeidos);
IComando cmd = _presentacion.Entrada(msj);
respuestas = _aplicacion.Procesar(cmd);
Log.Information("Comando '{cmd}'", cmd.Referencia);
int i = 1; int i = 1;
foreach (var respuesta in respuestas) foreach (var respuesta in respuestas)
{ {
Log.Debug("Respuesta #{i}", i++); // Enviando respuestas.
var bufferSalida = _sesion.Salida(respuesta.TramaSCO); var bufferSalida = Sesion.Salida(respuesta.TramaSCO);
socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None); socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None);
Log.Information("Bytes enviados {bytes}", bufferSalida.Length); Log.Information("Respuesta {i}/{total} remitida, {bytes} bytes", i, respuestas.Count, bufferSalida.Length);
} }
log.Information("Fin del ciclo, se enviaron {nro} respuestas", respuestas.Count);
if (cmd.Referencia == "scsns:Terminate") if (cmd.Referencia == "scsns:Terminate")
{ {
result = false; continua = false;
break; }
} }
} }
catch (SocketException ex) catch (SocketException ex)
{ {
if (ex.SocketErrorCode == SocketError.ConnectionAborted) if (ex.SocketErrorCode == SocketError.ConnectionAborted)
{ {
// La conexión fue anulada por el software en el equipo remoto log.Warning("Conexión abortada por el equipo remoto.");
log.Warning("La conexión fue anulada por el software en el equipo remoto.");
} }
else else
{ {
// Otra excepción de SocketException
log.Error("Error de Socket: {error}", ex); log.Error("Error de Socket: {error}", ex);
} }
result = false; continua = false;
break;
} }
catch (Exception e ) catch (Exception e)
{ {
log.Error("Error : {error}" , e); log.Error("Error : {error}", e);
result = false; continua = false;
break;
} }
} }
return result; return continua;
}
//Metodo de respuesta para el pinpad del datafono
public Respuestas EjecutarProcesarDatafonoDirecto()
{
Respuestas respuestaPinPad = null;
POSBCStatusEventPinPadStatus posbcEventWaitScreenPaidPad = null;
posbcEventWaitScreenPaidPad = new POSBCStatusEventPinPadStatus(1, TipoMensaje.Event, "INFO", "WAITING_FOR_PINPAD_INPUT", "Siga las instrucciones del Datafono");
respuestaPinPad = new Respuestas { posbcEventWaitScreenPaidPad };
return respuestaPinPad;
} }
} }
...@@ -388,16 +402,17 @@ namespace EvaPosSCOSrv ...@@ -388,16 +402,17 @@ namespace EvaPosSCOSrv
/// Interpreta arreglo de bytes en mensaje, extrayendo del arreglo de bytes /// Interpreta arreglo de bytes en mensaje, extrayendo del arreglo de bytes
/// y copiando el contenido en un objeto tipo Trama. /// y copiando el contenido en un objeto tipo Trama.
/// </summary> /// </summary>
public TramaSCO Entrada(byte[] buffer, int nroBytes) public static TramaSCO Entrada(byte[] buffer, int nroBytes)
{ {
if (_isDebug) if (_isDebug)
{ {
log.Debug("Buffer entrada: >>{subBuffer}<<", buffer); log.Debug("Buffer entrada: >>{subBuffer}<<", buffer);
} }
TramaSCO trama = new TramaSCO(); TramaSCO trama = new()
trama.Longitud = Convert.ToUInt32(nroBytes); {
log.Debug("Longitud mensaje: {long}", trama.Longitud); Longitud = Convert.ToUInt32(nroBytes)
};
// Extrae encabezado. String con patron soeps~<texto>~ // Extrae encabezado. String con patron soeps~<texto>~
string datos = Encoding.UTF8.GetString(buffer, 0, nroBytes); string datos = Encoding.UTF8.GetString(buffer, 0, nroBytes);
...@@ -407,7 +422,7 @@ namespace EvaPosSCOSrv ...@@ -407,7 +422,7 @@ namespace EvaPosSCOSrv
// Extrae valor campo Session-Id en string. // Extrae valor campo Session-Id en string.
int inicioSessionId = parteEncabezado.IndexOf("Session-Id"); int inicioSessionId = parteEncabezado.IndexOf("Session-Id");
int finSessionId = parteEncabezado.Substring(inicioSessionId).IndexOf("|"); int finSessionId = parteEncabezado[inicioSessionId..].IndexOf("|");
string sessionId = parteEncabezado.Substring(inicioSessionId, finSessionId); string sessionId = parteEncabezado.Substring(inicioSessionId, finSessionId);
string valorSessionId = sessionId.Split('=')[1]; string valorSessionId = sessionId.Split('=')[1];
trama.IdSesion = Int32.Parse(valorSessionId); trama.IdSesion = Int32.Parse(valorSessionId);
...@@ -421,7 +436,7 @@ namespace EvaPosSCOSrv ...@@ -421,7 +436,7 @@ namespace EvaPosSCOSrv
// Extraer contenido. // Extraer contenido.
trama.TextoXML = datos.Substring(inicioXML); trama.TextoXML = datos.Substring(inicioXML);
log.Debug("Mensaje string con contenido: {contenido}", trama.TextoXML); log.Information("{contenido}", trama.TextoXML);
return trama; return trama;
} }
...@@ -431,7 +446,7 @@ namespace EvaPosSCOSrv ...@@ -431,7 +446,7 @@ namespace EvaPosSCOSrv
/// No interpreta string de mensaje. /// No interpreta string de mensaje.
/// <returns>Arreglo bytes con mensaje y cabecera con su longitud en bytes. Mensaje codificado UTF-8.</returns> /// <returns>Arreglo bytes con mensaje y cabecera con su longitud en bytes. Mensaje codificado UTF-8.</returns>
/// </summary> /// </summary>
public byte[] Salida(TramaSCO trama) public static byte[] Salida(TramaSCO trama)
{ {
// Codifica longitud del mensaje en los 4 primeros bytes. // Codifica longitud del mensaje en los 4 primeros bytes.
byte[] bytesConLongMensaje = BitConverter.GetBytes(trama.Longitud); byte[] bytesConLongMensaje = BitConverter.GetBytes(trama.Longitud);
...@@ -447,6 +462,9 @@ namespace EvaPosSCOSrv ...@@ -447,6 +462,9 @@ namespace EvaPosSCOSrv
Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length); Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length);
Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length); Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length);
if (_isDebug) log.Debug("Buffer salida: >>{bytes}<<", bytes); if (_isDebug) log.Debug("Buffer salida: >>{bytes}<<", bytes);
Log.Information("Mensaje {long} bytes", trama.Longitud);
Log.Information(".. encabezado\n{encabezado}", trama.TextoEncabezado);
Log.Information(".. contenido\n{contenido}", trama.TextoXML);
return bytes; return bytes;
} }
} }
...@@ -472,12 +490,8 @@ namespace EvaPosSCOSrv ...@@ -472,12 +490,8 @@ namespace EvaPosSCOSrv
IComando cmd; IComando cmd;
try try
{ {
XmlElement? docXml = mensaje.ContenidoXML.DocumentElement; XmlElement? docXml = mensaje.ContenidoXML.DocumentElement ?? throw new Exception("Contenido XML vacío.");
if (docXml == null) XmlNode? nodoRaiz = docXml.SelectSingleNode(".") ?? throw new Exception("Contenido XML vacío.");
throw new Exception("Contenido XML vacío.");
XmlNode? nodoRaiz = docXml.SelectSingleNode(".");
if (nodoRaiz == null)
throw new Exception("Contenido XML vacío.");
Log.Debug("Mensaje, contenido xml: '{nodoInicial}'", Util.ContenidoXmlComoString(nodoRaiz)); Log.Debug("Mensaje, contenido xml: '{nodoInicial}'", Util.ContenidoXmlComoString(nodoRaiz));
// Según el elemento XML raíz, se determina el comando y dto adecuados. // Según el elemento XML raíz, se determina el comando y dto adecuados.
...@@ -496,7 +510,7 @@ namespace EvaPosSCOSrv ...@@ -496,7 +510,7 @@ namespace EvaPosSCOSrv
catch (Exception e) catch (Exception e)
{ {
Log.Error("Excepción procesando Mensaje XML: {e}", e.Message); Log.Error("Excepción procesando Mensaje XML: {e}", e.Message);
ErrorDTO dto = new ErrorDTO(mensaje.IdSesion, mensaje.TipoMensaje, $"Mensaje XML con valor nulo o no reconocido: '{e.Message}'"); ErrorDTO dto = new(mensaje.IdSesion, mensaje.TipoMensaje, $"Mensaje XML con valor nulo o no reconocido: '{e.Message}'");
cmd = new ErrorCmd(); cmd = new ErrorCmd();
cmd.CargaDTO(dto); cmd.CargaDTO(dto);
} }
......
{ {
"GatewayConfig": { "GatewayConfig": {
"POS": "gk", "POS": "ECO_POSBC",
"POS_comment": "Indicates the set of commands to instantiate, according to the type of POS: evapos, tests, gk, etc.", "POS_comment": "Indicates the set of commands to instantiate, according to the type of POS: evapos, tests, gk, etc.",
"IpSCO": "127.0.0.1", "IpGateway": "127.0.0.1",
"IpSCO_comment": "SCO IP, local or remote", "IpGateway_comment": "Gateway IP, local or remote",
"PortSCO": 6697, "PortGateway": 6697,
"PortSCO_comment": "SCO IP Port", "PortGateway_comment": "Gateway IP Port",
"Language": "es", "Language": "es",
"Language_comment": "Language code as needed by the POS application" "Language_comment": "Language code as needed by the POS application"
}, },
"POSBC": {
"IpPOSBC": "127.0.0.1",
"PortPOSBC": 6698
},
"DataGK": { "DataGK": {
"IpGkSmartPOS": "10.10.117.10", "IpGkSmartPOS": "10.10.117.10",
"IpGkSmartPOS_comment": "SCO IP, local or remote", "IpGkSmartPOS_comment": "SCO IP, local or remote",
...@@ -29,7 +33,7 @@ ...@@ -29,7 +33,7 @@
"Serilog.Sinks.Console", "Serilog.Sinks.Console",
"Serilog.Sinks.File" "Serilog.Sinks.File"
], ],
"MinimumLevel": "Verbose", "MinimumLevel": "Debug",
"WriteTo": [ "WriteTo": [
{ {
"Name": "Console", "Name": "Console",
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<WarningLevel>1</WarningLevel> <WarningLevel>1</WarningLevel>
......
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace gatewayPruebaECO;
// Cliente de pruebas del gateway, remite mensajes, espera respuesta.
public class SocketClientECO
{
private const int Port = 6697; // Puerto del servidor gateway
private const string Server = "127.0.0.1"; // Dirección IP del servidor
// Crear un socket
static Socket socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static int numeroRespuestas = 0;
public static void Main()
{
try
{
// Conectarse al servidor
socket.Connect(new IPEndPoint(IPAddress.Parse(Server), Port));
Console.WriteLine("Conectado al servidor.");
string mensaje1 = """
soeps~Message-Type=REQ|Session-Id=400|~<?xml version="1.0" encoding="UTF-8"?><scsns:Initialize xmlns:scsns="http://bc.si.retail.ibm.com/POSBCSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://bc.si.retail.ibm.com/POSBCSchema C:\PosBc\POSBCSchema_main.xsd">
<InitializeRequest>
<OperatorID>NO_DEFAULT</OperatorID>
<TerminalNumber>400</TerminalNumber>
<Recovery>false</Recovery>
</InitializeRequest>
</scsns:Initialize>
""";
Mensaje(mensaje1);
Respuesta();
string mensaje2 = """
soeps~Message-Type=REQ|Session-Id=400|~<?xml version="1.0" encoding="UTF-8"?><scsns:Initialize xmlns:scsns="http://bc.si.retail.ibm.com/POSBCSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://bc.si.retail.ibm.com/POSBCSchema C:\PosBc\POSBCSchema_main.xsd">
<InitializeRequest>
<OperatorID>NO_DEFAULT</OperatorID>
<TerminalNumber>800</TerminalNumber>
<Recovery>false</Recovery>
</InitializeRequest>
</scsns:Initialize>
""";
Mensaje(mensaje2);
Respuesta();
string mensaje3 = """
soeps~Message-Type=REQ|Session-Id=400|~<?xml version="1.0" encoding="UTF-8"?><scsns:Initialize xmlns:scsns="http://bc.si.retail.ibm.com/POSBCSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://bc.si.retail.ibm.com/POSBCSchema C:\PosBc\POSBCSchema_main.xsd">
<InitializeRequest>
<OperatorID>NO_DEFAULT</OperatorID>
<TerminalNumber>900</TerminalNumber>
<Recovery>false</Recovery>
</InitializeRequest>
</scsns:Initialize>
""";
Mensaje(mensaje3);
Respuesta();
// Cerrar el socket
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
catch (SocketException e)
{
Console.WriteLine($"Error de socket: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
public static void Mensaje(string mensaje)
{
// Enviar un mensaje al servidor
socket.Send(Salida(mensaje));
Console.WriteLine($"Mensaje #1 enviado: {mensaje}");
}
public static void Respuesta()
{
// Recibir la respuesta del servidor
byte[] buffer = new byte[4096];
int bytesRead = socket.Receive(buffer);
numeroRespuestas++;
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Respuesta #{numeroRespuestas} recibida: {response}");
}
public static byte[] Salida(string trama)
{
// Codifica longitud del mensaje en los 4 primeros bytes.
var longitud = Convert.ToUInt32(trama.Length);
byte[] bytesConLongMensaje = BitConverter.GetBytes(longitud);
// Bytes mas significativos deben ir primero, usa 'big-endian'.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesConLongMensaje);
// Codifica en bytes texto del mensaje.
byte[] bytesConMensaje = Encoding.UTF8.GetBytes(trama);
// Copia los 2 arreglos de bytes en un arreglo unificado.
byte[] bytes = new byte[bytesConLongMensaje.Length + bytesConMensaje.Length];
Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length);
Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length);
return bytes;
}
}
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\gatewayGK\gatewayGK.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace gatewayPruebasECO_POSBC;
// Activa servidor para pruebas, acepta mensajes que retorna identicos.
public class SocketServer
{
private const int Port = 6698; // Puerto en el que el servidor escuchará
public static void Main()
{
Socket? listener = null;
try
{
// Crear un socket de escucha
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, Port));
listener.Listen(10);
Console.WriteLine($"Servidor ECO_POSBC escuchando en el puerto {Port}...");
// Aceptar una conexión de cliente
using Socket clientSocket = listener.Accept();
Console.WriteLine("Cliente conectado.");
while (true)
{
Console.WriteLine("..esperando mensaje.");
// Recibir el mensaje del cliente
byte[] buffer = new byte[4096];
int bytesRead = clientSocket.Receive(buffer);
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Mensaje recibido: {message}");
// Enviar el mismo mensaje de vuelta al cliente
clientSocket.Send(buffer, bytesRead, SocketFlags.None);
Console.WriteLine("Mensaje enviado de vuelta al cliente.");
}
}
catch (SocketException e)
{
Console.WriteLine($"Error de socket: {e.Message}");
}
finally
{
// Cerrar el socket de escucha
listener?.Close();
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\gatewayGK\gatewayGK.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment