Модераторы: Snowy, Poseidon, MetalFan

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многопоточный сервак 
:(
    Опции темы
Antony41
Дата 15.4.2013, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



какого типа должен быть Buff
PM MAIL   Вверх
Antony41
Дата 15.4.2013, 20:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



не работает то принимает то нет, если размер структуры больше 1000 уже не принимает
PM MAIL   Вверх
Antony41
Дата 16.4.2013, 07:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



разобрался немного еще отпишусь
PM MAIL   Вверх
Чучмек
Дата 16.4.2013, 09:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  15.4.2013,  18:13 Найти цитируемый пост)
какого типа должен быть Buff 

Код

 I := recv(S, (PAnsiChar(@Buff)+iStillToReceive)^, SizeOf(Buff)-iStillToReceive , 0)
или
Код
    
 I := recv(S, Buff[iStillToReceive] , SizeOf(Buff)-iStillToReceive , 0)







--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 17.4.2013, 17:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



какой сервер лучше? многопоточный? синхронный асинхронный? синхронный и асинхронный это однопоточные? Могу ли я мспользовать WSAAsyncSelect в многопоточном сервере?

Добавлено через 12 минут и 1 секунду
система асинхронных сокетов прикольная и удобная. вобще мне нужно разработать так, чтобы сервер максимально быстро реагировал на входящие события сокетов и так же быстро рассылал им ответное сообщение, на данный момент, могу использовать 8 ядерный Core i7 или серверный комп
PM MAIL   Вверх
Чучмек
Дата 17.4.2013, 23:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  17.4.2013,  17:59 Найти цитируемый пост)
какой сервер лучше? многопоточный? синхронный асинхронный? синхронный и асинхронный это однопоточные?

Сколько одновременных подключений ожидается?
Сколько времени будет занимать обработка данных от клиента + подготовка ответа?
Все что ниже - лишь мое мнение.
Если несколько подключений - однозначно многопоточный в блокирующем режиме (простота,распределение нагрузки по ядрам процессора, быстрота реакции)
Если большое количество подключений -  асинхронный.
Если важна максимально быстрая реакция сервера на запросы клиента или  процессорозатраты на обработку всех соединений не влезают в один поток (в производительность одного ядра), то необходим многопоточный (число потоков равно числу ядер) асинхронный.
Цитата(Antony41 @  17.4.2013,  17:59 Найти цитируемый пост)
 Могу ли я мспользовать WSAAsyncSelect в многопоточном сервере?

На каждый поток должно быть свое окно со своей очередью сообщений. Соединения должны распределятся между потокам.
Окна придется создавать на WinAPI.
Или используй другой способ работы с сокетами в неблокирующем режиме.


Это сообщение отредактировал(а) Чучмек - 17.4.2013, 23:09


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 18.4.2013, 10:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



сейчас пытаюсь сделать так
создал класс Tserver=class (TWinControl) //окно? окно!
в нем создал слушающий serversocket, и событие WSAAsyncSelect, передающее FD_Accept в это же окно TServer

при обработке сообщения внутри тела TServer, создается дополнительный класс TConnection = class(TWinControl), и в неё заполняются все данные о подключенном сокете, далее  указатель на PConnection добавляется в список TList и связывает WSAAsyncSelect с окном TConnection
как такой подход?

Код

const
  WM_ACCEPTMESSAGE = WM_USER + 1;
  WM_SOCKETMESSAGE = WM_USER + 2;

type
  TServer = class;
  TWMSocketMessage = packed record
    Msg: Cardinal;
    Socket: TSocket;
    SockEvent: Word;
    SockError: Word;
  end;

  PConnection = ^TConnection;
  TConnection = class(TWinControl)
    private
      Owner: TServer;
      procedure WMSocketMessage(var Msg: TWMSocketMessage); message WM_SOCKETMESSAGE;
    public
    ClientSocket: TSocket;
    ClientAddr: string;
    MsgSize: Integer;
    Msg: string;
    Offset: Integer;
    BytesLeft: Integer;
    SendRead: Boolean;
    constructor Create(AOwner: TServer);
    destructor Destroy; override;
  end;

  TServer = class(TWinControl)
  private
    vWSAData: TWSAData;
    FHost: AnsiString;
    procedure Startup; //разрешить сокетное использование
    function Cleaned: Boolean; //запретить сокетн. использование
    procedure ClearConnections;
    procedure WMAcceptMessage(var Msg: TWMSocketMessage); message WM_ACCEPTMESSAGE;
  public
    FPort: Word; //только чтение
    Connections: TList;
    vServerSocket: TSocket;//серверный сокет который принимает входящие подключения
    vServerAddr: TSockAddr; // Адрес, к которому привязывается слушающий сокет
    function Start_Server(Port: Word): Boolean;
    constructor Create;
    destructor Destroy; override;
    // Метод GetConnectionBySocket находит в списке FConnections
    // запсись, соответствующую данному сокету
    function GetConnectionBySocket(S: TSocket): PConnection;


  end;
...


Добавлено через 1 минуту и 15 секунд
получается типа многопоточности?

Добавлено через 3 минуты и 51 секунду
если учитывать что AsyncSelect переводит сокет в отдельный поток
получается при каждом подключении создается сокет и заполняется в класс TWinControl и переводится в отдельный поток, получая в свою очередь события в виде сообщений

Добавлено через 4 минуты и 36 секунд
или это не так
PM MAIL   Вверх
Чучмек
Дата 18.4.2013, 10:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  18.4.2013,  10:20 Найти цитируемый пост)
получается типа многопоточности? 

А где сами потоки? Нет? Какая тогда многопоточность?

Цитата(Antony41 @  18.4.2013,  10:20 Найти цитируемый пост)
Tserver=class (TWinControl) //окно? окно!

Цитата(Чучмек @  17.4.2013,  23:08 Найти цитируемый пост)
со своей очередью сообщений



Цитата(Antony41 @  18.4.2013,  10:20 Найти цитируемый пост)
TWinControl

Цитата(Чучмек @  17.4.2013,  23:08 Найти цитируемый пост)
WinAPI

В Delphi отдельную очередь сообщений  для окон с объектной оберткой (TWinControl) можно сделать только через Ж., и то, не уверен. 


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 18.4.2013, 16:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



ну сообщения приходят нормально и программа работает без ошибок

Добавлено через 44 секунды
былы конечно проблемы с CreateWnd, но они решились перекрытием метода
PM MAIL   Вверх
Antony41
Дата 18.4.2013, 17:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



вроде работает хорошо вот полный код
unit ServerSock;

interface

uses Dialogs, Classes, WinSock, Errunit, Controls, Forms, Messages, Windows,
SysUtils;

const
  WM_ACCEPTMESSAGE = WM_USER + 1;
  WM_SOCKETMESSAGE = WM_USER + 2;

type
  TServer = class;
  TWMSocketMessage = packed record
    Msg: Cardinal;
    Socket: TSocket;
    SockEvent: Word;
    SockError: Word;
  end;
  TTransportPhase = (tpReceiveLength, tpReceivestring, tpSendstring);
  PConnection = ^TConnection;
  TConnection = class(TWinControl)
    private
      Owner: TServer;
      procedure WMSocketMessage(var Msg: TWMSocketMessage); message WM_SOCKETMESSAGE;
    public
    ClientSocket: TSocket;
    ClientAddr: AnsiString;
    MsgSize: Integer;
    Msg: AnsiString;
    Phase: TTransportPhase;
    Offset: Integer;
    BytesLeft: Integer;
    SendRead: Boolean;
    constructor Create(AOwner: TServer);
    destructor Destroy; override;
  end;

  TServer = class(TWinControl)
  private
    vWSAData: TWSAData;
    FHost: AnsiString;
    procedure Startup; //разрешить сокетное использование
    function Cleaned: Boolean; //запретить сокетн. использование
    procedure ClearConnections;
    procedure WMAcceptMessage(var Msg: TWMSocketMessage); message WM_ACCEPTMESSAGE;
  public
    FPort: Word; //только чтение
    Connections: TList;
    vServerSocket: TSocket;//серверный сокет который принимает входящие подключения
    vServerAddr: TSockAddr; // Адрес, к которому привязывается слушающий сокет
    function Start_Server(Port: Word): Boolean;
    constructor Create;
    destructor Destroy; override;
    // Метод GetConnectionBySocket находит в списке FConnections
    // запсись, соответствующую данному сокету
    function GetConnectionBySocket(S: TSocket): PConnection;


  end;

function GetErrorstring(Error: Integer = 0): string;

implementation

uses Mainunit;

{ TServer }

function GetErrorstring(Error: Integer = 0): string;
var
  Buffer: array[0..2047] of AnsiChar;
begin
  FormatMessage(forMAT_MESSAGE_FROM_SYSTEM, nil, Error, $400,
    @Buffer, SizeOf(Buffer), nil);
  Result := Buffer;
end;

procedure TServer.ClearConnections;
var
  I: Integer;
begin
  for I := 0 to Connections.Count - 1 do 
  begin
    closesocket(PConnection(Connections[I])^.ClientSocket);
    PConnection(Connections[I])^.Destroy;
  end;
  Connections.Clear;
end;

constructor TServer.Create;
var
  Params:TCreateParams;
begin
  inherited Create(Application.MainForm);
  Parent := Application.MainForm;
  HandleNeeded;
  CreateParentedControl(Application.MainFormHandle);
  Connections := TList.Create;
  Startup;
end;

destructor TServer.Destroy;
var
  Res: Integer;
begin
  if Assigned(Connections) then 
    begin
      closesocket(vServerSocket);
      ClearConnections;
      Cleaned;
      Connections.Free;
      Connections := nil;
    end;
  DestroyHandle;
  inherited;
end;

function TServer.GetConnectionBySocket(S: TSocket): PConnection;
var
  I: Integer;
begin
  for I := 0 to Connections.Count - 1 do 
    if PConnection(Connections[I]).ClientSocket = S then 
    begin
      Result := Connections[I];
      Exit;
    end;
  Result := nil;
end;

function TServer.Cleaned: Boolean;
begin
  Result := False;
  if WSACleanup = SOCKET_ERROR then  //Говорим что сокеты больше не будут использоваться
    begin
      Err_(WSAGetLastError);
      Exit;
    end;
  Logging('Сервер остановлен');
  Result := True;
end;

procedure TServer.Startup;
var
  Res: Integer;
begin
  Res := WSAStartup($101, vWSAData); //Разрешаем использ. сокетов
  if Res <> 0 then  //если ошибка передадим её в обработку ошибок
    Err_(Res);
end;

function TServer.Start_Server(Port: Word): Boolean;
begin
  Result := False;
  FPort := 0;
  // Формируем адрес для привязки.
  FillChar(vServerAddr.sin_zero, SizeOf(vServerAddr.sin_zero), 0);
  vServerAddr.sin_family := AF_INET;
  vServerAddr.sin_addr.S_addr := INADDR_ANY;
  try
    vServerAddr.sin_port := htons(Port);
    if vServerAddr.sin_port = 0 then 
    begin
      Logging('Номер порта должен лежать в диапазоне 1-65535');
      //MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0);
      Exit;
    end;
    // Создание сокета
    vServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if vServerSocket = INVALID_SOCKET then 
    begin
      Err_(WSAGetLastError);
      //MessageDlg(''#13#10 +InttoStr(WSAGetLastError), mtError, [mbOK], 0);
      Exit;
    end;
    // Привязка сокета к адресу
    if bind(vServerSocket, vServerAddr, SizeOf(vServerAddr)) = SOCKET_ERROR then 
    begin
      Err_(WSAGetLastError);
      //MessageDlg('Ошибка при привязке сокета к адресу:'#13#10 + GetErrorstring, mtError, [mbOK], 0);
      closesocket(vServerSocket);
      Exit;
    end;
    // Перевод сокета в режим прослушивания
    if listen(vServerSocket, SOMAXCONN) = SOCKET_ERROR then 
    begin
      Err_(WSAGetLastError);
      //MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 + GetErrorstring, mtError, [mbOK], 0);
      closesocket(vServerSocket);
      Exit;
    end;
    // Связь слушающего сокета с событием FD_ACCEPT
    if WSAAsyncSelect(vServerSocket, Handle, WM_ACCEPTMESSAGE, FD_ACCEPT) = SOCKET_ERROR then 
    begin
      Err_(WSAGetLastError);
      //MessageDlg('Ошибка при установке асинхронного режима слушающего сокета:'#13#10 + GetErrorstring, mtError, [mbOK], 0);
      closesocket(vServerSocket);
      Exit;
    end;
    // Перевод элементов управления в состояние "Сервер работает"
    Logging('Сервер работает');
    FPort := Port;
    Result := True;
  except
    on  EConvertError do 
      // Это исключение может возникнуть только в одном месте -
      // при вызове StrToInt(EditPortNumber.Text)
      begin
        Logging('"' + InttoStr(Port) + '" не является целым числом');
        //MessageDlg('"' + InttoStr(Port) + '" не является целым числом', mtError, [mbOK], 0);
      end;
    on  ERangeError do 
    begin
      // Это исключение может возникнуть только в одном месте -
      // при присваивании значения номеру порта
      Logging('Номер порта должен лежать в диапазоне 1-65535');
      MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0);
    end;
  end;
end;

procedure TServer.WMAcceptMessage(var Msg: TWMSocketMessage);
var
  NewConnection: PConnection;
  // Сокет, который создаётся для вновь подключившегося клиента
  ClientSocket: TSocket;
  ClientAddr: TSockAddr; // Адрес подключившегося клиента
  AddrLen: Integer; // Длина адреса
begin
  // Стр###мся от "тупой" ошибки
  if Msg.Socket <> vServerSocket then 
    begin
      Logging('Внутренняя ошибка сервера - неверный серверный сокет');
      Exit;
    end;
  // Обрабатываем ошибку на сокете, если она есть.
  if Msg.SockError <> 0 then 
  begin
    Logging('Ошибка при подключении клиента. Код:' + IntToStr(Msg.SockError) +
    ' Сервер будет остановлен.');
    ClearConnections;
    closesocket(vServerSocket);
    on StopServer;
    Exit;
  end;
  // Стр###мся от ещё одной "тупой" ошибки
  if Msg.SockEvent <> FD_ACCEPT then 
    begin
      Logging('Внутренняя ошибка сервера - неверное событие на сокете');
      Exit;
    end;
  AddrLen := SizeOf(TSockAddr);
  ClientSocket := accept(vServerSocket, @ClientAddr, @AddrLen);
  if ClientSocket = INVALID_SOCKET then 
  begin
    // Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает,
    // что на данный момент подключений нет, а вообще всё в порядке,
    // поэтому ошибку WSAEWOULDBLOCK мы просто игнорируем. Прочие же
    // ошибки могут произойти только в случае серьёзных проблем,
    // которые требуют остановки сервера.
    if WSAGetLastError <> WSAEWOULDBLOCK then 
    begin
      Logging('Ошибка при подключении клиента. Сервер будет остановлен');
      ClearConnections;
      closesocket(vServerSocket);
      on StopServer;
    end;
  end
  else
  begin
    // Создаём запись для нового подключения и заполняем её
    New(NewConnection);
    NewConnection^ := TConnection.Create(Self);
    NewConnection.ClientSocket := ClientSocket;
    NewConnection.ClientAddr := format('%u.%u.%u.%u:%u', [
          Ord(ClientAddr.sin_addr.S_un_b.s_b1),
          Ord(ClientAddr.sin_addr.S_un_b.s_b2),
          Ord(ClientAddr.sin_addr.S_un_b.s_b3),
          Ord(ClientAddr.sin_addr.S_un_b.s_b4),
          ntohs(ClientAddr.sin_port)]);
    NewConnection.Phase := tpReceiveLength;
    NewConnection.Offset := 0;
    NewConnection.BytesLeft := SizeOf(Integer);
    NewConnection.SendRead := False;
    // Связываем сообщение с новым сокетом
    if WSAAsyncSelect(ClientSocket, NewConnection.Handle, WM_SOCKETMESSAGE, FD_READ or FD_WRITE or FD_CLOSE) = SOCKET_ERROR then 
    begin
      Logging('Ошибка при установке асинхронного режима подключившегося сокета.');
      closesocket(ClientSocket);
      Exit;
    end;

    // Добавляем запись нового соединения в список
    Connections.Add(NewConnection);
    Logging('Зафиксировано подключение с адреса ' + NewConnection.ClientAddr);
  end;
end;

{ TConnection }

constructor TConnection.Create(AOwner: TServer);
begin
  inherited Create(Application.MainForm);
  Parent := Application.MainForm;
  HandleNeeded;
  Owner := AOwner;
end;

destructor TConnection.Destroy;
begin
  DestroyHandle;
  inherited;
end;

procedure TConnection.WMSocketMessage(var Msg: TWMSocketMessage);
var
  Connection: PConnection;
  Res: Integer;
  // Вспомогательная процедура, освобождающая ресурсы, связанные
  // с клиентом и удаляющая запись подключения из списка
  procedure RemoveConnection;
  begin
    closesocket(Connection.ClientSocket);
    Owner.Connections.Remove(Connection);
    Connection^.Destroy;
    //Dispose(Connection);
  end;

begin
  // Ищем соединение по сокету
  Connection := Owner.GetConnectionBySocket(Msg.Socket);
  if Connection = nil then 
  begin
    Logging('Внутренняя ошибка сервера - не найдено соединение для сокета');
    Exit;
  end;
  // Проверяем, были ли ошибки при взаимодействии
  if Msg.SockError = 10050 then 
  begin
    ShowMessage('1');
    Logging('Ошибка при взаимодейсвии с клиентом ' + Connection.ClientAddr +
      ': ' + Inttostr(Msg.SockError));
    RemoveConnection;
    Exit;
  end;
  // Анализируем, какое событие произошло
  case Msg.SockEvent of
    FD_READ:
      begin
        // Проверяем, на каком этапе находится взаимодействие с клиентом.
        if Connection.Phase = tpReceiveLength then 
        begin
          // Этап получения от клиента длины строки. При выполнении этого
          // этапа сервер получает от клиента длину строки и размещает её
          // в поле Connection.MsgSize. Здесь приходится учитывать, что
          // теоретически даже такая маленькая (4 байта) посылка может
          // быть разбита на несколько пакетов, поэтому за один раз этот
          // этап не будет завершён, и второй раз его придётся продолжать,
          // загружая оставшиеся байты. Connection.Offset - количество
          // уже прочитанных на данном этапе байт - одновременно является
          // смещением, начиная с которого заполняется буфер.
          Res := recv(Connection.ClientSocket, (PAnsiChar(@Connection.MsgSize) + Connection.Offset)^,
            Connection.BytesLeft, 0);
          if Res > 0 then 
          begin
            Logging('Клиент шлет данные...');
            // Если Res > 0, это означает, что получено Res байт.
            // Соответственно, увеличиваем на Res количество прочитанных
            // на данном этапе байт и на такую же величину уменьшаем
            // количество оставшихся.
            Inc(Connection.Offset, Res);
            Dec(Connection.BytesLeft, Res);
            // Если количество оставшихся байт равно нулю, можно переходить
            // к следующему этапу.
            if Connection.BytesLeft = 0 then 
            begin
              // Проверяем корректность принятой длины строки
              if Connection.MsgSize <= 0 then 
              begin
                Logging('Неверная длина строки от клиента ' + Connection.ClientAddr +
                  ': ' + IntToStr(Connection.MsgSize));
                RemoveConnection;
                Exit;
              end;
              // Следующий этап - это чтение самой строки
              Connection.Phase := tpReceivestring;
              // Пока на этом этапе не прочитано ни одного байта
              Connection.Offset := 0;
              // Осталось прочитать Connection.MsgSize байт
              Connection.BytesLeft := Connection.MsgSize;
              // Сразу выделяем память под строку
              SetLength(Connection.Msg, Connection.MsgSize);
            end;
          end
          else
          if Res = 0 then 
          begin
            logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение');
            RemoveConnection;
            Exit;
          end
          else
            // Ошибку WSAEWOULDBLOCK игнорируем, т.к. она говорит
            // только о том, что входной буфер сокета пуст, но в целом
            // всё в порядке - такое вполне возможно при ложных
            // срабатываниях сообщения
            if WSAGetLastError <> WSAEWOULDBLOCK then 
            begin
              logging('Ошибка при получении данных от клиента ' +
                Connection.ClientAddr + ': ' + GetErrorstring);
              RemoveConnection;
              Exit;
            end;
        end
        else
        if Connection.Phase = tpReceivestring then 
        begin
          // Следующий этап - чтение строки. Он практически не отличается
          // по реализации от этапа чтения длины строки, за исключением
          // того, что теперь буфером, куда помещаются полученные от клиента
          // данные, служит не Connection.MsgSize, а Connection.Msg.
          Res := recv(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],
            Connection.BytesLeft, 0);
          if Res > 0 then 
          begin
            Inc(Connection.Offset, Res);
            Dec(Connection.BytesLeft, Res);
            // Если количество оставшихся байт равно нулю, можно переходить
            // к следующему этапу.
            if Connection.BytesLeft = 0 then 
            begin
              Logging('От клиента ' + Connection.ClientAddr +
                ' получена строка: ' + Connection.Msg);
              // Преобразуем строку. В отличие от предыдущих примеров, здесь
              // мы явно добавляем к строке #0. Это связано с тем, что при
              // отправке, которая тоже может быть выполнена не за один раз,
              // мы указываем индекс того символа строки, начиная с которого
              // нужно отправлять данные. И (хотя теоретически вероятность
              // этого очень мала) может возникнуть ситуация, когда за
              // один раз будут отправлены все символы строки, кроме
              // завершающего #0, и тогда при следующей отправке начинать
              // придётся с него. Если мы будем использовать тот #0, который
              // добавляется к концу строки автоматически, то в этом случае
              // индекс выйдет за пределы диапазона. Поэтому мы вручную
              // добавляем ещё один #0 к строке, чтобы он стал законной
              // её частью.
              Connection.Msg := AnsiUpperCase(stringReplace(Connection.Msg, #0, '#0', [rfReplaceAll])) +
                ' (AsyncSelect server)'#0;
              // Следующий этап - отправка строки клиенту
              Connection.Phase := tpSendstring;
              // Отправлено на этом этапе 0 байт
              Connection.Offset := 0;
              // Осталось отправить Length(Connection.Msg) байт.
              // Единицу к длине строки, в отличие от предыдущих примеров,
              // не добавляем, т.к. там эта единица нужна была для того,
              // чтобы учесть добавляемый к строке автоматически символ #0.
              // Здесь мы ещё один #0 добавили к строке явно, поэтому
              // он уже учтён в функции Length.
              Connection.BytesLeft := Length(Connection.Msg);
              // Ставим в очередь сообщение с событием FW_WRITE.
              // Его получение заставит сервер отправить данные
              PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_WRITE);
            end;
          end
          else if Res = 0 then 
          begin
            Logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение');
            RemoveConnection;
            Exit;
          end
          else
            // Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем
            if WSAGetLastError <> WSAEWOULDBLOCK then 
            begin
              Logging('Ошибка при получении данных от клиента ' + Connection.ClientAddr + ': ' + GetErrorstring);
              RemoveConnection;
              Exit;
            end;
        end
        else if Connection.Phase = tpSendstring then 
          // Если сервер находится на этапе отправки данных,
          // а событие FD_READ всё же произошло, отмечаем это
          Connection.SendRead := True;
      end;
    FD_WRITE:
      begin
        if Connection.Phase = tpSendstring then 
        begin
          // При наступлении события FD_WRITE проверяем, находится ли
          // сервер на этапе отправки данных, и если да, отправляем их
          Res := send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],
            Connection.BytesLeft, 0);
          if Res > 0 then 
          begin
            Inc(Connection.Offset, Res);
            Dec(Connection.BytesLeft, Res);
            // Если Connections.BytesLeft = 0, значит, строка отправлена
            // полностью.
            if Connection.BytesLeft = 0 then 
            begin
              Logging('Клиенту ' + Connection.ClientAddr + ' отправлена строка: ' + Connection.Msg);
              // Очищаем строку просто чтобы сэкономить память
              Connection.Msg := '';
              // Следующий этап - снова получение длины строки от клиента
              Connection.Phase := tpReceiveLength;
              // Получено - 0 байт
              Connection.Offset := 0;
              // Осталось прочитать столько, сколько занимает целое число
              Connection.BytesLeft := SizeOf(Integer);
              // Если были промежуточные события FD_READ, вызываем их
              // снова искусственно
              if Connection.SendRead then 
              begin
                PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_READ);
                Connection.SendRead := False;
              end;
            end;
          end
          else
            if WSAGetLastError <> WSAEWOULDBLOCK then 
            begin
              Logging('Ошибка при отправке данных клиенту ' + Connection.ClientAddr + ': ' + GetErrorstring);
              RemoveConnection;
              Exit;
            end;
        end;
      end;
    FD_CLOSE:
      begin
        // Клиент вызвал функцию shutdown. Закрываем соединение.
        Logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение');
        shutdown(Connection.ClientSocket, SD_BOTH);
        RemoveConnection;
      end;
    else
      begin
        Logging('Неверное событие при обмене с клиентом ' + Connection.ClientAddr);
        RemoveConnection;
      end;
  end;
end;

end.

Это сообщение отредактировал(а) Antony41 - 18.4.2013, 17:12
PM MAIL   Вверх
Чучмек
Дата 18.4.2013, 22:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Antony41, это однопоточный. Задержка в обработке одного соединения приведет к временной блокировке всех остальных.
Цитата(Чучмек @  17.4.2013,  23:08 Найти цитируемый пост)
Сколько одновременных подключений ожидается?
Сколько времени будет занимать обработка данных от клиента + подготовка ответа?

Сколько???



--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 19.4.2013, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



думаю одновременно 3-4 подключения (пока) обработка данных будет занимать минимум времени, короче клиент выполняет свою задачу локально, а подключается к серверу "лицензий" для того чтобы посмотреть в каком состоянии его лицензия
по сути сервер будет отсылать всего один пакет, который скажет что всё ОК или нет.
PM MAIL   Вверх
Чучмек
Дата 19.4.2013, 10:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  19.4.2013,  09:57 Найти цитируемый пост)
3-4 подключения

Я в какой то момент подумал, что счет пойдет на тысячи постоянных соединений.

Создавать отдельное окно на каждое соединение? Все можно обработать одним.


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 19.4.2013, 13:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



ок с этим разобрались код который выше я привел, впринципе работает нормально, но там происходит при нескольких подключениях OutOfMemory, немогу отловить... еще выдается исключение InvalidPointer при уничтожении подкласса Connection точнее тут
Код

procedure RemoveConnection;
  begin
    closesocket(Connection.ClientSocket);
    Owner.Connections.Remove(Connection);
    Connection^.Destroy;
    //Dispose(Connection);
  end;

тоже не могу понять, если убрать
Connection^.Destroy; то всё ОК, остается только OutOfMemory

Добавлено через 24 секунды
помогите найти пож. время поджимает
PM MAIL   Вверх
Чучмек
Дата 19.4.2013, 15:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Ищи где утечка памяти. Где память выделяется но не освобождается, объект  создается но не уничтожается.
Какой длинны передаешь строки?
Цитата(Antony41 @  18.4.2013,  17:08 Найти цитируемый пост)
  New(NewConnection);
    NewConnection^ := TConnection.Create(Self);

Это лишнее. Объект и так указатель.
Достаточно
Код

var NewConnection:TConnection;
...
NewConnection := TConnection.Create(Self);
...
...
Connections.Add(NewConnection);
 




Это сообщение отредактировал(а) Чучмек - 19.4.2013, 15:41


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0616 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.