¿Cuál es la forma correcta de cerrar un C# Socket en .NET Core 3.1?
La cuestión
Estoy tratando de manejar la desconexión de mi aplicación y cualquier enfoque que haya intentado hasta ahora ha fallado, traté de desconectarme del lado del servidor, traté de desconectarme del lado del cliente pero recibí problemas en ambos extremos, lo que estoy tratando de lograr aquí es para desconectar la aplicación a través de un comando SALIR sin tener que cerrarla desde el ícono de cerrar ya que arroja una excepción
cliente.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace MessengerConsole
{
class Client
{
static string username;
static int port = 8888;
static IPAddress clientIP;
static Socket serverSocket;
static Thread processThread;
static bool connected = false;
static string GetIp()
{
Console.WriteLine("Type the server ip:");
String clientIP = Console.ReadLine();
Console.WriteLine("Client IP: " + clientIP);
//return clientIP;
//temp solution
return "192.168.0.106";
}
static void printSession()
{
Console.Clear();
Console.WriteLine("//=====================================================");
Console.WriteLine("// Session Details ");
Console.WriteLine("// =====================");
Console.WriteLine("// IP: " + clientIP + "\n// Time: " + DateTime.Now);
Console.WriteLine("//=====================================================");
}
static void clientReceiver()
{
while (true)
{
Thread.Sleep(500);
byte[] buffer = new byte[300];
int rece = serverSocket.Receive(buffer, 0, buffer.Length, 0);
Array.Resize(ref buffer, rece);
if (connected == false)
{
Console.WriteLine("[" + DateTime.Now.ToString() + "] " + Encoding.Default.GetString(buffer) + " Connected!");
connected = true;
}
else
{
if (Encoding.Default.GetString(buffer) == "QUIT")
{
//Quit
Console.WriteLine("Server Shutdown");
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Close();
}
else
{
Console.WriteLine("[" + DateTime.Now.ToString() + "] " + Encoding.Default.GetString(buffer));
}
}
}
}
public static void StartClient()
{
processThread = new Thread(clientReceiver);
Console.WriteLine("Please enter your name");
username = Console.ReadLine();
clientIP = IPAddress.Parse(GetIp()); //Returns IP from GetIP()
Console.WriteLine("Please enter HostPort");
//string portString = Console.ReadLine();
//temp port
string portString = "80";
try
{
port = Convert.ToInt32(portString);
}
catch
{
port = 8888;
}
try
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Connect(new IPEndPoint(clientIP, port));
processThread.Start();
byte[] name = Encoding.Default.GetBytes(username);
//Send Name
serverSocket.Send(name);
// byte[] data = Encoding.Default.GetBytes("<" + username + "> Connected");
//serverSocket.Send(data, 0, data.Length, 0);
printSession();
while (serverSocket.Connected)
{
//byte[] sdata = Encoding.Default.GetBytes("<" + username + ">" + Console.ReadLine());
byte[] sdata = Encoding.Default.GetBytes(Console.ReadLine());
if(Encoding.Default.GetString(sdata) == "QUIT")
{
serverSocket.Send(sdata, sdata.Length, 0);
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Disconnect(true);
serverSocket.Close();
}
else
{
serverSocket.Send(sdata, 0, sdata.Length, 0);
}
}
}
catch (Exception e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
Console.ForegroundColor = ConsoleColor.White;
}
}
}
}
servidor.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace MessengerConsole
{
class Server
{
//Server Socket
static Socket serverSocket;
//Client Socket
static Socket clientSocket;
//Other Variables
static int port = 8888;
static IPAddress serverIP;
static Thread processThread;
static string username;
static bool connected = false;
//Function returns IP Address
static string GetIp()
{
//Computer Name
string hostname = Dns.GetHostName();
/*The IPHostEntry class associates a Domain Name System (DNS) host name with an array of aliases and
* an array of matching IP addresses.
*/
IPHostEntry ipentry = Dns.GetHostEntry(hostname);
//The Address
IPAddress[] ipAddress = ipentry.AddressList;
return ipAddress[ipAddress.Length - 1].ToString();
}
static void printSession()
{
Console.Clear();
Console.WriteLine("//=====================================================");
Console.WriteLine("// Session Details ");
Console.WriteLine("// =====================");
Console.WriteLine("// You are hosting the server");
Console.WriteLine("// Time: " + DateTime.Now);
Console.WriteLine("//=====================================================");
}
//Receive
static void serverReciever()
{
while (true)
{
Thread.Sleep(500);
byte[] buffer = new byte[300];
int rece = clientSocket.Receive(buffer, 0, buffer.Length, 0);
Array.Resize(ref buffer, rece);
if (connected == false)
{
Console.WriteLine("[" + DateTime.Now.ToString() + "] " + Encoding.Default.GetString(buffer) + " Connected!");
connected = true;
}
else
{
if(Encoding.Default.GetString(buffer) == "QUIT")
{
//Quit
Console.WriteLine("Client disconnected from the chat");
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
else
{
Console.WriteLine("[" + DateTime.Now.ToString() + "] " + Encoding.Default.GetString(buffer));
}
}
}
}
public static void StartServer()
{
//Thread
processThread = new Thread(serverReciever);
//Display
Console.WriteLine("Your Local Ip is " + GetIp());
Console.WriteLine("Please enter your name");
username = Console.ReadLine();
Console.WriteLine("Please enter HostPort");
// string portString = Console.ReadLine();
//temp solution
string portString = "80";
try
{
port = Convert.ToInt32(portString);
}
catch
{
port = 8888;
}
try
{
//GetIp returns string
serverIP = IPAddress.Parse(GetIp());
//TCP Socket
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(serverIP, port));
serverSocket.Listen(0);
//Server Socket listening for client requests
clientSocket = serverSocket.Accept();
printSession();
processThread.Start();
byte[] name = Encoding.Default.GetBytes(username);
clientSocket.Send(name);
while (true)
{
byte[] sdata = Encoding.Default.GetBytes(Console.ReadLine());
clientSocket.Send(sdata, 0, sdata.Length, 0);
}
}
catch
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Server already open!");
Console.ForegroundColor = ConsoleColor.White;
}
}
}
}
este es el error que he recibido
Excepción no controlada. System.ObjectDisposedException: no se puede acceder a un objeto desechado. Nombre del objeto: 'System.Net.Sockets.Socket'. en System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode) en System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) en MessengerConsole.Client.clientReceiver() en C:\Users\MessengerConsoleAppV2\MessengerConsole\Client.cs:línea 42 en System.Threading.ThreadHelper.ThreadStart_Context(Estado del objeto) en System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext , devolución de llamada de ContextCallback, estado del objeto) --- Fin del seguimiento de la pila desde la ubicación anterior donde se lanzó la excepción --- en System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, devolución de llamada de ContextCallback,
Intenté usar Socket.Disconnect() , Socket.Close(), Socket.Dispose() y todos dieron el mismo resultado
EDITAR:
agregando return
en el
if(Encoding.Default.GetString(sdata) == "QUIT")
{
serverSocket.Send(sdata, sdata.Length, 0);
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Close();
return;
}
No da como resultado ningún error en el lado del servidor, pero el cliente aún arroja la excepción del acceso al objeto desechado
Unhandled exception. System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.Socket'. at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode) at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) at MessengerConsole.Client.clientReceiver() in C:\Users\MessengerConsoleAppV2\MessengerConsole\Client.cs:line 44 at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown --- at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
Respuestas
En todos los lugares del código relacionado con QUIT
usted, solo cierra y desecha el socket, pero no regresa del bucle infinito que sirve a este socket. Creo que esta pila se lanza en la próxima iteración cuando Connected
la propiedad se evalúa en el socket desechado, intente agregar return
una declaración después de cerrar el socket.