📜 ⬆️ ⬇️

We write a multiplayer chat on C # in 15 minutes



C # is as stupid as it is simple. And it is so simple that a chat for several people with minimal protection is written in it for fifteen, well, a maximum of thirty minutes. It took us a little over two days, but this is already a problem of fools, and not tools.

Let's set the task: we want to make a decentralized group chat with some kind of protection. For such a “large” task, we need nothing at all: C # (you can even use a non-Orthodox MonoDevelop) with its great .NET Framework.
')
First, we write part of the sending and receiving messages. The problem is that you need to somehow deal with sending messages to multiple clients. It is quite difficult to do this; unfamiliar with networks, all sorts of dirty thoughts a la immediately come to mind: to store all the IPs that have ever been sent to messages, or there to organize some tricky graph between users. As usual, the problem is solved if you look at it from the side: why should we use TCP when there is UDP?



After a very vain attempt to establish at least some multi-user interaction via TCP, I chose the second option, and everything turned out to be very simple - there are separate groups in UDP, and the participants cannot so easily send a message to an individual group member. If the message is sent, it is sent to all members of the group - what we need. Let's make the class Chat, which will have the following fields and methods:

View code
private UdpClient udpclient; private IPAddress multicastaddress; private IPEndPoint remoteep; public void SendOpenMessage(string data); public void Listen(); 


For the UdpClient, IPAddress and IPEndPoint fields, let's include the System.Net.Sockets and System.Net libraries.
Well and designers-destruktor of itself. In the constructor, we will initialize the udpclient field:

View code
 public Chat() { multicastaddress = IPAddress.Parse("239.0.0.222"); //       UDP  udpclient = new UdpClient(); udpclient.JoinMulticastGroup(multicastaddress); remoteep = new IPEndPoint(multicastaddress, 2222); } 



In the destructor, while we will not do anything - the Garbage collector is the same.
Now the main thing - SendMessage and Listen. SendMessage will send the UTF8 representation of the string, and here again C # comes to the rescue, in which you can get a byte representation in one line:

View code
 public void SendMessage(string data) { Byte[] buffer = Encoding.UTF8.GetBytes(data); udpclient.Send(buffer, buffer.Length, remoteep); } 


In the Listen method, we simply run a separate stream on the same address and port through which we send messages, which will receive bytes, decrypt them, and write them to a common console:

View code
 public void Listen() { UdpClient client = new UdpClient(); client.ExclusiveAddressUse = false; IPEndPoint localEp = new IPEndPoint(IPAddress.Any, 2222); client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); client.ExclusiveAddressUse = false; client.Client.Bind(localEp); client.JoinMulticastGroup(multicastaddress); Console.WriteLine("\tListening started"); string formatted_data; while (true) { Byte[] data = client.Receive(ref localEp); formatted_data = Encoding.UTF8.GetString(data); Console.WriteLine(formatted_data); } } 



With the exchange of messages is now over. Encryption is screwed even easier: for him we will have to ask the user for the key when creating the chat object, add decryption encryption methods, send the string to the group after processing with the encryption method, and output after decryption. Delov then.

View code
  private byte[] Encrypt(string clearText, string EncryptionKey = "123") { byte[] clearBytes = Encoding.UTF8.GetBytes(clearText); byte[] encrypted; using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); //         . encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.Close(); } encrypted =ms.ToArray(); } } return encrypted; } private string Decrypt(byte[] cipherBytes, string EncryptionKey = "123") { string cipherText = ""; using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.Close(); } cipherText = Encoding.UTF8.GetString(ms.ToArray()); } } return cipherText; } 


Now we need to slightly change the SendMessage and Listen methods, adding encryption and decryption there. Quite trivial in my opinion.

View code
 //  SendMessage public void SendMessage(string data) { Byte[] buffer = Encoding.UTF8.GetBytes(data); Byte[] encrypted = Encrypt(data); udpclient.Send(encrypted, encrypted.Length, remoteep); } //  Listen while (true) { Byte[] data = client.Receive(ref localEp); formatted_data = Decrypt(data); Console.WriteLine(formatted_data); } 



Now the final step is the main function. In it, we will run one thread, so we need System.Threading;
With the additional thread, everything is implemented literally in four lines:

View code
 static void Main(string[] args) { Chat chat = new Chat(); Thread ListenThread = new Thread(new ThreadStart(chat.Listen)); ListenThread.Start(); string data = Console.ReadLine(); chat.SendMessage(data); } 


Everything, the simplest messaging we wrote. To him you can finish the messaging in an infinite loop, nicknames, message history, settings, windows - a lot of things, but this can already be attributed to another article.

PS bitbucket.org/AnAverageGuy/termprojectc - here you can find all that gloomy horror that is depicted on the top of the second picture. Someday I will brush all the code, and the master branch will turn from a pumpkin into a carriage.

Source: https://habr.com/ru/post/252021/


All Articles