So hiiiii....In the previous thread I told you all something about the Socket Programming fundamental concepts followed with a example. Now I am going to explain you something more advanced.
My goal is to add a password system (which means that every client that wants to connect to it it must send its credentials to the server first) to our old server. Pretty easy if you ask me.
First of all we need two separated services for our server.
1: The main server
2: The password protection
Before talking about this I want to introduce you guys with the System.Runtime.Serialization namespace. To pack a class we need a Binary formatter.
What is a Binary Formatter?
-A binary formatter is a class which serializes a specific object to a array of bytes. (it can also deserialize an array) The only thing that we have to worry about is that a binary formatter needs to know the specific Assembly Name of the class to deserialize it. So if we want to use the same class in both server and client applications we need to have the same Assembly Name. How? Easy. We are going to use a Dynamic-Link-Library (DLL).
So start with making a new class library. Then paste the following code:
Code:
//NOTE: THIS CODE IS HAND WRITTEN! I WROTE THIS ON NOTEPAD SO THERE MIGHT BE GRAMMAR ERRORS. (Easy to fix btw)
using System;
//We need a base class to make the server's code smaller. (You are going to see in the next code example how)
[Serializable]
public class PasswordRequest
{
}
//The client is going to send this to the server. This class contains the info about the password.
[Serializable]
public class PasswordLoginQuery : PasswordRequest
{
private string pass; //the password needed to login
public PasswordLoginQuery(string p)
{
pass = p;
}
public string Password{ get{ return pass; } }
}
//The server will reply with this class to the client's request. This class contains the info regarding the result of the login operation.
[Serializable]
public class PasswordLoginResult : PasswordRequest
{
private bool accepted; //is the password correct?
private string welcome_message; //the message that the server delivers to the client
public PasswordLoginResult(bool accept, string msg)
{
accepted = accept;
welcome_message = msg;
}
public bool Accepted { get{ return accepted; } }
public string MessageFromServer{ get{ return welcome_message; } }
} 
Originally Posted by
Notes If we want to de/serialize a class we need to mark it as 'Serializable'.
This now hit compile and add the compiled class library as a reference to the server and client software. Now we can focus on the server stuff.
In the previous tutorial we've learned how to send and receive a array of bytes using Sockets. We are going to do the same for this one too.
Code:
/*
PasswordRequest - Base Class
PasswordLoginQuery - Login Request
PasswordLoginResult - Result Of The Login Operation
*/
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System****;
using System.Runtime.Serialization.Formatters.Binary;
using ClassLibrary1; //Your class library name.....
public class SomethingPasswordProtectionServer
{
//De/Serialization Functions.....:
private byte[] PackClass(object c)
{
BinaryFormatter binFmt = new BinaryFormatter();
MemoryStream memSr = new MemoryStream();
binFmt.Serialize(memSr, c);
return memSr.ToArray();
}
private object UnpackClass(byte[] c)
{
BinaryFormatter binFmt = new BinaryFormatter();
MemoryStream memSr = new MemoryStream(c);
return binFmt.Deserialize(memSr);
}
//--------------------------------------------
Socket s = new Socket(...);
string password = "MPGH-Richard Nixon";
public SomethingPasswordProtectionServer()
{
//We want a listening Socket
s.Bind((EndPoint)(new IPEndPoint(IPAddress.Any, 8887)));
s.Listen(0);
}
private void run_server()
{
while(true)
{
//I'm assuming that you know the basics. So use them for God's Sake.
Socket client = s.AcceptSocket();
byte[] data_from_client = new byte[300];
client.Receive(data_from_client);
PasswordRequest pc = (PasswordRequest)UnpackClass(data_from_client); //doesn't matter if te array which will hold the bytes is larger than the received bytes size.
if(pc is PasswordLoginQuery)
{
PasswordLoginQuery plq = (PasswordLoginQuery)pc;
if(plq.Password == password)
{
PasswordLoginResult plr = new PasswordLoginResult(true, "Welcome to the server.");
client.Send(PackClass(plr));
} else {
PasswordLoginResult plr = new PasswordLoginResult(false, "Password is incorrect.");
client.Send(PackClass(plr));
}
}
}
}
public void Run()
{
(new Thread(new ThreadStart(run_server))).Start();
}
} Code:
if(pc is PasswordLoginQuery) - Trust me this one might turn out really helpful (in other situations) to reduce the number of lines in your code and to don't overload it with beginners shits. ;)
So now we have the password server up and the main server too (this one from the old code on the first tut).
Code:
public SomethingServer()
{
//We want a listening Socket
s.Bind((EndPoint)(new IPEndPoint(IPAddress.Any, 8888)));
s.Listen(0);
SomethingPasswordProtectionServer spps = new SomethingPasswordProtectionServer();
spps.Run();
}
----------
This is the client part now....We already have the main client working, but we have to add the login operation before the wanted data transfer.
Code:
/*
PasswordRequest - Base Class
PasswordLoginQuery - Login Request
PasswordLoginResult - Result Of The Login Operation
*/
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System****;
using System.Runtime.Serialization.Formatters.Binary;
using ClassLibrary1; //Your class library name.....
public class SomethingClientVerification
{
//De/Serialization Functions.....:
private byte[] PackClass(object c)
{
BinaryFormatter binFmt = new BinaryFormatter();
MemoryStream memSr = new MemoryStream();
binFmt.Serialize(memSr, c);
return memSr.ToArray();
}
private object UnpackClass(byte[] c)
{
BinaryFormatter binFmt = new BinaryFormatter();
MemoryStream memSr = new MemoryStream(c);
return binFmt.Deserialize(memSr);
}
//--------------------------------------------
public bool Verify()
{
Socket s = new Socket(...);
s.Connect(...);
PasswordLoginQuery plq = new PasswordLoginQuery("MPGH-Richard Nixon");
s.Send(PackClass(plq));
byte[] result = new byte[500];
s.Receive(result);
PasswordLoginResult plr = (PasswordLoginResult)UnpackClass(result);
return plr.Accepted; //The message is unneeded.
}
} ^ we will use SomethingClientVerification class to verify our password. (We must use a new class for this because we obv do not want to overload our main client code)
Now we simply have to add some lines to the client code:
Code:
public Something()
{
SomethingClientVerification scv = new SomethingClientVerification();
bool can_enter = scv.Verify();
if(can_enter)
{
s.Connect("HostIP", 4444); //4444 - Server Port
} else {
Console.WriteLine("Password Is Invalid!");
return;
}
} Just edit the constructor.
And there we are! Both client and server are working, and the server is also password-protected!
Obv. this was just an example on how you can use classes to create your own advanced Socket-Based-Application.
Thank you, Elio.