我正在尝试用 C# 设置套接字服务器。一切正常,直到突然显示错误:System.Net.Sockets.SocketException (995): I/O 操作已因线程退出或应用程序请求而中止。
我尝试了一些在堆栈溢出上找到的解决方案,但它不起作用。我也尝试在我的机器上禁用防火墙,但它仍然不起作用。这个错误突然发生,当我恢复到以前可以工作的旧版本时,该错误仍然显示。
这是我使用 dotnet 6.0 的服务器代码:
using System;
using System.Text;
using System.Net.Sockets;
using System.IO;
using Newtonsoft.Json;
using System.Security.Cryptography.X509Certificates;
using System.Net.Http;
using System.Net;
namespace PokemonWebSocketServer
{
internal class Server
{
public static List<Client>? clients = new();
public static List<Room>? rooms = new();
public static ClientRequest clientRequest;
private static async Task Main(string[] args)
{
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, 587);
listener.Start();
Console.WriteLine("Server has started !");
Console.WriteLine("Waiting for clients to connect");
while (true)
{
using (TcpClient tcpClient = await listener.AcceptTcpClientAsync())
{
_ = HandleClientAsync(tcpClient);
}
}
}
private static async Task HandleClientAsync(TcpClient tcpClient)
{
using NetworkStream networkStream = tcpClient.GetStream();
using StreamReader reader = new(networkStream);
using StreamWriter writer = new(networkStream);
try
{
byte[] buffer = new byte[15];
int bytesRead = await networkStream.ReadAsync(buffer, 0, buffer.Length);
string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
clientRequest = Enum.Parse<ClientRequest>(receivedData);
if (clientRequest == ClientRequest.Create)
{
await CreateRoom(writer, networkStream, tcpClient);
}
else if (clientRequest == ClientRequest.Join)
{
await JoinRoom(writer, networkStream, tcpClient);
}
}
catch (System.IO.IOException e)
{
Console.WriteLine("Something went wrong !" + e);
}
}
程序会抛出错误:
int bytesRead = await networkStream.ReadAsync(buffer, 0, buffer.Length);
这是我使用 dotnet 6.0 的客户端代码:
using System.Net.Sockets;
using System.Text;
using Newtonsoft.Json;
namespace PokemonGame
{
internal class OnlinePokeBattle
{
public static byte[]? sendData;
public static string? response;
internal static void MakeConnection()
{
try
{
TcpClient client = new TcpClient("127.0.0.1", 587);
StreamReader streamReader = new(client.GetStream());
if(client.Connected)
{
Console.Clear();
Console.WriteLine(AcsiiArt.MainMenuArt,"\n\n");
string prompt = "Would you like to join or create a room ?";
string[] options = {"Join", "Create", "Back"};
Console.WriteLine("\n\n\n");
Menu menu = new(prompt, options);
int MenuInput = menu.Run(false);
if(MenuInput == 0)
{
JoinRoom(client, streamReader);
}
else if(MenuInput == 1)
{
CreateRoom(client, streamReader);
}
if(MenuInput == 3)
{
Console.Clear();
return;
}
}
else
{
Console.WriteLine("The server is currently not available.");
Thread.Sleep(2000);
return;
}
}
catch(IOException e)
{
Console.WriteLine("The connection to server was lost ! \n" + e);
return;
}
catch(Exception e)
{
Console.WriteLine("An error occured while trying to connect to the server !" + e);
return;
}
}
这是错误:
!System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
客户端连接到服务器时就会出现错误。
请帮助我
预期的结果是服务器等待客户端发送字符串“Join”或“Create”,然后程序相应地继续。
我已经找到问题的答案了。总而言之,这是由我的服务器中的这段代码引起的:
while (true)
{
using (TcpClient tcpClient = await listener.AcceptTcpClientAsync())
{
_ = HandleClientAsync(tcpClient);
}
}
将此代码更改为此代码有效:
while (true)
{
TcpClient tcpClient = await listener.AcceptTcpClientAsync();
_ = HandleClientAsync(tcpClient);
}
为了进一步详细说明,如果有人感兴趣,我会尽力解释为什么第一个不起作用(请记住我是一名初学者程序员)。
我试图实现的是允许多个客户端同时连接到我的服务器。但是,当我的服务器代码使用等待时,例如
while (true)
{
TcpClient tcpClient = await listener.AcceptTcpClientAsync();
await HandleClientAsync(tcpClient);
}
发生的情况是,第一个客户端将被连接,并且程序将继续第一个客户端,但是,对于第二个客户端,程序将暂停,直到第一个客户端完成。所以我知道我必须异步做一些事情,而不需要“等待”。所以我将代码更改为
while (true)
{
TcpClient tcpClient = await listener.AcceptTcpClientAsync();
_ = HandleClientAsync(tcpClient);
}
这很有效。 2 个客户端能够同时连接。但是,当我查看代码时,我认为使用“using”关键字是有益的,因为“using”关键字将在“using”块完成后释放所有资源。当我将代码更改为
后 while (true)
{
using (TcpClient tcpClient = await listener.AcceptTcpClientAsync())
{
_ = HandleClientAsync(tcpClient);
}
}
(我应该对我的代码进行另一次测试,但我很愚蠢没有这样做),然后我继续前进,那就是错误发生的时候。我很快恢复了所有新更改,直到使用“using”关键字(因为我认为这不会成为问题)。
问题是由“using”关键字引起的。在“using”语句之后,它转移到另一个异步函数
_ = HandleClientAsync(tcpClient);
在这个函数中,它还有另一个异步函数
'await networkStream.ReadAsync(buffer, 0, buffer.Length);'
但是,程序不是等待 networkstream.ReadAsync,而是异步线程完成并返回到 while 循环,从而释放 tcpClient 的资源。这就是它抛出错误的原因
!System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
但在这种情况下,这是由线程退出引起的。当networksteam尝试读取数据时,已经没有tcpClient了,因为程序已经释放了tcpClient的所有资源。