Hello everyone! In this tutorial I will teach you how to do simple ServerSocket and ClientSocket implementation using Asynchronous Sockets (non blocking, doesn't wait for data & doesn't freeze the current thread)
Requirement(s)
- Microsoft Visual C# 2008 Express Edition
- .NET Framework 3.5 (I'm not sure wether this comes with the above program or not.)
I will be using that program on all my C# tutorials so get ready to download it. :D
I'll start off with Async ServerSocket; I'll add the ClientSocket part later on.
I'll show how to implement it step by step. Starting from scratch.
I'll be numbering the steps (1, 2, 3...)
Alright, lets begin! I hope you got all the required stuff already.
1. Create a new Project (File -> New Project) There should open a list of the project types you can create
2. Pick the "Windows Forms Application". Give it what ever name you like (A little bit down from the project list). And then click OK.
3. Add a class to the project (Project -> Add Class & Give it a name) I'll be naming it SocketSystem.cs
3.5 Now go to Project > <Namespace> Properties > Build > Check the "allow unsafe code"
4. Now the window should look like this
5. Change the code to look like this :
(When it doesn't have a namespace it's globally usable in your project; Thanks Infamous for this information)
6. You might wonder why did we create the BSocket. Well it's because we need instance to handle the Sockets Buffer, Packet, ID and so on.
7. I've also added those "using System.Net;" and so on, these are required to create a wrapper around sockets. And the System.Collections is used to access Hashtable in the code.
8. Now we need to add all the variables needed to the BSocket class, to identify each sockets properly.
9. After the adding all the variables; The BSocket class should look like
10. Now that we have the BSocket ready, We have to add delegates to handle the correct events (OnConnect, OnReceive etc)
11. Add these lines
So the code looks like :
12. Now we need all the variables set for ServerSocket.
13. What we need is a Socket, NextID (For client identifying), something to hold the clients and ofcourse events based on those delegates.
14. This is how I did it :
15. Now we need to add functions to start listening and closing the server
16. Lets make the Listen void first, add this to the code
17. This will start the ServerSocket to listen on specified port. Server.Listen(100); Indicates how many user there will be queue
18. Now we need to add that AcceptConnection void that the above code exaomple takes as a param for AsyncCallback.
19. So add this to the code
20. I will later on explain how to access the events; (OnClientConnect etc) Did you notice that this started new AsyncCallback for receiving?
21. Now we need to add a function to handle the receiving packet so add this to your code.
22. You might have noticed that the InvokeDC hasn't been implemented yet, lets get on that.
23. Add this to your code
24. Basically it takes the BSocket as a param and resets it, and invokes the Disconnect.
25. We're still missing the the Closing down so lets add that now.
26. WE HAVE IT DONE! Lets take a look how our code should look now
27. How to use this code now, You can go to you Main form and add this to the code.
If you need any help with the following code, please post it here. I might have missed something.
Any feedback is greatly appreciated.
Requirement(s)
- Microsoft Visual C# 2008 Express Edition
- .NET Framework 3.5 (I'm not sure wether this comes with the above program or not.)
I will be using that program on all my C# tutorials so get ready to download it. :D
I'll start off with Async ServerSocket; I'll add the ClientSocket part later on.
I'll show how to implement it step by step. Starting from scratch.
I'll be numbering the steps (1, 2, 3...)
Alright, lets begin! I hope you got all the required stuff already.
1. Create a new Project (File -> New Project) There should open a list of the project types you can create
2. Pick the "Windows Forms Application". Give it what ever name you like (A little bit down from the project list). And then click OK.
3. Add a class to the project (Project -> Add Class & Give it a name) I'll be naming it SocketSystem.cs
3.5 Now go to Project > <Namespace> Properties > Build > Check the "allow unsafe code"
4. Now the window should look like this
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
class SocketSystem
{
}
}
(When it doesn't have a namespace it's globally usable in your project; Thanks Infamous for this information)
Code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;
public unsafe class Native
{
[DllImport("msvcrt.dll")]
public static extern unsafe void* memcpy(void* dest, void* src, uint size);
}
class BSocket
{
}
class ServerSocket
{
}
7. I've also added those "using System.Net;" and so on, these are required to create a wrapper around sockets. And the System.Collections is used to access Hashtable in the code.
8. Now we need to add all the variables needed to the BSocket class, to identify each sockets properly.
9. After the adding all the variables; The BSocket class should look like
Code:
class BSocket
{
public Socket wSocket;
public int ID = -1, BufferLength = 0;
public byte[] Buffer;
public unsafe byte[] Packet
{
get
{
byte[] ret = new byte[BufferLength];
fixed (byte* src = Buffer, des = ret)
Native.memcpy(des, src, (uint)BufferLength);
return ret;
}
}
public BSocket(uint BufferSize)
{
Buffer = new byte[BufferSize];
}
}
11. Add these lines
Code:
delegate void SocketEvent(object sender, BSocket Socket); delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
Code:
delegate void SocketEvent(object sender, BSocket Socket);
delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
class ServerSocket
{
}
13. What we need is a Socket, NextID (For client identifying), something to hold the clients and ofcourse events based on those delegates.
14. This is how I did it :
Code:
public event SocketEvent OnClientConnect,
OnReceivePacket;
public event SocketErrorEvent OnClientDisconnect;
public Hashtable Connections = new Hashtable(200);
public Socket Server;
private int NextID = 0;
16. Lets make the Listen void first, add this to the code
Code:
public void Listen(ushort Port)
{
if (Port == 0) return;
IPEndPoint Bind = new IPEndPoint(IPAddress.Any, Port);
Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Server.Bind(Bind);
Server.Listen(100);
Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF));
}
18. Now we need to add that AcceptConnection void that the above code exaomple takes as a param for AsyncCallback.
19. So add this to the code
Code:
private void AcceptConnection(IAsyncResult res)
{
try
{
BSocket Socket = (BSocket)res.AsyncState;
Socket.wSocket = Server.EndAccept(res);
Socket.ID = NextID++;
Connections.Add(Socket.ID, Socket);
if (OnClientConnect != null)
OnClientConnect(this, Socket);
Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF));
Socket.wSocket.BeginReceive(Socket.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Socket);
}
catch (Exception) { Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF)); }
}
21. Now we need to add a function to handle the receiving packet so add this to your code.
Code:
private void ReceivePacket(IAsyncResult res)
{
BSocket Socket = (BSocket)res.AsyncState;
SocketError Error;
try
{
if (Socket.wSocket.Connected)
{
Socket.BufferLength = Socket.wSocket.EndReceive(res, out Error);
if (Error == SocketError.Success && Socket.BufferLength > 0)
{
if (OnReceivePacket != null)
OnReceivePacket(this, Socket);
Socket.wSocket.BeginReceive(Socket.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Socket);
}
else
{
InvokeDC(Socket, "Error == " + Error + "");
}
}
else { InvokeDC(Socket, "Lost connection."); }
}
catch (Exception E)
{
InvokeDC(Socket, E.ToString());
}
}
23. Add this to your code
Code:
private void InvokeDC(BSocket Socket, string Reason)
{
if (Socket == null)
return;
if (OnClientDisconnect != null)
OnClientDisconnect(this, Socket, Reason);
Socket.Buffer = null;
Socket.BufferLength = -1;
Socket.ID = -1;
Socket.IsAlive = false;
Socket.wSocket = null;
Socket = null;
}
25. We're still missing the the Closing down so lets add that now.
Code:
public void Close()
{
foreach (DictionaryEntry DE in Connections)
{
BSocket BSocket = (BSocket)DE.Value;
InvokeDC(BSocket, "User shutdown server");
}
Server.Close();
Connections.Clear();
}
Code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Runtime.InteropServices;
public unsafe class Native
{
[DllImport("msvcrt.dll")]
public static extern unsafe void* memcpy(void* dest, void* src, uint size);
}
class BSocket
{
public Socket wSocket;
public int ID = -1, BufferLength = 0;
public byte[] Buffer;
public unsafe byte[] Packet
{
get
{
byte[] ret = new byte[BufferLength];
fixed (byte* src = Buffer, des = ret)
Native.memcpy(des, src, (uint)BufferLength);
return ret;
}
}
public BSocket(uint BufferSize)
{
Buffer = new byte[BufferSize];
}
}
delegate void SocketEvent(object sender, BSocket Socket);
delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
class ServerSocket
{
public event SocketEvent OnClientConnect,
OnReceivePacket;
public event SocketErrorEvent OnClientDisconnect;
public Hashtable Connections = new Hashtable(200);
public Socket Server;
private int NextID = 0;
public void Listen(ushort Port)
{
if (Port == 0) return;
IPEndPoint Bind = new IPEndPoint(IPAddress.Any, Port);
Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Server.Bind(Bind);
Server.Listen(100);
Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF));
}
public void Close()
{
foreach (DictionaryEntry DE in Connections)
{
BSocket BSocket = (BSocket)DE.Value;
InvokeDC(BSocket, "User shutdown server");
}
Server.Close();
Connections.Clear();
}
private void InvokeDC(BSocket Socket, string Reason)
{
if (Socket == null)
return;
if (OnClientDisconnect != null)
OnClientDisconnect(this, Socket, Reason);
Socket.Buffer = null;
Socket.BufferLength = -1;
Socket.ID = -1;
Socket.wSocket = null;
Socket = null;
}
private void AcceptConnection(IAsyncResult res)
{
try
{
BSocket Socket = (BSocket)res.AsyncState;
Socket.wSocket = Server.EndAccept(res);
Socket.ID = NextID++;
Connections.Add(Socket.ID, Socket);
if (OnClientConnect != null)
OnClientConnect(this, Socket);
Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF));
Socket.wSocket.BeginReceive(Socket.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Socket);
}
catch (Exception) { Server.BeginAccept(new AsyncCallback(AcceptConnection), new BSocket(0xFFFF)); }
}
private void ReceivePacket(IAsyncResult res)
{
BSocket Socket = (BSocket)res.AsyncState;
SocketError Error;
try
{
if (Socket.wSocket.Connected)
{
Socket.BufferLength = Socket.wSocket.EndReceive(res, out Error);
if (Error == SocketError.Success && Socket.BufferLength > 0)
{
if (OnReceivePacket != null)
OnReceivePacket(this, Socket);
Socket.wSocket.BeginReceive(Socket.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Socket);
}
else
{
InvokeDC(Socket, "Error == " + Error + "");
}
}
else { InvokeDC(Socket, "Lost connection."); }
}
catch (Exception E)
{
InvokeDC(Socket, E.ToString());
}
}
}
Code:
public Form1()
{
InitializeComponent();
ServerSocket Server = new ServerSocket();
Server.Listen(12345);
Server.OnClientConnect += new SocketEvent(Server_OnClientConnect);
Server.OnClientDisconnect += new SocketErrorEvent(Server_OnClientDisconnect);
Server.OnReceivePacket += new SocketEvent(Server_OnReceivePacket);
}
void Server_OnReceivePacket(object sender, BSocket Socket)
{
byte[] Packet = Socket.Packet;
}
void Server_OnClientDisconnect(object Sender, BSocket Socket, string Error)
{
MessageBox.Show("Client Disconnected");
}
void Server_OnClientConnect(object sender, BSocket Socket)
{
MessageBox.Show("Client Connected");
}
If you need any help with the following code, please post it here. I might have missed something.
Any feedback is greatly appreciated.