HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > ИНФО > Статьи
   
 
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 23.02.2011, 01:00
GHYJaxon4
Новичок
Регистрация: 23.04.2026
Сообщений: 0
С нами: 33290

Репутация: 0
По умолчанию

Проект mySockLib


Краткое предисловие:

Сегодня я решил опубликовать одну из своих сетевых библиотек, написанную ещё давно для облегчённой работы с сокетами.

Эта библиотека довольно сильно облегчает создание различных клиентских приложений и не подгружает много лишнего кода.

Описание функционала:

function mySock_Startup: Integer; - инициализирует сетевую библиотеку WinSock2 для дальнейшей работы.

Даже в многопоточном приложении вполне достаточно одного вызова инициализации библиотеки.

После завершения работы с библиотекой должна быть вызвана функция деинциализации mySock_Cleanup столько же раз,

сколько была вызвана функция инициализации.

function mySock_Open(Host: String; Port: Word): TSocket; - создаёт сокет и выполняет соединение.

В случае успешного вызова возвращает дескриптор сокета, в случае ошибки возвращает 0.

В параметре Host может быть передано как и доменное имя, так и IP адрес.

function mySock_SetTimeout(Sock: TSocket; millisec: Integer): Integer; - задаёт время жизни сокета в миллисекундах. Например, если вы хотите задать сокету время жизни равное 10 секундам - то параметр millisec должен быть равен 10000.

После истечения таймаута вы уже не сможете работать с этим сокетом и вам придётся его закрыть.

function mySock_SetBlockMode(Sock: TSocket; NoneBlock: Boolean): Integer; - Несложно догадатьсе, что данная функция задаёт режим работы сокета.

Соответственно, если параметр NoneBlock будет равен True - то сокет будет переведён в неблокирующий, в случае с False - сокет будет переведён в блокирующий режим.

Различия блокирующего и неблокирующего режимов состоят в том, что в блокирующем режиме сокет будет ждать, пока данные отправятся/примуться, что вызывает так сказать блокирование и подвисание программы на этом этапе.

В неблокирующем режиме сокет не будет ждать данных и в случае ошибки сразу же вернёт -1.

Так как данные всёже не приходят и не уходят мгновенно - то рекомендую перед приёмом/передачей использовать функции mySock_WaitRecv/mySock_WaitSend соответственно.

function mySock_WaitSend(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean; - как я уже сказал выше, данная функция проверяет готовность сокета к отправке данных втечении определённого таймаута.

В случае вызова без параметров mySock_WaitSend(mySock) - интервал ожидания булет равен 15 секундам.

В случае, если за указанный интервал сокет будет готов к передаче данных - то функция вернёт True иначе False

При использовании данной функции вам следует учитывать, что если процесс затянется - то это вызовет блокировку (подвисание) программы на указанный интервал.

Пример вызова для ожидания в 10.450 секунд:

mySock_WaitSend(mySock, 10, 450)

function mySock_WaitRecv(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean; - вызов данной функции аналогичен предыдущей, заисключением что она используется для ожидания готовности принять данные.

function mySock_Send(Sock: TSocket; Buff: Pointer; Size: Integer): Integer; - функция предназначена для передачи данных.

В случае успешной передачи вернёт количество переданных байт.

В случае ошибки вернёт -1.

Buff - указатель на данные, которые нужно передать.

Size - объём переданных данных в байтах.

Пример передачи текстового заголовка req: String:

mySock_Send(mySock, PChar(req), Length(req));

Важно понимать, что если предстоит передать/принять некоторый объём данных - то не обязательно передавать/принимать всё сразу.

Это можно делать порциями, используя промежуточный буффер, равный например 1024 байта

function mySock_Recv(Sock: TSocket; Buff: Pointer; Size: Integer): Integer; - вызов этой функции немного отличается от вызова предыдущей.

Часто мы заранее не знаем, какой объём данных нам предстоит принять.

Поэтому оптимальным решением будет выделить некоторый объём памяти под временный буффер, а после получения всех данных его освободить.

Функция может вернуть целое положительное число в случае удачной приёмки данных,

0 - в случае закрытия сервером сокета и отсутствии данных для приёма и

соответственно -1 - в случае ошибки.

Пример организации приёмки данных в цикле:

Код:
{ Пытаемся взять память для буфера }
    Buff := GetMemory(BuffSize);
    if (Buff = nil) then raise Exception.Create('Error! Cannot get memory!');
    Size := 0;
    Stream.Position := 0;
    Resp := '';
    try
      Repeat
        { В случае таймаута ожидания данных - выходим }
        if not mySock_WaitRecv(Sock, TmSec, TmMillisec) then Exit;
        { Чистим память буфера }
        ZeroMemory(Buff, BuffSize);
        { Принимаем данные }
        iSize := mySock_Recv(Sock, Buff, BuffSize);
        { В случае ошибки - выходим }
        if (iSize  0) then
                            begin
                              { Пример записи принятых данных в поток }
                              Stream.Write(Buff^, iSize);
                              { Пример добавления принятых данных в строку }
                              Resp := Resp + Copy(Buff, 1, iSize);
                              { Увеличиваем счётчик всего принятых байт }
                              Inc(Size, iSize);
                            end;
      Until (iSize = 0);
    finally
      FreeMemory(Buff);
    end;
function mySock_Close(Sock: TSocket): Integer; - закрывает сокет и освобождает все ресурсы, занимаемые им.

function mySock_Cleanup: Integer; - как я уже говорил, эта функция предназначена для инициализации сокетной библиотеки.

Не забывайте, вызывать деинициализацию после окончания пользования библиотекой.

procedure mySock_GetLastError(var ErrCode: Integer; var ErrStr: String); - очень полезная процедура для отладочных целей.

Я постарался собрать наиболее частые сетевые ошибки и составить их описания.

В переменную ErrCode запишется код последней возникшей ошибки, а в переменную ErrStr - запишется её описание.

Послесловие:

Ну вот собственно и подошла к логическому финалу моя первая статья.

Надеюсь, что кому-то это окажется полезным.

Я постарался сделать исходник и описание наиболее простыми и понятными. Вы можете дополнить этот исходный код по своему усмотрению. Если что-то будет непонятно - пишите в этой теме, я постараюсь объяснить.


Особую благодарность я хотел бы выразить пользователю под ником Slesh, а также всем, кто помогал мне в освоении темы сетевого программирования.

Исходный код модуля:

Код:
unit mySockLib;

interface

uses
  Windows, WinSock2;

type
  TSocket = DWORD;

function mySock_Startup: Integer;
function mySock_Open(Host: String; Port: Word): TSocket;
function mySock_SetTimeout(Sock: TSocket; millisec: Integer): Integer;
function mySock_SetBlockMode(Sock: TSocket; NoneBlock: Boolean): Integer;
function mySock_WaitSend(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean;
function mySock_WaitRecv(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean;
function mySock_Send(Sock: TSocket; Buff: Pointer; Size: Integer): Integer;
function mySock_Recv(Sock: TSocket; Buff: Pointer; Size: Integer): Integer;
function mySock_Close(Sock: TSocket): Integer;
function mySock_Cleanup: Integer;
procedure mySock_GetLastError(var ErrCode: Integer; var ErrStr: String);

implementation

function mySock_Startup: Integer;
var
  wData: TWSAData;
begin
  Result := WSAStartup($0202, wData);
end;

function mySock_Open(Host: String; Port: Word): TSocket;
var
  Sock: TSocket;
  HostEnt: PHostEnt;
  SockAddr: TSockAddrIn;
begin
  Result := 0;
  HostEnt := GetHostByName(PChar(Host));
  if (HostEnt = nil) then Exit;
  Sock := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  if (Sock = INVALID_SOCKET) then Exit;
  ZeroMemory(@SockAddr, SizeOf(SockAddr));
  With SockAddr do
    begin
      sin_family := AF_INET;
      sin_port := HToNS(Port);
      With sin_addr, HostEnt^ do
        s_addr := PDWORD(h_addr^)^;
    end;
  if (Connect(Sock, @SockAddr, SizeOf(SockAddr)) <> 0) then
                                                           begin
                                                             CloseSocket(Sock);
                                                             Exit;
                                                           end;
  Result := Sock;
end;

function mySock_SetTimeout(Sock: TSocket; millisec: Integer): Integer;
var
  tv: TTimeVal;
begin
  tv.tv_sec := 0;
  tv.tv_usec := millisec;
  Result := setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, @tv, SizeOf(tv));
end;

function mySock_SetBlockMode(Sock: TSocket; NoneBlock: Boolean): Integer;
var
  iMode: DWORD;
begin
  iMode := DWORD(NoneBlock);
  Result := IOCtlSocket(Sock, FIONBIO, iMode);
end;

function mySock_WaitSend(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean;
var
  FDSet: TFDSet;
  tv: TTimeVal;
begin
  Result := False;
  FD_ZERO(FDSet);
  FD_SET(Sock, FDSet);
  tv.tv_sec := sec;
  tv.tv_usec := millisec * 1000;
  if (select(0, nil, @FDSet, nil, @tv) = 1) then Result := True;
end;

function mySock_WaitRecv(Sock: TSocket; sec: Integer = 15; millisec: Integer = 0): Boolean;
var
  FDSet: TFDSet;
  tv: TTimeVal;
begin
  Result := False;
  FD_ZERO(FDSet);
  FD_SET(Sock, FDSet);
  tv.tv_sec := sec;
  tv.tv_usec := millisec * 1000;
  if (select(0, @FDSet, nil, nil, @tv) = 1) then Result := True;
end;

function mySock_Send(Sock: TSocket; Buff: Pointer; Size: Integer): Integer;
begin
  Result := send(Sock, Buff^, Size, 0);
end;

function mySock_Recv(Sock: TSocket; Buff: Pointer; Size: Integer): Integer;
begin
  Result := recv(Sock, Buff^, Size, 0);
end;

function mySock_Close(Sock: TSocket): Integer;
begin
  Result := CloseSocket(Sock);
end;

procedure mySock_GetLastError(var ErrCode: Integer; var ErrStr: String);
begin
  ErrCode := WSAGetLastError;
  Case ErrCode of
   0: ErrStr := 'Ошибки нет';
   WSANOTINITIALISED: ErrStr := 'Нужно сначала вызвать функцию WSASturtup';
	 WSAENETDOWN: ErrStr := 'Проблема с сетью';
	 WSAEADDRINUSE: ErrStr := 'Указанный адрес уже используется';
	 WSAEPROTONOSUPPORT: ErrStr := 'Протокол не поддерживается';
	 WSAEPROTOTYPE: ErrStr := 'Некорректный протокол для данного типа сокета';
	 WSAEFAULT: ErrStr := 'Параметры name и namelen не соответствуют данной адресации';
	 WSAEINPROGRESS: ErrStr := 'Уже выполняется операция в блокирующем режиме';
	 WSAEINVAL: ErrStr := 'Сокет уже связан с адресом';
   WSAESOCKTNOSUPPORT: ErrStr := 'Некорректный сокет для данной адресации';
	 WSAENOBUFS: ErrStr := 'Недостаточно памяти';
	 WSAENOTSOCK: ErrStr := 'Неверный дескриптор сокета';
   WSAEISCONN: ErrStr := 'Сокет уже подключён';
   WSAEMFILE: ErrStr := 'Нет больше доступных дескрипторов';
   else ErrStr := 'Неизвестная ошибка';
  end;
end;

function mySock_Cleanup: Integer;
begin
  Result := WSACleanup;
end;
end.
 
Ответить с цитированием
 



Предыдущая тема Следующая тема

Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.