abuckau907 (05-05-2013),NLErwinZ (05-08-2013)
C&P from my developers forum...
Hello fellow community,
Today I'll show you how to send a detailed amount of information using sockets in a simple and really efficient way.
We'll be using classes to carry those infos, a formatter to de/serialize the classes and two socket-based applications.
So first of all open up Visual Studio and go to File->New Project. Then create a 'Blank Solution', afterwards right click on the solution's name in the Solution Explorer, and choose 'Add->New Project' and create a Class Library named "test_lib".
Now before starting over there're some notes that you must take under consideration before continuing.
- BinaryFormatters can deserialize a class only if it's assembly is loaded.
- Maximum length of a socket is 30KB (so watch out for this, or you can split the packet if you want)
Now in this tutorial we will create a software which sends a Account information to the server, and the server then proceeds to store the information regarding the account.
This class pretty much explains itself but keep in mind to mark the class as "Serializable" in case that you want to serialize it, and also if you add a custom class inside it (ie a Stream class) then you cannot serialize it because each class member have to be serializable.Code:[Serializable] public class AccountInfo { private string user_name; private int user_id; private bool active; public string Username{ get{ return user_name; } } public int ID{ get{ return user_id; } } public bool Active{ get{ return active; } } public AccountInfo(string un, int id, bool isactive) { user_name = un; user_id = id; active = isactive; } }
Next step is making the communication software. Let's start with the client:
Now time to work with the server..Code:using System; using System.Net; using System.Net.Sockets; using System****; using System.Runtime.Serialization.Formatters.Binary; using test_lib; //Add our test_lib as a reference. public class Program { public static byte[] SerializeClass(object c) { MemoryStream memsr = new MemoryStream(); // A graph which will soon represent our object. BinaryFormatter bfmt = new BinaryFormatter(); // A binary formatter which will handle the serialization part. bfmt.Serialize(memsr, c); //Serialize the class. return memsr.ToArray(); } public static void SendData(AccountInfo info) { byte[] data = SerializeClass(info); Socket s = new Socket( ... ); // '...' just to skip the parameters s.Connect(IPAddress.Parse("127.0.0.1"), 5588); // 127.0.0.1 is the localhost address and 5588 is our custom server's port. s.Send(data); s.Disconnect(false); //Disconnect the socket. } static void Main(string[] args) { Console.WriteLine("Please enter your account name:"); string name = Console.ReadLine(); int id = new Random().Next(0, 100); Console.Clear(); Console.WriteLine("Sending your data..."); SendData(new AccountInfo(name, id, true)); Console.Write("DONE!"); Console.Read(); } }
Once ran the server then the client the server's output should be:Code:using System; using System.Net; using System.Net.Sockets; using System****; using System.Runtime.Serialization.Formatters.Binary; using test_lib; //Add our test_lib as a reference. using System.Threading; class Program{ static object Deserialize(byte[] o) { MemoryStream memsr = new MemoryStream(o); // A graph which represents our object. BinaryFormatter bfmt = new BinaryFormatter(); // A binary formatter which will handle the deserialization part. return bfmt.Deserialize(memsr); //Deerialize the class. } static void CreateRunThread() { Socket s = new Socket( ... ); s.Bind((EndPoint)(new IPEndPoint(IPAddress.Any, 5588))); //IPAddress.Any is recommended for this kind of operations. s.Listen(100); //Listens for incoming connections... while(true) { Socket client = s.Accept(); byte[] bytes = new byte[1024]; //1KB should be enough to handle the info's size. client.Receive(bytes); AccountInfo acc = (AccountInfo)Deserialize(bytes); Console.WriteLine("Account received!\r\nName: " + acc.Username + "\r\nID: " + acc.ID.ToString() + "\r\nIs Active: " + acc.Active.ToString()); Console.Read(); } } static void Main(string[] args) { new Thread(new ThreadStart(CreateRunThread)).Start(); //Starts the server stuffs. while(true) { Console.Title = "Server"; } } }
Name: Elio
ID: 52
Active: True
Hope it helps, Elio.
Last edited by ♪~ ᕕ(ᐛ)ᕗ; 05-05-2013 at 01:21 PM.
abuckau907 (05-05-2013),NLErwinZ (05-08-2013)
Looks cool. You mind answering a question? How many bytes get sent in that example? "Elio" + "52" + bool, so maybe like 5 + 4 + 1 bytes or?
Last edited by abuckau907; 05-05-2013 at 05:15 PM.
'Some things that can be counted, don't matter. And some things that matter, can't be counted' - A.E.
--
Euhm,. not realy,.
This gives me '188' as output, why? because you used:Code:Console.WriteLine(data.Length);
Which insn't bad ofcourse, but just a little differend, it gave me this as output: (in sbytes)Code:BinaryFormatter.Serialize(Stream, object);
Which actually contains a lot of 'junk' information like the name of the namespace, class, property's, and such..Code:0 1 0 0 0 255 255 255 255 1 0 0 0 0 0 0 0 12 2 0 0 0 74 67 111 110 115 111 108 101 65 112 112 108 105 99 97 116 105 111 110 49 44 32 86 101 114 115 105 111 110 61 49 46 48 46 48 46 48 44 32 67 117 108 116 117 114 101 61 110 101 117 116 114 97 108 44 32 80 117 98 108 105 99 75 101 121 84 111 107 101 110 61 110 117 108 108 5 1 0 0 0 31 67 111 110 115 111 108 101 65 112 112 108 105 99 97 116 105 111 110 49 46 65 99 99 111 117 110 116 73 110 102 111 3 0 0 0 9 117 115 101 114 95 110 97 109 101 7 117 115 101 114 95 105 100 6 97 99 116 105 118 101 1 0 0 8 1 2 0 0 0 6 3 0 0 0 4 69 108 105 111 52 0 0 0 1 11
I 'stripped' it down a little bit for you: (not everthing, i'm not einstein or such,. can be that i made mistakes,.)
*counted* yup,. 188Code://?? 0 1 0 0 0 //?? 255 255 255 255 //?? 1 0 0 0 0 0 0 0 12 2 0 0 0 //(string)ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //74 67 111 110 115 111 108 101 65 112 112 108 105 99 97 116 105 111 110 49 44 32 86 101 114 115 105 111 110 61 49 46 48 46 48 46 48 44 32 67 117 108 116 117 114 101 61 110 101 117 116 114 97 108 44 32 80 117 98 108 105 99 75 101 121 84 111 107 101 110 61 110 117 108 108 //?? 5 1 0 0 0 //(string)ConsoleApplication1.AccountInfo //31 67 111 110 115 111 108 101 65 112 112 108 105 99 97 116 105 111 110 49 46 65 99 99 111 117 110 116 73 110 102 111 //?? 3 0 0 0 //(string)user_name //9 117 115 101 114 95 110 97 109 101 //(string)user_id //7 117 115 101 114 95 105 100 //(string)active //6 97 99 116 105 118 101 //?? 1 0 0 8 1 2 0 0 0 6 3 0 0 0 //(string)Elio //4 69 108 105 111 //(int)52 52 0 0 0 //(bool)true 1 //?? 11
PS:
@ლ(ಠ_ಠლ) ,
Don't confuse bytes with bits,
A 'normal' size of an int is 4, a 'normal' int also called 'int32', which means it's 4bytes, not 32bytes haha =p that would be a long long number...
PS2:
@abuckau907 ,
You're kind of right, but, remember that 'usually' a short gets used for a string, that means that it's 2 bytes for the length, it would result into 6+4+1, but also don't forget 'usually' in those case's without (de-)serialize there also would be a 'length' (int/int32) and a 'header' (short/int16) value.. which also will add a total of 6 bytes..
EDIT:
Oh, awesome tutorial btw! (totally forgate to say it D=, but now i did =p)
Last edited by NLErwinZ; 05-08-2013 at 02:27 PM. Reason: Grammar and Tags.. i hate them..
@NLErwinZ Thanks.
@OP Only reason I asked is because you described it as "really efficient" - I guess it depends how you look at it.
'Some things that can be counted, don't matter. And some things that matter, can't be counted' - A.E.
--