Mein TCPServer Versuch

  • C#
  • .NET 4.5

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Radinator.

    Mein TCPServer Versuch

    Hallo zusammen,

    ich war mal entschlossen einen TCPServer aufzusetzen. Problem nur ich hab kaum ahnung von dem Thema :D
    Belesen hab ich mich zumal hier im Forum ("TuT") nur hat mir das nicht viel geholfen um das Thema zu verstehen :(
    Weiter hab ich mich Hier schlau gemacht weiter Hier nur nützt das einen nicht, wenn er es immer noch nicht verstanden hat.
    Somit habe ich mich an dem TUT lang geangelt und hab nun das, dazu muss ich sagen, wahrscheinlich nicht die beste Methode.
    #ConsolenApplication

    Frage ist nun, wie kann ich es verbessern bzw. wie kann ich jetzt den Benutzern jetzt eine eindeutige id geben Oo?
    #Wahrscheinlich_bad_practice

    Implentierung

    C#-Quellcode

    1. private static ServerTcpIp server;
    2. static void Main(string[] args)
    3. {
    4. server = new ServerTcpIp(IPAddress.Any, 4000);
    5. server.GetMessageFromClient += new ServerTcpIp.ClientMessage(ClientMessage);
    6. Console.ReadKey();
    7. }
    8. private static void ClientMessage(TcpClient e,string msg)
    9. {
    10. Console.WriteLine(msg);
    11. }


    C#-Quellcode

    1. using System.Collections.Generic;
    2. using System.IO;
    3. using System.Net;
    4. using System.Net.Sockets;
    5. using System.Threading.Tasks;
    6. namespace ServerManager
    7. {
    8. public class ServerTcpIp
    9. {
    10. private List<ClienManager> clients;
    11. private TcpListener server;
    12. private IPEndPoint ipendpoint;
    13. public event ClientMessage GetMessageFromClient;
    14. public delegate void ClientMessage(TcpClient e ,string Message);
    15. public ServerTcpIp(IPAddress adress,int port)
    16. {
    17. clients = new List<ClienManager>();
    18. ipendpoint = new IPEndPoint(adress, port);
    19. }
    20. private void Start()
    21. {
    22. Task.Run(() => StartServer()).Start();
    23. void StartServer()
    24. {
    25. server = new TcpListener(ipendpoint);
    26. server.Start();
    27. while (true)
    28. {
    29. TcpClient client = server.AcceptTcpClient();
    30. ClienManager cm = new ClienManager(client);
    31. cm.SendToServerMsg += ReadClientMessage;
    32. clients.Add(cm);
    33. void ReadClientMessage(TcpClient e,string msg){
    34. GetMessageFromClient(e, msg);
    35. }
    36. }
    37. }
    38. }
    39. protected class ClienManager
    40. {
    41. private StreamWriter StreamWriter;
    42. private StreamReader StreamReader;
    43. private TcpClient Client;
    44. public delegate void SendMessageToServer(TcpClient e, string msg);
    45. public event SendMessageToServer SendToServerMsg;
    46. public ClienManager(TcpClient client)
    47. {
    48. Client = client;
    49. NetworkStream ns = Client.GetStream();
    50. StreamWriter = new StreamWriter(ns);
    51. StreamReader = new StreamReader(ns);
    52. Task.Run(() => ReadMessage()).Start();
    53. void ReadMessage()
    54. {
    55. while (true)
    56. {
    57. string msg = StreamReader.ReadToEnd();
    58. SendToServerMsg(Client, msg);
    59. }
    60. }
    61. }
    62. private async void SendMessageAsync(string Message)
    63. {
    64. await StreamWriter.WriteAsync(Message);
    65. }
    66. }
    67. }
    68. }
    @Facebamm Es ist nicht ganz sinnvoll, solo einen Server zu bauen.
    Gleichzeitig solltest Du einen Client dazu bauen, um die einzelnen Server- und Client-Funktionen zu testen.
    In der MSDN gibt es da gepaarte Beispiele für.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    Facebamm schrieb:

    Frage ist nun, wie kann ich es verbessern
    Das könntest du tun (damit erleichterst du dir viel umcodieren, wenn du das ganze mal releasen willst und vor allem musst du dich dann nicht von der Schreibweise her umgewöhnen.

    Facebamm schrieb:

    GetMessageFromClient

    1.) Ist kein guter Name für ein Event. Besser wäre hier MessageFromClient
    2.) Brauchst du nicht erst einen delegate void ClientMessage(TcpClient e, string Message) und zusätzlich einen public event ClientMessage GetMessageFromClient. Das ist doppelt gemoppelt. Ist nur unnötiger Code.
    3.) Wenn du schon einen Eventhandler erstellt, dann nimm bitte immer die Signatur
    - [Zugriffsart] delegate void [Eventname](object sender, [Entweder Eventargs oder eine Ableitung davon] e) oder
    - [Zugriffsart] event EventHandler [Eventname] bzw das ganze halt generisch mit
    - [Zugriffsart] event EventHandler< [Entweder Eventargs oder eine Ableitung davon]> [Eventname]

    Facebamm schrieb:

    public delegate void ClientMessage(TcpClient e ,string Message);
    Wie gerade: Events haben immer die Signatur: public delegate void ClientMessage(object sender, EventArgs e); (bei EventArgs kann wie gesagt auch eine Ableitung verwendet werden, die von EventArgs erbt und als Property einen String enthält.

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Hallo,
    nun hab ich es Überarbeitet und bin auf das nächste Problem gestoßen hinsichtlich dem Client.
    Die Ip muss doch die Ip sein, die ich per wieistmeineip.de abfrage, richtig? Oo

    Würde wieder um Tipps zur Code-Verbesserung bitten :D

    ... einen Client gebaut (Wirklich simpel) @RodFromGermany

    C#-Quellcode

    1. namespace Client
    2. {
    3. //Test Client
    4. class Program
    5. {
    6. private static string IpAdresse = "88.65.143.174";
    7. private static int Port = 4000;
    8. static void Main(string[] args)
    9. {
    10. TcpClient client = new TcpClient();
    11. client.Connect(IpAdresse, Port);
    12. if (client.Connected)
    13. Console.WriteLine("Conneced");
    14. else
    15. Console.WriteLine($"Verbidnung konnte nicht mit {IpAdresse} aufgebaut werden!");
    16. Console.ReadKey();
    17. }
    18. }
    19. }



    Weiterhin hab ich das mit dem Events umgesetzt, ich hoffe ich hab das richtig Verstanden @Radinator

    C#-Quellcode

    1. namespace ServerManager
    2. {
    3. public class ServerTcpIp
    4. {
    5. private class ClientUser
    6. {
    7. private StreamWriter StreamWriter;
    8. public IPAddress IP { private set; get; }
    9. public event EventHandler ClientUserMessage;
    10. /// <summary>
    11. /// Handel diffrenz User
    12. /// </summary>
    13. /// <param name="client">Joining User</param>
    14. public ClientUser(TcpClient client)
    15. {
    16. IPEndPoint ipep = (IPEndPoint)client.Client.RemoteEndPoint;
    17. IP = ipep.Address;
    18. Console.WriteLine($"User: {IP.ToString()} has join");
    19. NetworkStream ns = client.GetStream();
    20. StreamWriter = new StreamWriter(ns);
    21. StreamReader sr = new StreamReader(ns);
    22. Task.Run(() => ReadMessage()).Start();
    23. void ReadMessage()
    24. {
    25. while (true)
    26. ClientUserMessage(this, new MessageContainer(sr.ReadToEnd()));
    27. }
    28. }
    29. public void SendMessageToClinte(string value)
    30. {
    31. StreamWriter.WriteLine(value);
    32. StreamWriter.Flush();
    33. }
    34. }
    35. public class MessageContainer : EventArgs
    36. {
    37. public string Message { get; }
    38. public MessageContainer(string message)
    39. {
    40. Message = message;
    41. }
    42. }
    43. private List<ClientUser> Clients;
    44. private TcpListener Server;
    45. private IPEndPoint IPEndPoint;
    46. public event EventHandler ClientMessage;
    47. public ServerTcpIp(IPAddress adress, int port)
    48. {
    49. Clients = new List<ClientUser>();
    50. IPEndPoint = new IPEndPoint(adress, port);
    51. }
    52. /// <summary>
    53. /// Startet den Server und Wartet auf eingehende Verbindungen
    54. /// </summary>
    55. public void Start()
    56. {
    57. Task.Run(() => StartServer()).Start();
    58. void StartServer()
    59. {
    60. Server = new TcpListener(IPEndPoint);
    61. Server.Start();
    62. while (true)
    63. {
    64. TcpClient client = Server.AcceptTcpClient();
    65. ClientUser clientuser = new ClientUser(client);
    66. clientuser.ClientUserMessage += (sender, e) => ClientMessage(sender, e);
    67. Clients.Add(clientuser);
    68. }
    69. }
    70. }
    71. /// <summary>
    72. /// Sendet eine Nachricht zu Allen benutzern des Servers
    73. /// </summary>
    74. /// <param name="Message">zu sendene Nachricht</param>
    75. public void SendMessageToClientUsern(string Message)
    76. {
    77. Clients.ForEach(x => {
    78. Console.WriteLine($"Message is Sending to {x.IP.ToString()}");
    79. x.SendMessageToClinte(Message);
    80. });
    81. }
    82. }
    83. }


    Und die Implementierung

    C#-Quellcode

    1. namespace Server
    2. {
    3. class Program
    4. {
    5. private static ServerTcpIp server;
    6. static void Main(string[] args)
    7. {
    8. server = new ServerTcpIp(IPAddress.Any, 4000);
    9. server.ClientMessage += (sender, e) => Console.WriteLine(((ServerTcpIp.MessageContainer)e).Message);
    10. while (true)
    11. {
    12. string message = Console.ReadLine();
    13. server.SendMessageToClientUsern(message);
    14. }
    15. }
    16. }
    17. }
    Die Ip muss doch die Ip sein, die ich per wieistmeineip.de abfrage, richtig?
    Nicht unbedingt. Nur, wenn du von außerhalb deines Netzwerkes (z.B dein Bekannter/Nachbar/NSA) auf deinen Server zugreifen will und du entsprechende Portweiterleitung(en) aktiviert hast. Ansonsten reicht es ​127.0.0.1 einzutragen
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Dann bekomm ich eine Fehlermeldung like
    System.Net.Sockets.SocketException: "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"


    dazu muss ich sagen, ich hab das Client-Programm von meiner Freundin aus gestartet
    Firewall z.B. könnte auch noch schuld sein.

    Außerdem dafür lauter Tasks zu erzeugen ist etwas dämlich, ich würd lieber auf die Begin Methoden setzen, ich glaub mit denen hast auch den Vorteil von IOT. Wobei glaub ich die Async Methoden dasselbe verwenden(jedoch mit mehr Overhead)
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    @jvbsl Jou.
    @Facebamm Fang an und entwickle das Programm / die Programme an einem Rechner, da funktioniert die Verbindung als solche immer.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    @Facebamm: Du solltest dich erst einmal mit den Grundsätzen der TCP Kommunikation auseinander setzten und den Unterschied erkennen zwischen einer, wie bei dir, 88.65.143.174er IP und einer 192.168.178.1er IP.
    Das eine (erste) ist die öffentliche IP (sozusagen die Hausadresse, in dem du wohnst. Die kann/sollte [Namen eines bekannten Onlinehändlers einfügen]wissen, wenn du da was bestellst). Die zweite ist eine sogenannte private Adresse - oder auch IP Adresse aus dem privaten IP Bereich. Die kannst du in der Analogie von gerade mit deiner Wohnungsnummer vergleichen. Der Paketdienst braucht die nicht wissen / weiß sie im Normalfall auch gar nicht. Der Postbote steht unten, klingelt und du machst im auf.
    Zurück zu deinem Problem:

    Facebamm schrieb:

    ich
    hab das Client-Programm von meiner Freundin aus gestartet

    Radinator schrieb:

    und du entsprechende Portweiterleitung(en) aktiviert hast
    Wenn du dem Postboten die Türe nicht aufmachst, dann wirst du dein [Namen eines bekannten Onlinehändlers einfügen] Paket nie erhalten.

    RodFromGermany schrieb:

    da
    funktioniert die Verbindung als solche immer.

    Radinator schrieb:

    reicht es 127.0.0.1
    Wie oben: Wenn du 127.0.0.1 verwendest, ist das, als ob du bei dir in der Wohnung was in einen ([Namen eines bekannten Onlinehändlers einfügen])Karton einpackst, vor die Türe stellst und dich freust, dass ein (Amazon) Paket angekommen ist ;D Das klappt (immer).
    Ok Spaß beiseite: die 127.0.0.1 wird auch Loopback Interface genannt. Diese ist dazu da, um zu testen, ob die Netwerkkarte funktioniert (CMD -> Ping auf 127.0.0.1 oder "localhost" - wenn was ankommt ist alles gut, sonst ist deine Netzwerkkarte im Eimer)

    Ich hoffe ich konnte dir eine ausführliche Erklärung/Einleitung bieten
    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    @Radinator war verständlich.
    Also ist das Modem/Router mein Haus und die 198....... dann meine Haustür :D

    So zwischen standpunkt:
    Verbindung steht :thumbup:
    Port freigeschalten im Modem/Router :thumbup:
    FireWall Portfreigabe :thumbup:

    Nachrichten senden :thumbdown:

    (Links: Server|Mitte:Client|Rechts:Client)
    Zum Bild, die jeweiligen "5" habe ich versucht an den Server zu senden und die "Message" vom Server zu den Clients
    Was mach ich da noch falsch.

    also die Analogie ist so:
    Verbindung kommt rein, die wir dann in einen ClientUser umgewandel

    C#-Quellcode

    1. TcpClient client = Server.AcceptTcpClient();
    2. ClientUser clientuser = new ClientUser(client);


    Der ClientUser erstellen einen StreamWriter und einen StreamReader, der in einem neuen Thread den Stream liest

    C#-Quellcode

    1. public ClientUser(TcpClient client){
    2. ...
    3. NetworkStream ns = client.GetStream();
    4. StreamWriter = new StreamWriter(ns);
    5. StreamReader = new StreamReader(ns);
    6. new Thread(new ThreadStart(ReadMessage)).Start();
    7. void ReadMessage(){
    8. while (true)
    9. ClientUserMessage(this, new MessageContainer(StreamReader.ReadToEnd()));
    10. }
    11. }


    Aber wenn ich jetzt wie im Bilde was Senden möchte, dann kommt nichts an Oo
    Könnte das daran liegen, das der Stream "blockiert" wird?

    Sende Methode(Server)

    C#-Quellcode

    1. public void SendMessageToClientUsern(string Message)
    2. {
    3. Clients.ForEach(x => {
    4. Console.WriteLine($"Message is Sending to {x.IP.ToString()}");
    5. x.SendMessageToClinte(Message);
    6. });
    7. }
    Ich würde nicht ReadToEnd() sondern ReadLine() verwenden. Das könnte das Problem sogar schon beheben.
    Wenn nicht, häng mal dein Projekt an oder lad es auf GitHub hoch.

    Grüße
    Vainamo
    Soo, ich hab den Client jetzt mal auf deine Webseite ausgelagert
    Was ich bisher ergoogelt habe, ist das ich jetzt einen Handshake vollziehen muss
    Der klappt so naja

    Meine Frage ist nun wie kann ich das 'unrecognized frame opcode 13' problem lösen, dazu muss ich noch sagen: mir ist noch nicht ganz klar was das bedeutet :/ ...
    Und wieder wie erwünscht, codeverbesserungs Vorschläge :thumbsup:

    Js Code

    Quellcode

    1. connection = new WebSocket('ws://88.65.139.189:80');
    2. // When the connection is open, send some data to the server
    3. connection.onopen = function () {
    4. connection.send('Ping'); // Send the message 'Ping' to the serv
    5. };
    6. connection.onerror = function (error) {
    7. console.log('WebSocket Error ' + error.data);
    8. };
    9. connection.onclose = function (closeEvent) {
    10. console.log('Die Verbindung wurde geschlossen --- Code: ' + closeEvent.code + ' --- Grund: ' + closeEvent.reason);
    11. };


    handshake in c#

    C#-Quellcode

    1. //swkc enthält den Sec-WebSocked-Key vom Client
    2. string swkc = clientrequests.Find(x => x.Contains("Sec-WebSocket-Key:")).Split(' ')[1];
    3. /*
    4. * HTTP/1.1 101 Switching Protocols
    5. * Upgrade: websocket
    6. * Connection: Upgrade
    7. * Sec-WebSocket-Accept: +++++++++++++++++++
    8. */
    9. string key = SecKeyGen(swkc);
    10. string lz = Environment.NewLine;
    11. StreamWriter.WriteLine($"HTTP/1.1 101 Switching Protocols{lz}Upgrade: websocket{lz}Connection: Upgrade{lz}Sec-WebSocket-Accept: {key}{lz}{lz}");
    12. StreamWriter.Flush();
    13. string SecKeyGen(string clientkey)
    14. {
    15. string GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    16. byte[] hash = new SHA1Managed().ComputeHash(Encoding.UTF8.GetBytes($"{clientkey}{GUID}"));
    17. return Convert.ToBase64String(hash);
    18. }
    Handshake ist im TCP Protokoll bereits erledigt. DU musst dich nur noch um einen geregelten Datenaustausch kümmern (was schickt wer in welchem Format)
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell