Let's begin our guide.
I. Purpose
The purpose of this tutorial is covering the following aspects
• What is the Asynchronous Calls ?.
• What is the difference between Synchronous and Asynchronous code ?.
• What is the Socket class ?.
• How to create a simple Socket Server for Conquer developing.
II. Requirements
This guide is written as a beginner guide; However, It is expected that you have a basic knowledge of C# and a working brain.
In order to be able to follow along, you will need:
• Brain.exe
• Microsoft Visual Studio 2008 or later (or equivalent)
• Will to learn
III. Explanations
I will start to explain what i was talking about previously so let's take
What is the Asynchronous Calls ?
•Simply Asynchronous(Non-Blocking) calls are made, but we don't have to wait for them to complete; therefore, the program can resume to the next function without suspending the execution .
What is the difference between Synchronous and Asynchronous code ?
•Unlike Asynchronous Calls, Synchronous(Blocking) program is executed line by line. Each time a function is called, program execution waits until that function returns before continuing to the next line of code.
So we can conclude that using an Asynchronous Call, We can prevent program freeze while doing some Time-Consuming functions.
What is the Socket class ?
•The Socket class allows you to perform both synchronous and asynchronous data transfer between clients and servers.
IV. Implementation
Now we are ready to get started. The first thing we will be doing is creating a new Console Application File->New->Project and giving it any name Ex. Asynchronous Socket Server
-Create a new class and give it a name, i will name mine Socket.cs
-After creating the class we will have something like this
Code:
[SIZE=2]using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Asynchronous_SocketServer { class Socket { } }
Code:
[SIZE=2]using System; using System.Net; //The networking class using System.Net.Sockets; //The Socket class that we will gonna use
-Now that we just added the namespaces we gonna use, we may proceed inside our class
Code:
namespace Asynchronous_SocketServer { [COLOR=Red] /*class Socket { }[SIZE=2]*/ }
Code:
public class SocketServerEventArgs : EventArgs { [SIZE=2] public byte[] Buffer { get; private set; } public byte[] EndBuffer { get; private set; } public uint EndLength { get; private set; } public Socket Socket; [SIZE=2] public void CreateBuffer(uint size) { Buffer = new byte[size]; } public void CreateEndBuffer(uint size) { EndBuffer = new byte[size]; } public void SetEndLength(uint length) { EndLength = length; } public void BeginReceive(AsyncCallback receiveData) { if (Buffer == null) throw new ApplicationException("Buffer has not been set."); Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, receiveData, this); } public int EndReceive(IAsyncResult result) { return Socket.EndReceive(result); } public bool IsConnected() { return Socket.Connected; } public void Disconnect(bool reuseSocket) { if (Socket.Connected) { Socket.Disconnect(reuseSocket); } } -So let's start by removing the red part at the code above and replace it with [SIZE=2] }
-You may wonder, What we have added right now ?, Just calm down and everything will be self explained late on this guide
-Right below our SocketServerEventArgs public class we will add another public class and name it Sockets and it will look like
[/SIZE][/SIZE][/SIZE]
Code:
public class Sockets { private const int ListenLength = 500;//The listening length public event EventHandler<SocketServerEventArgs> Connected;//Client Connected Event public event EventHandler<SocketServerEventArgs> Disconnected;//Client Disconnected Event public event EventHandler<SocketServerEventArgs> Received;// Packet Received Event private readonly Socket _socket;// Our socket public Sockets(int port)//Class constructor { try { _socket = InitializeSocket(port);//sets our socket equals to the socket we have initialized } catch (Exception e) { Console.WriteLine(e);//Write an exception (if happened) } } }
The public events [public event EventHandler] is what we gonna call when something happen like Client Connected, Packet Received, and Client Disconnected to inform our program.cs about what is going inside our Socket class
-Now we must add our InitializeSocket function
Code:
private Socket InitializeSocket(int port) { var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, port)); socket.Listen(ListenLength); socket.BeginAccept(AcceptConnections, new SocketServerEventArgs()); return socket; }
2- SocketType : The type of our socket.
3- ProtocolType.Tcp : There is 2 main protocol type TCP & UDP, However TCP would be the best choice for us now.
4- socket.Bind : Binds our socket to an ip adrees and a port.
5- socket.Listen : Starts the Listening Process.
6- socket.BeginAccept : The Asynchronous method of socket.Accept which starts to accept clients
-That we called an Asynchronous method we must add an Asynchronous Call Back so we are going to add this to our Sockets class
Code:
private void AcceptConnections(IAsyncResult ar) { var socketServerResult = (SocketServerEventArgs) ar.AsyncState; try { socketServerResult.Socket = _socket.EndAccept(ar); socketServerResult.CreateBuffer(65535); socketServerResult.BeginReceive(RecieveData); Connected.InvokeSafely(this, socketServerResult); _socket.BeginAccept(AcceptConnections, new SocketServerEventArgs()); } catch (Exception e) { socketServerResult.Disconnect(true); Disconnected.InvokeSafely(this, socketServerResult); Console.WriteLine(e); } }
2- socketServerResult.BeginReceive(ReceiveData); The asynchronous method of socket.Recerive, we will add the ReceiveData callback after Step 4
3- Connected.InvokeSafely : before we can use InvokeSafely we must add a new class
and add this to it
Code:
using System; namespace Asynchronous_SocketServer { public static class EventHandlerExtensions { public static void InvokeSafely<T>(this EventHandler<T> eventHandler, object sender, T eventArgs) where T : EventArgs { if (eventHandler != null) { eventHandler(sender, eventArgs); } } } }
4- _socket.BeginAccept(AcceptConnections, new SocketServerEventArgs()); : To start listening for another connection
-Now let's add another callback for the ReceiveData : To handle the data received from the client
Code:
private void ReceiveData(IAsyncResult ar) { var socketServerResult = (SocketServerEventArgs) ar.AsyncState; try { if (!socketServerResult.IsConnected()) { throw new SocketException(); } socketServerResult.SetEndLength((uint)socketServerResult.EndReceive(ar)); if(socketServerResult.EndLength == 0) throw new SocketException(997); socketServerResult.CreateEndBuffer(socketServerResult.EndLength); Native.MemoryCopy(socketServerResult.
Code:
[FONT=Franklin Gothic Medium][FONT=Franklin Gothic Medium][COLOR=Red][COLOR=Black][FONT=Franklin Gothic Medium][FONT=Franklin Gothic Medium][COLOR=Red][COLOR=Black]EndBuffer[/COLOR][/COLOR][/FONT][/FONT], socketServerResult.Buffer, socketServerResult.EndLength); Received.InvokeSafely(this, socketServerResult); //Create a new buffer and start receiveing again socketServerResult.CreateBuffer(65535); socketServerResult.BeginReceive(ReceiveData); } catch (SocketException e) { socketServerResult.Disconnect(true);//Disconnect the client Disconnected.InvokeSafely(this, socketServerResult);Call the Disconnected Event if (e.SocketErrorCode == SocketError.IOPending) return; if (e.SocketErrorCode == SocketError.ConnectionReset) return; Console.WriteLine(e.Message); } } [/COLOR][/COLOR][/FONT][/FONT]
2- socketServerResult.SetEndLength((uint)socketServer Result.EndReceive(ar)); :Set the received buffer length
3- socketServerResult.CreateEndBuffer(socketServerRes ult.EndLength); : Create a new buffer with the received length from step 2
4-Native.MemoryCopy(socketServerResult.Buffer, socketServerResult.EndBuffer, socketServerResult.EndLength); :
Memory Copy is used to copy the values from Buffer to the EndBuffer so we need to declare it in a new class and add this code to it
Code:
using System.Runtime.InteropServices; namespace Asynchronous_SocketServer { public unsafe class Native { [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] private static extern void* Memcpy(void* dest, void* src, uint cont); public static void MemoryCopy(byte[] destination, byte[] source, uint count) { fixed (byte* dest = destination, src = source) Memcpy(dest, src, count); } } }
5- Received.InvokeSafely(this, socketServerResult); : Call our Received Event
So our end Socket.cs looking will be somthing like this
Code:
using System; using System.Net; using System.Net.Sockets; namespace Asynchronous_SocketServer { public class SocketServerEventArgs : EventArgs { public byte[] Buffer { get; private set; } public byte[] EndBuffer { get; private set; } public uint EndLength { get; private set; } public Socket Socket; public void CreateBuffer(uint size) { Buffer = new byte[size]; } public void CreateEndBuffer(uint size) { EndBuffer = new byte[size]; } public void SetEndLength(uint length) { EndLength = length; } public void BeginReceive(AsyncCallback receiveData) { if (Buffer == null) throw new ApplicationException("Buffer has not been set."); Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, receiveData, this); } public int EndReceive(IAsyncResult result) { return Socket.EndReceive(result); } public bool IsConnected() { return Socket.Connected; } public void Disconnect(bool reuseSocket) { if (Socket.Connected) { Socket.Disconnect(reuseSocket); } } } public class Sockets { private const int ListenLength = 500; public event EventHandler<SocketServerEventArgs> Connected; public event EventHandler<SocketServerEventArgs> Disconnected; public event EventHandler<SocketServerEventArgs> Received; private readonly Socket _socket; public Sockets(int port) { try { _socket = InitializeSocket(port); } catch (Exception e) { Console.WriteLine(e); } } private Socket InitializeSocket(int port) { var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, port)); socket.Listen(ListenLength); socket.BeginAccept(AcceptConnections, new SocketServerEventArgs()); return socket; } private void AcceptConnections(IAsyncResult ar) { var socketServerResult = (SocketServerEventArgs) ar.AsyncState; try { socketServerResult.Socket = _socket.EndAccept(ar); socketServerResult.CreateBuffer(65535); socketServerResult.BeginReceive(ReceiveData); Connected.InvokeSafely(this, socketServerResult); _socket.BeginAccept(AcceptConnections, new SocketServerEventArgs()); } catch (Exception e) { socketServerResult.Disconnect(true); Disconnected.InvokeSafely(this, socketServerResult); Console.WriteLine(e); } } private void ReceiveData(IAsyncResult ar) { var socketServerResult = (SocketServerEventArgs) ar.AsyncState; try { if (!socketServerResult.IsConnected()) { throw new SocketException(); } socketServerResult.SetEndLength((uint)socketServerResult.EndReceive(ar)); if(socketServerResult.EndLength == 0) throw new SocketException(997); socketServerResult.CreateEndBuffer(socketServerResult.EndLength); Native.MemoryCopy(socketServerResult.Buffer, socketServerResult.EndBuffer, socketServerResult.EndLength); Received.InvokeSafely(this, socketServerResult); socketServerResult.CreateBuffer(65535); socketServerResult.BeginReceive(ReceiveData); } catch (SocketException e) { socketServerResult.Disconnect(true); Disconnected.InvokeSafely(this, socketServerResult); if (e.SocketErrorCode == SocketError.IOPending) return; if (e.SocketErrorCode == SocketError.ConnectionReset) return; Console.WriteLine(e.Message); } } } }
-So it is the time to go to our program.cs and add this code to it ! (pretty much self explained)
Code:
using System; using System.Text; namespace Asynchronous_SocketServer { class Program { /// <summary> /// TODO: .BeginSend Function /// </summary> static void Main() { Console.BackgroundColor = ConsoleColor.DarkBlue; Console.Clear(); const int authPort = 13000; var sockets = new Sockets(authPort); sockets.Connected += OnClientConnect;//Set the event handler sockets.Received += OnRecievePacket;//Set the event handler sockets.Disconnected += OnClientDisconnect;//Set the event handler Console.Title = "Asynchronous Socket Server Using Port : " + authPort;//Set the console Title Writer("Listening...", ConsoleColor.Yellow);//Console.WriteLine("Listening..."); Console.Read();//Prevent console exiting } private static void OnClientConnect(object sender, SocketServerEventArgs e) { Writer("Client Connected", ConsoleColor.Gray);//WriteLine (Client Connected) } private static void OnRecievePacket(object sender, SocketServerEventArgs e) { var message = Encoding.ASCII.GetString(e.EndBuffer);//Set the value from the readed EndBuffer var length = e.EndLength;//Get the EndLength Writer("Message Recieved : " + message, ConsoleColor.Green);//WriteLine (Message) Writer("Recieved Length : " + length, ConsoleColor.Green);//WriteLine (Length) } private static void OnClientDisconnect(object sender, SocketServerEventArgs e) { Writer("Client Disconnected", ConsoleColor.Gray);//WriteLine (Client Disconnected) } private static void Writer(string message, ConsoleColor color) { Console.ForegroundColor = color; Console.WriteLine(message); } } }
Build the solution and run it and your server will be running and ready to accept client connections.
V. Conclusion
In the end, you should have learned how to create a socket for your server development also you know what is asynchronous calls and how to use them; However, this guide is just a first step to socket programming.
NOTE :
●If you want to download the solution and test it yourself, you will find it in the attachment.
●Feel free to correct me if i failed to explain something (This is my first tutorial so i may be explained something wrong).
●Also ask any question here and i will be helping you as far as i can...
Best regards.
Danial Eugen .