• XSS.stack #1 – первый литературный журнал от юзеров форума

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

Jes

floppy-диск
Пользователь
Регистрация
25.01.2008
Сообщения
1
Реакции
0
Хотел на Ачате выложить , а он седня с*** не пашет , ну для поддержания проэкта >

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 даёт нам вод такой пример :
Код:
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# мы получим вот такой код :
Код:
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#)
В итоге , уточнив типы данных, приходим вот такому , наиболее точному коду :

Код:
[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 импортируем саму функуцию:

Код:
 [DllImport("ws2_32.dll")]
        static extern Int32 WSAStartup(Int16 wVR, WSA_Data lpWSAD);

при этом обьявим константу для версии сокета...

public const short WORD_VERSION = 36;

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

Используем вот так:

Код:
WSA_Data wsaData = new WSA_Data();

            if (WSAStartup(WORD_VERSION, wsaData) != 0)
            {
                MessageBox.Show("WSAStartup error");
            }

Соответственно нам понадобится узнавать ошибки:
WSAGetLastError обьявдяется так же как и в C++ ...

Код:
   [DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern Int32 WSAGetLastError();

Новичкам следует заметить , что для вывода результата , например в MessageBox ,нужно преобразовать его в строку (прим. в текст)...

Код:
  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 тип, который используется для представления указателей или дескрипторов...

Код:
  [DllImport("wsock32.dll")]
        static extern IntPtr socket(long af, long s_type, long protocol);


константы:
Код:
public const int AF_INET = 2;
public const int SOCK_STREAM = 1;
public const int PPROTO_TCP =6
public const int PPROTO_UDP = 17

Конструкция:
Код:
IntPtr s = socket(AF_INET, SOCK_STREAM, PPROTO_TCP);

 if (s.ToInt32() != 0)
          {
               MessageBox.Show("Socket Error:" + WSAGetLastError().ToString());
          }
Теперь переидем к функции bind. Функция bind ассациирет (привязывает) сокет к локальному адресу.

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

Код:
public struct sockaddr
        {
            public short sin_family;
            public short sin_port;
            public int sin_addr;
            public long sin_zero;
        }

Код:
  [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-разрядное двоичное число (с сетевым порядком байтов)).
Код:
        [DllImport("ws2_32.dll")]
        static extern short htons(int hostshort);

        [DllImport("wsock32.dll")]
        static extern int inet_addr(string cp);

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

Код:
 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]

небольшая подсказка:
Код:
[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#
http://www.intuit.ru/department/pl/csharp/
http://www.gotdotnet.ru/LearnDotNet/CSharp/default.aspx
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх