Guten Morgen, ich habe mir eine kleine TCP Klassenbibliothek erstellt, leider bekomme ich bei dem lesen des Streams folgende Exception:
System.IO.IOException: "Von der Übertragungsverbindung können keine Daten gelesen werden: Ein Verbindungsversuch ist fehlgeschlagen, da die Gegenstelle nach einer bestimmten Zeitspanne nicht richtig reagiert hat, oder die hergestellte Verbindung war fehlerhaft, da der verbundene Host nicht reagiert hat."
Könnt Ihr mir einen Tipp geben, wo mein Problem liegt?
Danke im voraus.
Server.cs:
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace TCP
{
public class Server
{
public event MessageRecived NewMessage;
public delegate void MessageRecived(string Message);
bool _running;
TcpListener Listener;
public bool Running
{
get
{
return _running;
}
set
{
_running = value;
}
}
public Server(int Port)
{
Listener = new TcpListener(IPAddress.Any, Port);
}
public async void Start()
{
try
{
Listener.Start();
Running = true;
while(Running)
{
var client = await Listener.AcceptTcpClientAsync();
Task.Run(() => HandleClient(client));
}
}
catch(SocketException)
{
throw;
}
}
public void Stop()
{
Listener.Stop();
Running = false;
}
private void HandleClient(TcpClient Client)
{
NetworkStream Stream = Client.GetStream();
while(Client.Connected)
{
byte[] msg = new byte[1024];
Stream.ReadTimeout = 250;
Stream.Read(msg, 0, msg.Length);
String message = Encoding.UTF8.GetString(msg);
NewMessage?.Invoke(message);
}
}
}
}
Client.cs:
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace TCP
{
public class Client
{
TcpClient client;
IPAddress ip;
int port;
public Client(IPAddress IP, int Port)
{
this.ip = IP;
this.port = Port;
client = new TcpClient();
}
public bool SendMessage(string Message)
{
bool succsess = false;
if (!client.Connected)
connect();
if(client.Connected)
{
NetworkStream stream = client.GetStream();
byte[] send = Encoding.UTF8.GetBytes(Message);
stream.Write(send, 0, 1024);
stream.Flush();
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
succsess = true;
}
return succsess;
}
void connect()
{
client.ConnectAsync(ip, port);
}
}
}
Im Server liest du nur aus dem Stream während dein Client per Read aber auf eine Antwort wartet.
Entsprechend müsste dein Server selbst in den Stream schreiben, sonst wartet der Client bis zum Timeout auf eine Antwort.
T-Virus
Developer, Developer, Developer, Developer....
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
Danke für die schnelle Antwort.
Ich versuche nun die empfangenen Daten direkt wieder an den Client zu senden. Leider tritt der Fehler schon beim lesen des Streams im Server auf.
Wenn ich keinen ReadTimeout definiere wird das Stream.Read nicht beendet. Fehlt hier vielleicht ein Zeichen, dass dem Read Befehl anzeigt, dass der Stream zu ende ist?
Die häufigste Ursache bei der IOException (mal abgesehen, dass Client etc nicht gestartet ist..) beim Verbindungsaufbau ist die Firewall. Geht die Connection durch eine Firewall, hast entsprechend die Incoming (Server) und Outgoing (Client) Regeln gesetzt?
Entsprechend viele Treffer findet man bei der Suche
=> https://mycsharp.de/forum/threads/95484/tcplistener-an-established-connection-was-aborted-by-the-software-in-your-host-machine
=> https://mycsharp.de/forum/threads/15768/faq-tcpclient-einfaches-beispiel
=> https://mycsharp.de/forum/threads/19670/tutorial-client-server-komponente-ueber-tcp-sockets
=> https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/sockets/tcp-classes
PS: Du solltest async/await korrekt umsetzen, so ist das nix. Das kann und wird ansonsten weitere (nicht dieser) Fehler verursachen.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke für die Links, ich habe heute morgen in der Forensuche immer "Kein Ergebnis" angezeigt bekommen.
Ich versuche momentan alles Lokal auf dem Rechner über die 127.0.0.1. Firewall sollte dann normal ja kein Thema sein, oder?
Zur Thematik async/await, ich werde den Task wohl besser in einem eigenen Thread umsetzen, da ich beim empfangen ein Event auslösen will und nicht auf eine Rückgabe der Methode warten will. Somit würde await rausfallen, da die Abfrage nach neuen Nachrichten ja kontinuierlich in der while Schleife läuft und nicht beendet wird.
Ich hoffe Ihr reißt euch jetzt nicht die Haare wegen mir aus 😄
Danke für die Links, ich habe heute morgen in der Forensuche immer "Kein Ergebnis" angezeigt bekommen.
Danke für den Hinweis, vielleicht hab ich neulich was kaputt-optimiert...
Zur Thematik async/await, ich werde den Task wohl besser in einem eigenen Thread umsetzen, da ich beim empfangen ein Event auslösen will und nicht auf eine Rückgabe der Methode warten will. Somit würde await rausfallen, da die Abfrage nach neuen Nachrichten ja kontinuierlich in der while Schleife läuft und nicht beendet wird.
Ich seh jetzt weiterhin kein Grund, dass Du den Thread brauchst (ohne keinen ganzen Kontext zu kennen) - außer Du willst das Gesamtkonstrukut auslagern; und auch das geht simpler mit einem Long-Running Task.
Aber trotzdem hast Du ein asynchrones Verhalten, zB allein durch AcceptTcpClientAsync. Und dann hast nen extra Task, mit dem Du asynchron arbeiten kannst.
Ich versuche momentan alles Lokal auf dem Rechner über die 127.0.0.1. Firewall sollte dann normal ja kein Thema sein, oder?
In Deinem Code arbeitest Du aber mit IPAddress.Any und nicht mit IPAddress.Loopback - und nur Loopback geht nicht über die Firewall. Any schon. Any ist nicht 127.0.0.1 sondern "*", also alles.
Verwendest vielleicht ein Port, den Du nicht verwenden darfst?Sowas wie 5000 is fine, alles unter 1024 ist reserviert / blockiert.
Ich hab mir dazu kurz zwei Demo Konsolen gebaut:
Server
int port = 5000;
IPAddress localAddr = IPAddress.Loopback;
TcpListener server = new(localAddr, port);
try
{
server.Start();
Console.WriteLine($"Server gestartet und hört auf {localAddr}:{port}...");
while (true)
{
Console.WriteLine("Warte auf eine Verbindung...");
TcpClient client = await server.AcceptTcpClientAsync();
Console.WriteLine("Ein Client wurde verbunden.");
// Lese Daten vom Client
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string dataReceived = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Empfangen: {dataReceived}");
// Antwort zurück an den Client senden
string response = "Hallo, Client!";
byte[] responseData = Encoding.UTF8.GetBytes(response);
await stream.WriteAsync(responseData, 0, responseData.Length);
Console.WriteLine("Antwort gesendet.");
// Verbindung schließen
client.Close();
}
}
catch (Exception e)
{
Console.WriteLine($"Fehler: {e.Message}");
}
finally
{
server.Stop();
}
Client
IPAddress server = IPAddress.Loopback;
int port = 5000;
try
{
TcpClient client = new(server.ToString(), port);
Console.WriteLine($"Verbunden mit dem Server auf {server}:{port}");
// Nachricht an den Server senden
string message = "Hallo, Server!";
byte[] data = Encoding.UTF8.GetBytes(message);
NetworkStream stream = client.GetStream();
await stream.WriteAsync(data, 0, data.Length);
Console.WriteLine($"Gesendet: {message}");
// Antwort vom Server lesen
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Antwort vom Server: {response}");
// Verbindung schließen
client.Close();
}
catch (Exception e)
{
Console.WriteLine($"Fehler: {e.Message}");
}
Output:
// Server
Server gestartet und hört auf 127.0.0.1:5000...
Warte auf eine Verbindung...
Ein Client wurde verbunden.
Empfangen: Hallo, Server!
Antwort gesendet.
Warte auf eine Verbindung...
// Client
Verbunden mit dem Server auf 127.0.0.1:5000
Gesendet: Hallo, Server!
Antwort vom Server: Hallo, Client!
PS: TCP Handling als Lernaufgabe ist gut; man sollte wissen, wie das alles funktioniert. Soll das jedoch ne produktive Sache werden, dann sollte man nicht das Rad neu erfinden und auf sowas wie HTTP APIs oder gRPC setzen.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke für die vielen Hilfen und Denkansätze.
Ich habe meinen eigentlichen Fehler gefunden.
Bei dem Client habe ich zum senden eine fixe Byte Länge angegeben
Ist:
stream.Write(send, 0, 1024);
Soll:
stream.Write(send, 0, send.Length);