DotNETWork: Generische TCP/IP und UDP Klasse

    • C#
    • .NET (FX) 4.5–4.8

    Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von φConst.

      DotNETWork: Generische TCP/IP und UDP Klasse

      Guten Abend,

      momentan( seit gestern ) entwickle ich eine Network-Klasse (DotNETWork) und möchte
      seinen Fortschritt hier dokumentieren.

      Es verfügt zurzeit über zwei Klassen , dem DotTcpClient und den DotTcpServer<T> , und drei weiteren Klassen die wichtige Funktionalitäten implementieren.

      Die Methode "Send()" der DotTcpClient-Klasse erlaubt dem Programmierer Typen beliebiger (!) Art zu versenden(Serialisierung).
      Die Klasse DotTcpServer<T> erwartet als Typparameter eine Klasse die von IClient erbt.

      Ein Beispiel-Programm:

      Die Klasse die von IClient erbt:

      C#-Quellcode

      1. public class User : IClient
      2. {
      3. public IPEndPoint ClientEndPoint { get; set; }
      4. public TcpClient TcpClient { get; set; }
      5. public BinaryReader BinReader { get; set; }
      6. public BinaryWriter BinWriter { get; set; }
      7. public void Call(object parameter)
      8. {
      9. int length = BinReader.ReadInt32();
      10. BinWriter.Write(length);
      11. BinWriter.Write(BinReader.ReadBytes(length));
      12. }
      13. }


      Die Konsolenanwendung:

      C#-Quellcode

      1. DotTcpServer<User> tcpServer = new DotTcpServer<User>(225);
      2. tcpServer.Run();
      3. Console.WriteLine("Server is running!");
      4. DotTcpClient client = new DotTcpClient("192.168.0.215", 225);
      5. if (client.Connect(60))
      6. {
      7. Console.WriteLine("Connected !");
      8. //SEND IMAGE
      9. client.Send(new Bitmap(@"image.png"));
      10. Console.WriteLine("SENT!");
      11. //RECEIVE IMAGE!
      12. Bitmap bitMap = client.Receive();
      13. bitMap.Save(@"received.png");
      14. Console.WriteLine("RECEIVED!");
      15. }


      Unschwer zu erkennen ist, dass der Parameter der Methode "Send()" vom Typ "Objekt" ist.
      Auch individuelle Klassen können versendet werden ( Stichwort : Serialisierung msdn.microsoft.com/de-de/libra…rializable(v=vs.110).aspx)

      Zur Klasse : GitHub (Oder Dateianhang, danke an die Moderation)
      Dateien
      • DotNETWork.zip

        (11,36 kB, 273 mal heruntergeladen, zuletzt: )
      Und Gott alleine weiß alles am allerbesten und besser.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „φConst“ ()

      φConst schrieb:

      C#-Quellcode

      1. DotTcpClient client = new DotTcpClient("192.168.0.215", 225);
      Was muss ich tun, damit Dein Codebeispiel bei mir läuft?
      Ich hab da lediglich meine eigene IP-Adresse reingeschrieben. Diese funktioniert zwischen zwei anderen Programmen auf demselben Rechner problemlos.
      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).
      Programmierfragen über PN / Konversation werden ignoriert!
      @φConst OK. Unter W10 läuft das, unter W8.1 nicht :/
      ======

      φConst schrieb:

      C#-Quellcode

      1. new System.Threading.ManualResetEvent(false).WaitOne(sendInterval);
      So geht das nicht.
      Erstelle eine Klassen-Instanz von ManualResetEvent und schalte die entsprechend mit .Set() bzw. .Reset().
      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).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      Ist provisorisch gewesen ^^ .

      Echt? Bei mir klappt alles wunderbar.
      Worin liegt denn das Problem, verbindet er sich nicht?

      Mal eine Frage OT :D , ich implementiere grade die Verschlüsselung ( msdn ), bin aber misstrauisch geworden, was die Methode Encyrpt anbelangt.
      Die MSDN Seite zeigt folgende Methode zu Encryption:

      C#-Quellcode

      1. private void EncryptFile(string inFile)
      2. {
      3. // Create instance of Rijndael for
      4. // symetric encryption of the data.
      5. RijndaelManaged rjndl = new RijndaelManaged();
      6. rjndl.KeySize = 256;
      7. rjndl.BlockSize = 256;
      8. rjndl.Mode = CipherMode.CBC;
      9. ICryptoTransform transform = rjndl.CreateEncryptor();
      10. // Use RSACryptoServiceProvider to
      11. // enrypt the Rijndael key.
      12. // rsa is previously instantiated:
      13. // rsa = new RSACryptoServiceProvider(cspp);
      14. byte[] keyEncrypted = rsa.Encrypt(rjndl.Key, false);
      15. // Create byte arrays to contain
      16. // the length values of the key and IV.
      17. byte[] LenK = new byte[4];
      18. byte[] LenIV = new byte[4];
      19. int lKey = keyEncrypted.Length;
      20. LenK = BitConverter.GetBytes(lKey);
      21. int lIV = rjndl.IV.Length;
      22. LenIV = BitConverter.GetBytes(lIV);
      23. // Write the following to the FileStream
      24. // for the encrypted file (outFs):
      25. // - length of the key
      26. // - length of the IV
      27. // - ecrypted key
      28. // - the IV
      29. // - the encrypted cipher content
      30. int startFileName = inFile.LastIndexOf("\\") + 1;
      31. // Change the file's extension to ".enc"
      32. string outFile = EncrFolder + inFile.Substring(startFileName, inFile.LastIndexOf(".")- startFileName) + ".enc";
      33. using (FileStream outFs = new FileStream(outFile, FileMode.Create))
      34. {
      35. outFs.Write(LenK, 0, 4);
      36. outFs.Write(LenIV, 0, 4);
      37. outFs.Write(keyEncrypted, 0, lKey);
      38. outFs.Write(rjndl.IV, 0, lIV);
      39. // Now write the cipher text using
      40. // a CryptoStream for encrypting.
      41. using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
      42. {
      43. // By encrypting a chunk at
      44. // a time, you can save memory
      45. // and accommodate large files.
      46. int count = 0;
      47. int offset = 0;
      48. // blockSizeBytes can be any arbitrary size.
      49. int blockSizeBytes = rjndl.BlockSize / 8;
      50. byte[] data = new byte[blockSizeBytes];
      51. int bytesRead = 0;
      52. using (FileStream inFs = new FileStream(inFile, FileMode.Open))
      53. {
      54. do
      55. {
      56. count = inFs.Read(data, 0, blockSizeBytes);
      57. offset += count;
      58. outStreamEncrypted.Write(data, 0, count);
      59. bytesRead += blockSizeBytes;
      60. }
      61. while (count > 0);
      62. inFs.Close();
      63. }
      64. outStreamEncrypted.FlushFinalBlock();
      65. outStreamEncrypted.Close();
      66. }
      67. outFs.Close();
      68. }
      69. }
      Quelle : msdn.microsoft.com/de-de/library/bb397867(v=vs.110).aspx

      Meine Frage:
      Beim Schreiben in die CryptoStream :

      C#-Quellcode

      1. using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
      2. {
      3. // By encrypting a chunk at
      4. // a time, you can save memory
      5. // and accommodate large files.
      6. int count = 0;
      7. int offset = 0;
      8. // blockSizeBytes can be any arbitrary size.
      9. int blockSizeBytes = rjndl.BlockSize / 8;
      10. byte[] data = new byte[blockSizeBytes];
      11. int bytesRead = 0;
      12. using (FileStream inFs = new FileStream(inFile, FileMode.Open))
      13. {
      14. do
      15. {
      16. count = inFs.Read(data, 0, blockSizeBytes);
      17. offset += count;
      18. outStreamEncrypted.Write(data, 0, count);
      19. bytesRead += blockSizeBytes;
      20. }
      21. while (count > 0);
      22. inFs.Close();
      23. }
      24. outStreamEncrypted.FlushFinalBlock();
      25. outStreamEncrypted.Close();
      26. }


      was wird das?
      Erstmal, wieso wird ein offset deklariert, der gar nicht benutzt wird?
      Müsste das nicht bei

      C#-Quellcode

      1. outStreamEncrypted.Write(data, 0, count);
      eingesetzt werden.
      Und wieso

      C#-Quellcode

      1. bytesRead += blockSizeBytes
      , das ja wie ersichtlich konstant ist.
      Müsste das nicht mit "count" addiert werden?


      Lieben Dank

      Edit: Flusht der CryptoStream direkt den Puffer und ist deswegen startIndex immer bei null?
      Und Gott alleine weiß alles am allerbesten und besser.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „φConst“ ()

      φConst schrieb:

      CryptoStream
      Da kann ich jeztz nix zu sagen. Vielleicht solltest Du dazu einen separaten Thread aufmachen.
      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).
      Programmierfragen über PN / Konversation werden ignoriert!
      Lol, hat funktioniert.

      Das Senden wird mit einem PublicKey verschlüsselt..
      der Programmierer übernimmt lediglich die Aufgabe der Server-Seite ( zum Entschlüsseln ).


      GitHub wird in Kürze geupdated.

      Ein Exempel ist im Anhang zu finden.


      Der Code :

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. using System.Text;
      5. using System.Threading.Tasks;
      6. using DotNETWork.Tcp;
      7. using System.Drawing;
      8. using System.Net.Sockets;
      9. using System.IO;
      10. using System.Net;
      11. using System.Threading;
      12. using DotNETWork.Udp;
      13. using DotNETWork.Globals;
      14. using DotNETWork.Udp.ACK;
      15. namespace TestingDotNETWork
      16. {
      17. class Program
      18. {
      19. static void Main(string[] args)
      20. {
      21. DotTcpServer<User> tcpServer = new DotTcpServer<User>(225);
      22. tcpServer.Run();
      23. Console.WriteLine("Server is running!");
      24. DotTcpClient client = new DotTcpClient("192.168.0.215", 225);
      25. if (client.Connect(60))
      26. {
      27. Console.WriteLine("Connected !");
      28. client.Send("Encrypted!");
      29. Console.WriteLine("RECEIVED: " + client.Receive());
      30. }
      31. Console.ReadLine();
      32. }
      33. }
      34. public class User : IClient
      35. {
      36. public IPEndPoint ClientEndPoint { get; set; }
      37. public TcpClient TcpClient { get; set; }
      38. public BinaryReader BinReader { get; set; }
      39. public BinaryWriter BinWriter { get; set; }
      40. public void Call(object parameter)
      41. {
      42. DotTcpServer<User> serverParam = (DotTcpServer<User>)parameter;
      43. int length = BinReader.ReadInt32();
      44. byte[] enryptedBuffer = serverParam.RijndaelDecryption.DecryptStream(BinReader.ReadBytes(length));
      45. BinWriter.Write(enryptedBuffer.Length);
      46. BinWriter.Write(enryptedBuffer);
      47. BinWriter.Flush();
      48. }
      49. }
      50. }


      Post scriptum: Weil das Dekompilieren von Anwendung in Deutschland imho mit dem deutschen Recht kollidiert, ( jetzt mal ganz richterlich ) permittiere ich jedem meine Applikationen die ich
      in diesem Forum publiziere zu dekompilieren.

      Edit: Aktualisiert.

      *facepalm* klar.. hier habt auch alle die IP : 192.168.0.215 8o
      Dateien
      • Debug.zip

        (12,47 kB, 251 mal heruntergeladen, zuletzt: )
      Und Gott alleine weiß alles am allerbesten und besser.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „φConst“ ()

      Ist auch meine Frage, ich teste das mal jetzt am Laptop zu Pc , Pc zu Laptop Test.

      Huhu 8o 8o klappt wunderbar.

      Hab nun auch den Client geupdated.

      Das Schema der Verschlüsselung:

      Client konnektiert zum Server ( registriert sich ) und erhält einen Public-Key.
      Client sendet darauffolgend seinen Public -Key.
      Server fügt Client in Client-Liste.

      Client übermittelt Nachricht an den Server ( verschlüsselt wohlgemerkt ), Server decrypted dies mit dem privaten Schlüssel und encrypted die Nachricht mit dem speziellen Public-Key das er vom Client zugesandt bekam.
      Ergo, verschlüsselt sowohl ausgehend wie auch eingehend!

      Anhang : Server und Client!

      Edit: Eigentümlich ist es wohl kaum.
      Ich glaube die Crypto-Klassen generieren den Private-Key spezifisch für die Maschine, die die Methode aufrief,
      sonst ergäbe es keinen Sinn, einen Public-Key exportieren zu dürfen.

      Anwendungen außerhalb des Showrooms sind nicht erlaubt!
      *Anhang entfernt*
      Und Gott alleine weiß alles am allerbesten und besser.

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „Solaris“ ()

      @φConst
      @ErfinderDesRades und @Solaris meinen, da Du sowieso oben schon die Quellen gepostet hast, kannst Du jetzt auch die aktuellen Quellen posten.
      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).
      Programmierfragen über PN / Konversation werden ignoriert!
      Entschuldigt wirklich..
      die Quelle ist angegeben, MSDN ( Kryptologie ), die Progressbar war von einem User zur Verfügung gestellt worden.
      Wenn ich etwas vergessen habe: Seid so freundlich und ergänzt die Quellen, will ja nicht plagieren.

      Rijndael : msdn.microsoft.com/de-de/library/bb397867(v=vs.110).aspx
      ASCII Progressbar: gist. github. com/DanielSWolf/0ab6a96899cc5377bf54

      Edit: Ahh haha 8o , ihr redet von Source :
      Server:

      C#-Quellcode

      1. class Program
      2. {
      3. static void Main(string[] args)
      4. {
      5. Console.Write("Port: ");
      6. DotTcpServer<User> tcpServer = new DotTcpServer<User>(int.Parse(Console.ReadLine()));
      7. tcpServer.MaximumClients = 2;
      8. tcpServer.Run();
      9. Console.WriteLine("Server is running!");
      10. }
      11. }
      12. public class User : IClient
      13. {
      14. public IPEndPoint ClientEndPoint { get; set; }
      15. public TcpClient TcpClient { get; set; }
      16. public BinaryReader BinReader { get; set; }
      17. public BinaryWriter BinWriter { get; set; }
      18. public string PublicKeyXML { get; set; }
      19. DotRijndaelEncryption rEncryptor;
      20. private string UserId;
      21. public void Call(object parameter)
      22. {
      23. ClientParameters<User> clientParameters = (ClientParameters<User>)parameter;
      24. try
      25. {
      26. string text = clientParameters.DecryptedBytes.DeserializeToDynamicType();
      27. if (text.Contains("$$$USER"))
      28. {
      29. this.UserId = text.Split(new char[]
      30. {
      31. '='
      32. })[1];
      33. }
      34. else if (text.Contains("$$§$$CMD"))
      35. {
      36. User user = clientParameters.ServerHandler.ClientList.Find((User p) => p.UserId != this.UserId);
      37. this.rEncryptor = new DotRijndaelEncryption(user.PublicKeyXML);
      38. byte[] array = this.rEncryptor.EncryptStream(clientParameters.DecryptedBytes);
      39. user.BinWriter.Write(array.Length);
      40. user.BinWriter.Write(array);
      41. }
      42. else
      43. {
      44. for (int i = 0; i < clientParameters.ServerHandler.ClientList.Count; i++)
      45. {
      46. this.rEncryptor = new DotRijndaelEncryption(clientParameters.ServerHandler.ClientList[i].PublicKeyXML);
      47. byte[] array2 = this.rEncryptor.EncryptStream(clientParameters.DecryptedBytes);
      48. clientParameters.ServerHandler.ClientList[i].BinWriter.Write(array2.Length);
      49. clientParameters.ServerHandler.ClientList[i].BinWriter.Write(array2);
      50. }
      51. }
      52. Console.WriteLine("Received..!");
      53. }
      54. catch (Exception ex)
      55. {
      56. Console.WriteLine("Error...!" + ex.Message);
      57. }
      58. }
      59. }


      Client:

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.ComponentModel;
      4. using System.Data;
      5. using System.Drawing;
      6. using System.Linq;
      7. using System.Text;
      8. using System.Threading.Tasks;
      9. using System.Windows.Forms;
      10. using DotNETWork;
      11. using DotNETWork.Globals;
      12. using DotNETWork.Security;
      13. using DotNETWork.Tcp;
      14. using System.Threading;
      15. using System.Diagnostics;
      16. namespace ClientExampleDotNETWork
      17. {
      18. public partial class Client : Form
      19. {
      20. public Client()
      21. {
      22. InitializeComponent();
      23. }
      24. private DotTcpClient dotTcpClient;
      25. private bool startedAnalyzing, isTyping;
      26. private Stopwatch stopWatch = new Stopwatch();
      27. private Random randomInt = new Random();
      28. private void Client_Load(object sender, EventArgs e)
      29. {
      30. string inputString = Microsoft.VisualBasic.Interaction.InputBox("Type in ip and port (Scheme: ip:port)");
      31. string serverIp = inputString.Split(':')[0];
      32. int serverPort = int.Parse(inputString.Split(':')[1]);
      33. dotTcpClient = new DotTcpClient(serverIp, serverPort);
      34. if (!dotTcpClient.Connect(1000))
      35. {
      36. MessageBox.Show("Connection refused!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
      37. tbInput.Enabled = false;
      38. return;
      39. }
      40. pbAvatar.Image = new Bitmap(1, 1);
      41. // Eindeutige ID , damit das Avatar und der Status des Schreibens an den Sender erneut verschickt werden.
      42. string clientId = "Id_" + randomInt.Next(0, 1000);
      43. dotTcpClient.Send("$$$USER=" + clientId);
      44. //dotTcpClient.Send("$$$AVATAR");
      45. //dotTcpClient.Send(pbAvatar.Image);
      46. new Thread(new ThreadStart(() =>
      47. {
      48. while (true)
      49. {
      50. string messageIn = dotTcpClient.Receive();
      51. if (messageIn.Contains("$$§$$CMD"))
      52. {
      53. this.Invoke(new Action(() =>
      54. {
      55. statusLb.Text = "Status " + (messageIn.Split('=')[1].Equals("TRUE") ? "is writing..." : "idle");
      56. }));
      57. }
      58. //else if (messageIn.Contains("$$$AVATAR"))
      59. //{
      60. // this.Invoke(new Action(() =>
      61. // {
      62. // pbAvatar.Image = (Image)dotTcpClient.Receive();
      63. // }));
      64. //}
      65. else
      66. this.Invoke(new Action(() =>
      67. {
      68. receivingLb.Items.Add(messageIn);
      69. }));
      70. }
      71. })).Start();
      72. }
      73. private void tbInput_KeyDown(object sender, KeyEventArgs e)
      74. {
      75. if (!startedAnalyzing)
      76. {
      77. startedAnalyzing = true;
      78. Task.Run(() =>
      79. {
      80. dotTcpClient.Send("$$§$$CMD_WRITING=TRUE");
      81. while (startedAnalyzing)
      82. {
      83. if (isTyping)
      84. {
      85. stopWatch.Reset();
      86. stopWatch.Start();
      87. }
      88. else
      89. {
      90. if (stopWatch.Elapsed.TotalMilliseconds >= 500)
      91. {
      92. break;
      93. }
      94. }
      95. new ManualResetEvent(false).WaitOne(1);
      96. }
      97. startedAnalyzing = false;
      98. dotTcpClient.Send("$$§$$CMD_WRITING=FALSE");
      99. });
      100. }
      101. if (e.KeyCode == Keys.Enter && !isTyping)
      102. {
      103. sendBttn.PerformClick();
      104. e.SuppressKeyPress = true;
      105. }
      106. isTyping = true;
      107. }
      108. private void tbInput_KeyUp(object sender, KeyEventArgs e)
      109. {
      110. isTyping = false;
      111. }
      112. private void sendBttn_Click(object sender, EventArgs e)
      113. {
      114. dotTcpClient.Send(tbInput.Text);
      115. tbInput.Clear();
      116. }
      117. }
      118. public static class Extension
      119. {
      120. public static void InvokeI(this Form frm, Action aDelegate)
      121. {
      122. frm.Invoke(aDelegate);
      123. }
      124. }
      125. }
      Und Gott alleine weiß alles am allerbesten und besser.
      Ah komm schon, wirklich ?
      Ein Klick mehr: Zu github navigieren und exportieren als Zip... ist doch redundant alles auf VBP hochzuladen, wenn
      ich mein VS mit der Repo synchronisieren kann.

      :P
      Und Gott alleine weiß alles am allerbesten und besser.