[Статья] WinSockets _C# или велосипед голыми руками

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Jes, 26 Jan 2008.

  1. Jes

    Jes Elder - Старейшина

    Joined:
    16 Apr 2007
    Messages:
    370
    Likes Received:
    391
    Reputations:
    34
    [ WinSockets _C# или велосипед голыми руками ]

    [интро]
    Собственно в этой статье нет ничего нового , особенного ...

    Я даже не могу толком сказать , для кого эта статья ориентирована ... в первую очередь для новичков в C# ...
    Предворительно , читатель уже разбирается в win сокетах , например знает C++ , в C# недавно...
    Вот , представим читающий знает C++ и изучая C# хочет работать с советыми максимально 'близко'...

    Но думаю начинающему изучать c# будет полезно ...

    [абоут]
    В статье мы будем использовать Winsock , не подключая System.net.sockets , а напрямую из ws2_32.dll и wsock32.dll , импортируя необходимые нам функции ...
    Я опишу , возможные ошибки и трудности с типами пременных, которые могут возникнуть...

    [go]
    ну ладно , теперь уже приступим :

    Как я уже говорил , мы будем импортировать функции из библиотек...
    пожалуй приступим:

    (инклудим dll-импорт)
    using System.Runtime.InteropServices;

    Для начала нам понадобится WSAStartup ...

    Подключив System.Net.Sockets мы вообще можем не думать о инициализации ...

    имея заголоваочный файл winsock2.h , в C++ эта функция задается вот так:

    WSADATA wdata;
    WSAStartup(MAKEWORD(2,2), &wdata);


    Здесь нам прилется самим обьявить и заполнить struct wsadata:
    MSDN даёт нам вод такой пример :
    Code:
    typedef struct WSAData {
      WORD wVersion;
      WORD wHighVersion;
      char szDescription[WSADESCRIPTION_LEN+1];
      char szSystemStatus[WSASYS_STATUS_LEN+1];
      unsigned short iMaxSockets;
      unsigned short iMaxUdpDg;
      char FAR* lpVendorInfo;
    } WSADATA, 
     *LPWSADATA;
    Переведя 'наскоком' на C# мы получим вот такой код :
    Code:
    public struct WSA_Data{
                public int wVersion;
                public int wHighVersion;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x101)] 
                public string szDescription;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x81)]
                public string szSystemStatus;
                public int iMaxSockets;
                public int iMaxUdpDg;
                public long lpVendorInfo;
    
    Но этот код будет несовсем точным т к C# очен требователен в отношении типов данных
    (см про явные и неявные преобразования в C#)
    В итоге , уточнив типы данных, приходим вот такому , наиболее точному коду :

    Code:
    [StructLayout(LayoutKind.Sequential)]
            public class WSA_Data
            {
                public Int16 wVersion;
                public Int16 wHighVersion;
                public String szDescription;
                public String szSystemStatus;
                public Int16 iMaxSockets;
                public Int16 iMaxUdpDg;
                public IntPtr lpVendorInfo;
            }
    Собственно вот они элементарные трудности перевода ...

    Теперь , имея свой WSA_Data импортируем саму функуцию:
    Code:
     [DllImport("ws2_32.dll")]
            static extern Int32 WSAStartup(Int16 wVR, WSA_Data lpWSAD);
    при этом обьявим константу для версии сокета...

    public const short WORD_VERSION = 36;

    Функцию мы подготовили ...

    Используем вот так:
    Code:
    WSA_Data wsaData = new WSA_Data();
    
                if (WSAStartup(WORD_VERSION, wsaData) != 0)
                {
                    MessageBox.Show("WSAStartup error");
                }
    
    Соответственно нам понадобится узнавать ошибки:
    WSAGetLastError обьявдяется так же как и в C++ ...

    Code:
      [DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            static extern Int32 WSAGetLastError();
    Новичкам следует заметить , что для вывода результата , например в MessageBox ,нужно преобразовать его в строку (прим. в текст)...

    Code:
     if (WSAStartup(WORD_VERSION, wsaData) != 0)
                {
                    MessageBox.Show(WSAGetLastError().ToString());
                }

    Далее перейдем к функции socket();
    msdn описание:

    SOCKET WSAAPI socket(
    __in int af,
    __in int type,
    __in int protocol
    );

    В случае успеха функция возвращает дискриптор нового сокета ... в С++ тип int почти универсален для функций ,в C# для дескриптора мы будем использовать специальный тип IntPtr.
    IntPtr – это platform-specific тип, который используется для представления указателей или дескрипторов...

    Code:
      [DllImport("wsock32.dll")]
            static extern IntPtr socket(long af, long s_type, long protocol);
    
    константы:
    Code:
    public const int AF_INET = 2;
    public const int SOCK_STREAM = 1;
    public const int PPROTO_TCP =6
    public const int PPROTO_UDP = 17
    Конструкция:
    Code:
    IntPtr s = socket(AF_INET, SOCK_STREAM, PPROTO_TCP);
    
     if (s.ToInt32() != 0)
              {
                   MessageBox.Show("Socket Error:" + WSAGetLastError().ToString());
              }
    Теперь переидем к функции bind. Функция bind ассациирет (привязывает) сокет к локальному адресу.

    Обьявим структуру sockaddr

    Code:
    public struct sockaddr
            {
                public short sin_family;
                public short sin_port;
                public int sin_addr;
                public long sin_zero;
            }

    Code:
     [DllImport("wsock32.dll")]
            public static extern int bind(IntPtr socket, ref sockaddr addr, int namelen);
    
    Новичкам следует обратить внимание на модификатр ref . "Этот модификатор одновременно является и in и out модификатором. Дело в том, что при использовании параметра с модификатором ref объект передается в метод по ссылке. В результате метод получает возможность изменить этот объект. Очень часть таким способом передаются массивы." (blog.excode.ru)

    для заполнения структуры нам понадобятся еще функции htons(преобразовывающая данные для их правильности при использовании WinSock) и inet_addr(для преобразования строки с IP-адресом в формате десятичное с точкой в 32-разрядное двоичное число (с сетевым порядком байтов)).

    Code:
      [DllImport("ws2_32.dll")]
            static extern short htons(int hostshort);
    
            [DllImport("wsock32.dll")]
            static extern int inet_addr(string cp);

    Теперь же создадим и заполним эту структуру , в итоге (обобщив) получим небольшую функцию:

    Code:
      public static bool AdvBind(string ipAddress, int port,IntPtr socketHandle)
            {
                sockaddr remoteAddress;            // Обьявляем 
                int resultCode = 0;            //это будет код резудбтата
                int errorCode = 0;                //а это код (в сучае) ошибки
                bool returnValue = false;            // то - что будет возвращать функция
    
                if (socketHandle != IntPtr.Zero)   // проверяем валидность сокета
                {
                    try
                    {
    
                        remoteAddress = new sockaddr();
                        remoteAddress.sin_family = AF_INET;
                        remoteAddress.sin_port = htons((short)port);  
                        remoteAddress.sin_addr = inet_addr(ipAddress);
                        remoteAddress.sin_zero = 0;
    
    
                        if (remoteAddress.sin_addr != 0)
                        {
    
                            resultCode = bind(socketHandle,ref remoteAddress,Marshal.SizeOf(remoteAddress));
                            errorCode = WSAGetLastError();
                            returnValue = (resultCode == 0);
                        }
                    }
                    catch
                    {
                        returnValue = false;
                    }
                }
                return returnValue;
            }
    
    Теперь уже , более мение разобравшись наиболее трудными (для новичка) с типами и структурами данного примера мы уже без труда можем оперировать с основными функциями WinSock ...

    <не буду повторяться продолжая писать уже однообразный код к каждой йункции , хотя при освоении языка мне этот процесс показался довольно интересным и увлекательным>
    Дальше вам поможет Msdn , Гугл и собственное стремление ...

    [bonus]

    небольшая подсказка:
    Code:
    [DllImport("wsock32.dll")]
            static extern int connect(IntPtr socket,sockaddr addr, int addrlen);
    
    [DllImport("wsock32.dll")]
            static extern int recv(IntPtr socket, string buf, int len, int flag);
    
    [DllImport("wsock32.dll")]
    static extern long WSAAsyncSelect(IntPtr socket, long hwnd, long iMsg, long lEvent);
    
    [DllImport("ws2_32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    static extern IntPtr accept(
              IntPtr socketHandle,
              ref sockaddr socketAddress, 
              ref int addressLength);
    
    [DllImport("wsock32.dll")]
    static extern int closesocket(IntPtr s);
    
    [DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            static extern Int32 WSACleanup();
    [outro]
    Я хотел описать все функции и впринципе можно было добавить их подробное описание , но посмотрев , скока я написал вначале , я решил остановиться на основных функциях...возможно , когда будет время и желание...
    Еще можно задуматься об актуальности данной статьи . Помог изобрести велосипед , можете сказать ... но это тоже полезно ...


    [bonus]
    Полезные ссылки по C#
    Code:
    http://www.intuit.ru/department/pl/csharp/
    http://www.gotdotnet.ru/LearnDotNet/CSharp/default.aspx
     
    #1 Jes, 26 Jan 2008
    Last edited: 26 Jan 2008
    6 people like this.
  2. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    729
    Reputations:
    266
    А почему не работать с сокетами средствами .net?
    Какое преимущество это дает?
     
  3. KEZ

    KEZ Ненасытный школьник

    Joined:
    18 May 2005
    Messages:
    1,604
    Likes Received:
    754
    Reputations:
    397
    ты че это нехакерский способ. вот, скажем, придумать способ писать asm-вставки в .net - это для античатовцев настоящих.
     
    1 person likes this.