[Intermediate] - C# AsyncSockets(Client)

08/17/2008 12:16 tanelipe#1
Hello everyone! This is my second tutorial I'll be writing on AsyncSockets and also the last one (I've then covered both Client/Server sides of it)

So lets start off with the tutorial

Requirements
- Microsoft Visual C# 2008 Express Edition
- .NET Framework 3.5

I'll be numbering the steps in my opinion it's alot easier to follow that way.


1. Start your Visual C# 2008 Express Edition and create a new project (File -> New Project -> Microsoft forms application) and name it.

2. Now you have to add a new class in this project (Project > Add class) Name it 'ClientSocket.cs' so it's easier to follow this tutorial.

3. Now the code should look something like this :
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace <YourNameSpace>
{
    class ClientSocket
    {
    }
}
4. Now we need to remove the namespace <name> { } so it can be accessed globally. Also we have to add a baseclass to handle the packets etc.
5. We also need to add the namespaces to beable to use sockets. The code should look like this after these steps.
Code:
using System;
using System.Net;
using System.Net.Sockets;

class BSocket
{

}
class ClientSocket
{

}
6. Alright now we need to add this to the top
Code:
using System.Runtime.InteropServices;
7. Now we need to make a new class to handle unsafe methods, I call the class Native. Add this to your project too
Code:
unsafe class Native
{
    [DllImport("msvcrt.dll")]
    public static extern void* memcpy(void* dest, void* src, uint size); 
}
8. You might have noticed that we can't compile it yet; Because of that unsafe.
9. We need to allow unsafe code in the project so go to Project > Properties > Build > Check the 'Allow unsafe code' box.
10. Now like on the ServerSocket part, we need to add the variables into BSocket class to handle all the information needed.
11. What we need is byte[] for buffer, int(s) for ID & BufferLength & a socket

12. The BSocket code should look like this :
Code:
class BSocket
{
    public BSocket(uint bSize)
    {
        Buffer = new byte[bSize];
    }
    public Socket wSocket;
    public int ID = -1,
               BufferLength = 0;
    public byte[] Buffer;
    public unsafe byte[] Packet
    {
        get
        {
            byte[] ret = new byte[BufferLength];
            fixed (byte* des = ret, src = Buffer)
                Native.memcpy(des, src, (uint)BufferLength);
            return ret;
        }
    }
}
13. Now we need to add delegates into the project so we can make our custom events (OnClientConnect, OnReceivePacket etc)

14. Add this code after the BSocket but before ClientSocket
Code:
delegate void SocketEvent(object Sender, BSocket Socket);
delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
15. The code should look like this now :
Code:
delegate void SocketEvent(object Sender, BSocket Socket);
delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
class ClientSocket
{

}
16. Now we need to add the actual events, so add this code inside your ClientSocket class
Code:
public event SocketEvent OnClientConnect,
                             OnReceivePacket;
public event SocketErrorEvent OnClientError;
17. Now we need to add instance of BSocket in it so add this to code (inside ClientSocket class) :
Code:
private BSocket Socket;
18. Now we need to add a method that takes the IP and Port as params for example like this :
Code:
 public void Connect(string IP, ushort Port)
    {
        if (IP == "" || Port == 0)
            return;
        Socket = new BSocket(0xFFFF);
        Socket.wSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress RemoteIP;
        if (!IPAddress.TryParse(IP, out RemoteIP))
            return;
        Socket.wSocket.BeginConnect(RemoteIP, Port, new AsyncCallback(Connecting), Socket);
    }
19. Now as you can see, the method will stop executin on following things, IP is a null string, or Port is 0. A little bit lower there is that part where it parses the ip into IPAddress, if it fails to do that (Invalid ip) It'll stop executing the method.

20. Now we need to add a method for that AsyncCall back 'Connecting'. So add this to your code
Code:
private void Connecting(IAsyncResult res)
    {
        BSocket Sckt = (BSocket)res.AsyncState;
        try
        {
            Sckt.wSocket.EndConnect(res);
            if (OnClientConnect != null)
                OnClientConnect(this, Sckt);
            Sckt.wSocket.BeginReceive(Sckt.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Sckt);
        }
        catch (Exception e)
        {
            if (OnClientError != null)
                OnClientError(this, Sckt, e.Message);
        }
    }
21. This basically triggers the OnClientConnect event, will explain this later on : how to use it.

22. Now we need to add a ReceivePacket method so add this to your code
Code:
private void ReceivePacket(IAsyncResult res)
    {
        BSocket Sckt = (BSocket)res.AsyncState;
        try
        {
            SocketError Error;
            Sckt.BufferLength = Sckt.wSocket.EndReceive(res, out Error);
            if (Error == SocketError.Success && Sckt.BufferLength > 0)
            {
                if (OnReceivePacket != null)
                    OnReceivePacket(this, Sckt);
                Sckt.wSocket.BeginReceive(Sckt.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Sckt);
            }
        }
        catch (Exception e)
        {
            if (OnClientError != null)
                OnClientError(this, Sckt, e.Message);
        }
    }
23. We still need to add a method to send bytes. So add this to your code

Code:
 public void Send(byte[] Packet)
    {
        if (Socket.wSocket.Connected)
            Socket.wSocket.Send(Packet);
    }
24. It'll send the bytes only if the Socket is connected somewhere. Now the final code should look like this
Code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

unsafe class Native
{
    [DllImport("msvcrt.dll")]
    public static extern void* memcpy(void* dest, void* src, uint size); 
}
class BSocket
{
    public BSocket(uint bSize)
    {
        Buffer = new byte[bSize];
    }
    public Socket wSocket;
    public int ID = -1,
               BufferLength = 0;
    public byte[] Buffer;
    public unsafe byte[] Packet
    {
        get
        {
            byte[] ret = new byte[BufferLength];
            fixed (byte* des = ret, src = Buffer)
                Native.memcpy(des, src, (uint)BufferLength);
            return ret;
        }
    }
}
delegate void SocketEvent(object Sender, BSocket Socket);
delegate void SocketErrorEvent(object Sender, BSocket Socket, string Error);
class ClientSocket
{
    public event SocketEvent OnClientConnect,
                             OnReceivePacket;
    public event SocketErrorEvent OnClientError;

    private BSocket Socket;

    public void Connect(string IP, ushort Port)
    {
        if (IP == "" || Port == 0)
            return;
        Socket = new BSocket(0xFFFF);
        Socket.wSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress RemoteIP;
        if (!IPAddress.TryParse(IP, out RemoteIP))
            return;
        Socket.wSocket.BeginConnect(RemoteIP, Port, new AsyncCallback(Connecting), Socket);
    }
    public void Send(byte[] Packet)
    {
        if (Socket.wSocket.Connected)
            Socket.wSocket.Send(Packet);
    }
    private void Connecting(IAsyncResult res)
    {
        BSocket Sckt = (BSocket)res.AsyncState;
        try
        {
            Sckt.wSocket.EndConnect(res);
            if (OnClientConnect != null)
                OnClientConnect(this, Sckt);
            Sckt.wSocket.BeginReceive(Sckt.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Sckt);
        }
        catch (Exception e)
        {
            if (OnClientError != null)
                OnClientError(this, Sckt, e.Message);
        }
    }
    private void ReceivePacket(IAsyncResult res)
    {
        BSocket Sckt = (BSocket)res.AsyncState;
        try
        {
            SocketError Error;
            Sckt.BufferLength = Sckt.wSocket.EndReceive(res, out Error);
            if (Error == SocketError.Success && Sckt.BufferLength > 0)
            {
                if (OnReceivePacket != null)
                    OnReceivePacket(this, Sckt);
                Sckt.wSocket.BeginReceive(Sckt.Buffer, 0, 0xFFFF, SocketFlags.None, new AsyncCallback(ReceivePacket), Sckt);
            }
        }
        catch (Exception e)
        {
            if (OnClientError != null)
                OnClientError(this, Sckt, e.Message);
        }
    }
}
25. Now you can go to your Form and test this code. This is example how I tested it

Code:
public Form1()
        {
            InitializeComponent();
            ClientSocket ClientSocket = new ClientSocket();
            ClientSocket.Connect("127.0.0.1", 9958);
            ClientSocket.OnClientError += new SocketErrorEvent(ClientSocket_OnClientError);
            ClientSocket.OnClientConnect += new SocketEvent(ClientSocket_OnClientConnect);
            ClientSocket.OnReceivePacket += new SocketEvent(ClientSocket_OnReceivePacket);
        }

        void ClientSocket_OnReceivePacket(object Sender, BSocket Socket)
        {
            MessageBox.Show("We go a packet");
        }

        void ClientSocket_OnClientConnect(object Sender, BSocket Socket)
        {
            MessageBox.Show("Wohoo, we connected succesfully!");
        }

        void ClientSocket_OnClientError(object Sender, BSocket Socket, string Error)
        {
            MessageBox.Show(Error);
        }
Now if you have any questions about the code or I made a mistake somewhere, please tell me.
08/17/2008 14:55 © Haydz#2
Once Again Very Good Tutorial :)
08/18/2008 06:55 joven3000#3
what is this i dont understand it will make soc ur items or nothing just a name name
08/18/2008 07:53 Hiyoal#4
Lol, Search Sockets on google. This has nothing to do with Conquer.

Hiyoal
08/18/2008 10:14 tanelipe#5
Wikipedia :

Quote:
An Internet socket (or commonly, a network socket or socket), is an end-point of a bidirectional process-to-process communication flow across an IP based network, such as the Internet. Each socket is mapped to an application process or thread. A socket is an interface between an application process or thread and the TCP/IP protocol stack provided by the operating system.
[Only registered and activated users can see links. Click Here To Register...] :

Quote:
When using non-blocking sockets, you have to continually poll the socket to see whether or not you have any actions that need to be performed. These actions can include sending and receiving data, establishing a connection, sending back a response to a connection request, etc. Non-blocking sockets have the advantage in that they allow normal program execution to continue – meaning you can process data while waiting for more to come in. This immediately makes them superior to blocking sockets in many cases (but not all).

However, asynchronous sockets take polling to a new level – they do it for you. This means that you can focus on other things while WinSock keeps watch over your sockets. (Sounds better than non-blocking sockets eh? Not always, as you’ll soon see.)
So, basically async sockets do not freeze the thread (a.k.a you can continue doing what ever you want). Sync sockets freezes the thread (Well, wait for incoming data etc)
08/18/2008 12:20 XtremeX-CO#6
:/ Its a good tutorial, but I rather use blocking sockets, to wait until a packet is received, then continue parsing the packet.. but do nothing until that. You can create other threads to handle everything you need done, while the 2 recv threads are working (and blocking if packets are coming in slower.... lol)
09/01/2008 16:53 Tw3ak#7
Quote:
Originally Posted by XtremeX-CO View Post
:/ Its a good tutorial, but I rather use blocking sockets, to wait until a packet is received, then continue parsing the packet.. but do nothing until that. You can create other threads to handle everything you need done, while the 2 recv threads are working (and blocking if packets are coming in slower.... lol)
That is the problem with the existing Private co sources,You don't want to use blocking as it freezes up the server threads and fubards the stability in connections ending up in complete server crashes.

You want to use AsyncSockets and have proper checking and error handling,Not just assume all the data being sent/received is correct resulting in malformed packets and possible race conditions.
09/02/2008 03:01 XtremeX-CO#8
Quote:
Originally Posted by Tw3ak View Post
That is the problem with the existing Private co sources,You don't want to use blocking as it freezes up the server threads and fubards the stability in connections ending up in complete server crashes.

You want to use AsyncSockets and have proper checking and error handling,Not just assume all the data being sent/received is correct resulting in malformed packets and possible race conditions.
Yeah I havent really used non blocking sockets that much, but it all depends on the application. I really need to study more :(