Quelle est la bonne façon de fermer un socket C# dans .NET Core 3.1 ?
Le problème
J'essaie de gérer la déconnexion de mon application et quelle que soit l'approche que j'ai essayée jusqu'à présent, j'ai essayé de me déconnecter du côté serveur, j'ai essayé de me déconnecter du côté client mais j'ai des problèmes aux deux extrémités, ce que j'essaie de réaliser ici est pour déconnecter l'application via une commande QUIT sans avoir à la fermer à partir de l'icône de fermeture car elle lève une exception
client.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;
}
}
}
}
serveur.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;
}
}
}
}
C'est l'erreur que j'ai reçu
Exception non-gérée. System.ObjectDisposedException : Impossible d'accéder à un objet supprimé. Nom de l'objet : 'System.Net.Sockets.Socket'. à System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode) à System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) à MessengerConsole.Client.clientReceiver() dans C:\Users\MessengerConsoleAppV2\MessengerConsole\Client.cs:line 42 à System.Threading.ThreadHelper.ThreadStart_Context(Object state) à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext , rappel ContextCallback, état de l'objet) --- Fin de la trace de la pile à partir de l'emplacement précédent où l'exception a été levée --- à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback,
J'ai essayé d'utiliser Socket.Disconnect() , Socket.Close(), Socket.Dispose() et tous donnent le même résultat
ÉDITER:
ajoutant return
dans le
if(Encoding.Default.GetString(sdata) == "QUIT")
{
serverSocket.Send(sdata, sdata.Length, 0);
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Close();
return;
}
Ne génère aucune erreur côté serveur, mais le client lève toujours l'exception de l'objet supprimé en cours d'accès
Exception non-gérée. System.ObjectDisposedException : Impossible d'accéder à un objet supprimé. Nom de l'objet : 'System.Net.Sockets.Socket'. à System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode) à System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) à MessengerConsole.Client.clientReceiver() dans C:\Users\MessengerConsoleAppV2\MessengerConsole\Client.cs:line 44 à System.Threading.ThreadHelper.ThreadStart_Context(Object state) à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext , rappel ContextCallback, état de l'objet) --- Fin de la trace de la pile à partir de l'emplacement précédent où l'exception a été levée --- à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback,
Réponses
Dans tous les endroits de code liés à QUIT
vous, fermez et supprimez uniquement le socket, mais ne revenez pas de la boucle infinie desservant ce socket. Je pense que cette pile est lancée à la prochaine itération lorsque Connected
la propriété est évaluée sur le socket supprimé, essayez d'ajouter une return
instruction après avoir fermé le socket.