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

Поиск:

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


Опытный
**


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

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



Привет всем!
Ребят нужна оперативная помощь.
Я пишу многопоточный сервер/клиент на TServerSocket и TClientSocket
Делаю так:
У меня есть запись(record) определенной структуры в ней есть переменная fCommand: Word;
Я заполняю эту структуру данными и передаю таким же образом эти данные принимаются затем структура заполняется даннымии распознается

проблема в том что в локальной сети всё без ошибок, а вот по инету помимо моих пакетов приходят еще какие то данные которые не распознаются (т.е. например вместо команды fCommand содержит 0), что это за данные, чем отличается передача данных в локальной сети от передачи через инет?
PM MAIL   Вверх
kami
Дата 11.2.2013, 19:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



как принимаешь и передаешь?
Что-то мне подсказывает (стандартная ошибка), что не учитываются результаты Socket.ReceiveBuf и Socket.SendBuf

Добавлено через 7 минут и 21 секунду
Цитата(Antony41 @  11.2.2013,  18:07 Найти цитируемый пост)
Я пишу многопоточный сервер/клиент на TServerSocket и TClientSocket

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


Опытный
**


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

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



Код

//Сама структура
type
  PData = ^TData;
  TData = packed record
    fCommand: Word;
    fNick: String[MAX_STRING];
    fMessages: array [0..MAX_CHAR - 1] of Char;
  end;



передаю так
Код

var
  GlobalData: TData;
  fRSData: TWinSocketStream;
implementation
//...
//отправка пакете
if FillTData(@GlobalData, 10010, Edit1.Text, Edit2.Text) then
  fRSData.Write(GlobalData, SizeOf(GlobalData));


FillTData это
Код

function TClient_Form.FillTData(aData: PData; aCommand: Word; aNick, aMessages: String): boolean;
var i: Integer;
begin
  try
    with aData^ do
      begin
        fCommand:=aCommand;
        fNick:=aNick;
        ZeroMemory(@fMessages, SizeOf(fMessages));
      end;
      for i := 0 to Length(aMessages) - 1 do
        aData^.fMessages[i]:=Char(aMessages[i + 1]);
      Result:=true;
  except
    Result:=false;
  end;
end;


//принимаю так. это соответственно происходит в потоке который постоянно проверяет на входящие данные
Код

...
private
  fData: PData;
...
...
const
  MAX_BUFF_IN = 32000;
var SizeBuf, i: Cardinal;
    MS: TMemoryStream;
    bufIn: array [0..MAX_BUFF_IN] of Char;
    fError: String;
begin
  fData:=@Client_Form.GlobalData;
...
SizeBuf := Client_Form.CS.Socket.ReceiveLength;
  if SizeBuf <> 0 then
    begin
      //Если длина принимаемых данных не равно нулю, то принимаем данные
      MS:=TMemoryStream.Create;
      try
        i:=0;
        bufIn:=#0;
        //Описание в серверной части
        repeat
          i:=Client_Form.fRSData.Read(bufIn, MAX_BUFF_IN);
          MS.Write(bufIn, i);
          SizeBuf:=Client_Form.CS.Socket.ReceiveLength;
        until SizeBuf = 0;
        MS.Seek(0, soFromBeginning);
        MS.Read(fData^, MS.Size);
      except
        on E: Exception do
        begin
          fData^.fCommand:=20010;
          fError:=E.Message;
        end;
      end;
      MS.Free;
      with fData^ do
      begin
        //Исходя из команды выполняем те или иные действия
        case fCommand of
          10000:
          begin
            fMSG := ('Сервисное сообщение'#13#10 + fNick +' вошел в чат');
          end;
          10010:
          begin
            fMSG := (fNick + '>>' + string(fMessages));
          end;
          20000:
          begin
            fMSG := ('Сервисное сообщение'#13#10 + fNick +' покинул чат');
          end;
          20010:
          begin
            fMSG := ('Сервисное сообщение'#13#10 + fError);
          end;
          30000:
          begin
            fMSG := ('Сервисное сообщение'#13#10'Сервер отключен');
            Client_Form.CS.Close;
          end;
          10:
          begin
            fMSG := ('Пришло сообщение о проверке соединения');
            Synchronize(Logger);
            //посылаем ответное сообщение что мы в сети
            fData^.fCommand := 11;
            //Client_Form.fRSData.Write(fData^, SizeOf(fData^));
            fMSG := ('Ответное сообщение отправлено');
          end;
          else
          begin
            fMSG := ('Command: '+IntToStr(fData.fCommand)+' Команда не распознана');
          end;
        end;
        Synchronize(Logger);
      end;
    end;
end;

PM MAIL   Вверх
Antony41
Дата 11.2.2013, 20:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Смотрите получается так:
сервак шлет данные и сразу получает какие то пустые пакеты, хотя клиент не отправлял данные я имею ввиду что команды или пакеты я не посылал в ответ серверу.
Предполагаю что когда сервер шлет какой либо пакет, то стандартно приходят какие то данные о подтверждении, что пакет этот доставлен, но так как у меня эти данные преобразутся в мою структуру, то я вижу пустой пакет(record)
возможен ли такой вариант?
PM MAIL   Вверх
kami
Дата 11.2.2013, 20:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(Antony41 @  11.2.2013,  19:59 Найти цитируемый пост)
MS.Read(fData^, MS.Size);

мне бы такую уверенность, что пришло данных ровно на 1 структуру, ни байтом больше или меньше.
Даже в локалке вероятность такого (+-) достаточно велика. Не говоря уже про глобальную сеть.
Ввести проверки, сколько пришло.
Если меньше - ждать следующей порции.
Если больше - выбирать SizeOf данных, а хвост - запоминать для стыковки со следующим пакетом.

Добавлено через 4 минуты и 8 секунд
Цитата(Antony41 @  11.2.2013,  20:23 Найти цитируемый пост)
то я вижу пустой пакет(record)

Поэтому нужно анализировать количество реально прочитанного
Цитата(Antony41 @  11.2.2013,  19:59 Найти цитируемый пост)
i:=Client_Form.fRSData.Read(bufIn, MAX_BUFF_IN);


Вот еще что непонятно - сокет создан в основном потоке, так (лежит-то он на форме)? Почему же обращение по выборке данных идет из дополнительного? Это несколько не-потокобезопасно.
PM MAIL WWW   Вверх
Antony41
Дата 11.2.2013, 20:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата

Вот еще что непонятно - сокет создан в основном потоке, так (лежит-то он на форме)? Почему же обращение по выборке данных идет из дополнительного? Это несколько не-потокобезопасно. 

Это ведь не Визуальный компонент почему не потокобезопасно?

Цитата

Поэтому нужно анализировать количество реально прочитанного


тоесть так?
Код

var
  len:Integer;
begin
  len := Client_Form.CS.Socket.ReceiveLength;
  i:=Client_Form.fRSData.Read(bufIn, len);
end;


Это сообщение отредактировал(а) Antony41 - 11.2.2013, 20:43
PM MAIL   Вверх
kami
Дата 11.2.2013, 20:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(Antony41 @  11.2.2013,  20:41 Найти цитируемый пост)
тоесть так?

не совсем. Под анализом я имел ввиду проверку i
Вот так делается у меня:
Код

  ReceiveCount := Socket.ReceiveLength;
  ReadPos := 0;

  if ReceiveCount = 0 then  
    exit  // ничего не считалось
  else
    if ReceiveCount > FReadBufferSize then // количество данных в сокете больше, чем приемный буфер
      begin
        FReadBufferSize := (ReceiveCount + 1024) and $FFFFFC00;
        ReallocMem(FReadBuffer, FReadBufferSize); // увеличиваем приемный буфер.
      end;

  Readed := Socket.ReceiveBuf(FReadBuffer[0], ReceiveCount); // считываем

  while ReadPos < Readed do // и дальше делаем всё исключительно на основе реально прочитанного, в вашем случае - вместо Readed будет i
    begin



Цитата(Antony41 @  11.2.2013,  20:41 Найти цитируемый пост)
Это ведь не Визуальный компонент почему не потокобезопасно?

Давайте возьмем за аксиому, что переключение между потоками происходит в любой момент. Этот момент определяется виндой и повлиять на него возможно только специальными методами, например - использованием объектов синхронизации (критические секции, мьютексы, евенты, сообщения).
Допустим, что Винда решила переключиться в основной поток после выполнения (а может, и во время выполнения) этой строчки:
Цитата(Antony41 @  11.2.2013,  19:59 Найти цитируемый пост)
SizeBuf := Client_Form.CS.Socket.ReceiveLength;

И в основном потоке сокет закрывается (ну, прервалось соединение - бывает и такое). И обратно переключается уже после того, как сокет закрыт.
Соответственно, всякие обращения типа Read, дальшейшее чтение ReceiveLength и т.п. будут как минимум невалидными, а как максимум - приведут к ошибке работы с памятью.

Добавлено через 3 минуты и 22 секунды
Цитата(kami @  11.2.2013,  20:49 Найти цитируемый пост)
овлиять на него возможно только специальными методами,

И то - далеко не факт, что произойдет моментальное переключение на нужный поток. Например, в случае использования критических секций это гарантировать нельзя. Некоторую гарантию дает только SendMessage, которая, в частности, используется (-лась, по крайней мере в D7) в Synchronize 
PM MAIL WWW   Вверх
Antony41
Дата 11.2.2013, 20:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



тоесть Вы хотите сказать, что лучше бы было использование компонента сокета внутри потока?
хотя эти действия у меня заключены в try except и finally
за примерчик спасибо отдельное smile 
PM MAIL   Вверх
kami
Дата 11.2.2013, 21:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(kami @  11.2.2013,  20:49 Найти цитируемый пост)
Вот так делается у меня:

забыл сказать - этот код из события OnRead компонента.
Заводить поток, чтобы без перерыва читать оттуда данные, а отправлять другие данные из основного потока - нельзя.

Добавлено через 4 минуты и 29 секунд
Цитата(Antony41 @  11.2.2013,  20:58 Найти цитируемый пост)
тоесть Вы хотите сказать, что лучше бы было использование компонента сокета внутри потока?

Как минимум - создание и активация сокета в OnExecute потока.
Но в этом случае для правильной работы сокета в потоке нужно организовать цикл выборки сообщений. В том случае, если тип у сокета (свойство ClientType)- ctNonBlocking. Если выставлен ctBlocking - ничем помочь не могу, не работал с этим режимом.

А чем вызвано желание сетевой работы именно в доп.потоке?
PM MAIL WWW   Вверх
Antony41
Дата 11.2.2013, 21:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(kami @  11.2.2013,  21:00 Найти цитируемый пост)
забыл сказать - этот код из события OnRead компонента.
Заводить поток, чтобы без перерыва читать оттуда данные, а отправлять другие данные из основного потока - нельзя. 

спасибо вот это не учел. Сейчас переделаю посмотрим что получится
PM MAIL   Вверх
kami
Дата 11.2.2013, 21:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(Antony41 @  11.2.2013,  20:58 Найти цитируемый пост)
за примерчик спасибо отдельное 

Это не примерчик, вот примерчик Реально использующийся уже несколько лет код, не претерпевший почти никаких изменений с тех пор smile

UPD: поменял ссылку на более поздний пост.

Это сообщение отредактировал(а) kami - 11.2.2013, 21:15
PM MAIL WWW   Вверх
Antony41
Дата 11.2.2013, 21:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Я так понимаю это всё в не блокирующем режиме? потому что в блокирующем у меня данные вообще не приходят в событии OnRead

Это сообщение отредактировал(а) Antony41 - 11.2.2013, 21:32
PM MAIL   Вверх
kami
Дата 11.2.2013, 21:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(Antony41 @  11.2.2013,  21:28 Найти цитируемый пост)
 это всё в не блокирующем режиме?

Цитата(kami @  11.2.2013,  21:00 Найти цитируемый пост)
 Если выставлен ctBlocking - ничем помочь не могу, не работал с этим режимом.

в блокирующем несколько другая система - там (вроде, могу ошибаться) на каждое соединение заводится отдельный поток, в котором всё и вертится. Но - он мне изначально не понравился, посему - даже не разбирался с ним. Это не значит, что блокирующий режим - плохой! Просто я не умею его готовить.

Кстати, там в примере - большой недостаток. Во всех событиях OnRead последним оператором должно быть Data.Free, иначе пойдут утечки.

Это сообщение отредактировал(а) kami - 11.2.2013, 21:41
PM MAIL WWW   Вверх
Antony41
Дата 11.2.2013, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Про сервер это так, но я имею ввиду клиента. т.е. сервер у меня в поточном режиме(blocking) а клиент впринципе тоже был в (blocking).
И вот теперь я сижу и думаю. Я понимаю что блочный режим для сервера нужен для того что бы поддерживать соединения в отдельном потоке и это нужно для того что бы сервак не зависал, НО зачем клиенту блочный режим ведь у него только одно соединение, или это нужно для поддержки соединений с несколькими серверами так что ли? В данном случае у меня один сервер и значит что мне не нужно использовать поточный режим для клиента?
PM MAIL   Вверх
kami
Дата 11.2.2013, 22:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 6
Всего: 72



Цитата(Antony41 @  11.2.2013,  21:47 Найти цитируемый пост)
 это нужно для того что бы сервак не зависал,

можно поспорить. Я не думаю, что у Вас в коде производится настолько ресурсоемкая работа с принимаемыми/отправляемыми данными (или их настолько много принимается/отправляется за единицу времени), что это замедлит работу основного потока.
Чаще всего, "заморозка" происходит из-за неправильных алгоритмов обработки данных в неблокирующем режиме.

Цитата(Antony41 @  11.2.2013,  21:47 Найти цитируемый пост)
НО зачем клиенту блочный режим ведь у него только одно соединение, или это нужно для поддержки соединений с несколькими серверами так что ли?

Один ClientSocket - одно соединение. А сделано, я думаю, чтобы использовать один и тот же подход к обработке данных. Согласитесь, нелогично было бы на клиенте ориентироваться на OnRead|OnWrite и т.п., а на сервере - использовать потоки.
"Всё должно быть единообразно - подстрижено, покрашено, посыпано песком" (с) армейское выражение.

Добавлено через 2 минуты и 41 секунду
Аргумент в пользу неблокирующего режима - создание на каждое соединение дополнительного потока считаю расточительством.
Что будет, если количество живых подключений к серверу будет в районе 1000? Вы представляете себе, как винде будет тяжко разрулить такое количество потоков?
PM MAIL WWW   Вверх
Antony41
Дата 13.2.2013, 17:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



впринципе переписал немного, получается что сервер работает в режиме blocking а клиент nonblocking, короче 

проблемка такова:
есть структура Record, и объект этой структуры GlobalData
указатель на эту структуру fData: PData

Код

fData := @GlobalData;

теперь заполняю структуру
Код

fData^.Command := 10; 

и отправлять её приходится непонятным пока образом.
Вот так всё нормально принимается клинтом:
Код

  fRSData.Write(fData^, SizeOf(fData)); //непонятно мне почему тут не так SizeOf(fData^) ???

если вместо этого написать 
Код

  fRSData.Write(fData^, SizeOf(fData^));

то отчет клиента такой:
Код

Соединение установлено
Пришло сообщение о проверке соединения //вот это Command := 10; которую я и посылал
Ответное сообщение отправлено
Command: 0 Команда не распознана //а вот это непонятная хрень что за пустые пакеты?
Command: 0 Команда не распознана 
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана
Command: 0 Команда не распознана


мне вот непонятно может быть эта строка всё таки верна?
Код

 fRSData.Write(fData^, SizeOf(fData));


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


НЭТ БИЛЭТ
**


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

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



fData указатель. SizeOf = 4(8)

Добавлено @ 17:40
Пкажи fRSDataTWinSocketStream.Write

Добавлено через 11 минут и 29 секунд
Код

  TWinSocketStream = class(TStream)
  private
    ...
  public
    ...
    function Write(const Buffer; Count: Longint): Longint; override;
    ...
  end;


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


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


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  13.2.2013,  17:28 Найти цитируемый пост)
 fRSData.Write(fData^, SizeOf(fData))

Передаешь только первые 4 байта.
Твоя структура принимается частями. 
Каждую часть, при приеме, ты считаеш новой структурой.  


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


Опытный
**


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

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



угу вот я тоже уже понял что это размер в памяти не структуры а указателя. Вот нифига не пойму

Структуры у меня одинаковы что в клиенте что на сервере
размер фиксированный у обоих как их не заполняй. вот она структура собственно
Код

const
  MAX_STRING = 30;
  MAX_CHAR = 10000;


type
  PData = ^TData;
  TData = packed record
    fCommand: Word;
    fNick: String[MAX_STRING];
    fMessages: array [0..MAX_CHAR - 1] of Char;
  end;


Смотрите пакет мне приходит рубленый значит потоки байт данных передаются последовательно один за другим это так?
а значит допустим событие OnRead в клиенте  может выполнится несколько раз...
А это значит мне нужно создать например MemoryStream, как глобальную переменную а при выполнении onRead писать всё в MemoryStream последовательно. Я в правильном направлении?

Как тогда мне определить начало и конец нового пакета? для того чтобы обработать принятый пакет очистить MemoryStream и начать принимать следующий пакет поток данных нового пакета?

Добавлено @ 18:10
Цитата(Чучмек @  13.2.2013,  18:05 Найти цитируемый пост)
Каждую часть, при приеме, ты считаеш новой структурой.   

С этим я согласен тупанул малёха, теперь делаю так 
Код

fRSData.Write(fData^, SizeOf(fData^));

но тут возникает вопрос чем отличается от того что выше вот это
fRSData.WriteBuffer(fData^, SizeOf(fData^));

Добавлено @ 18:15
Цитата(Чучмек @  13.2.2013,  17:33 Найти цитируемый пост)
Пкажи fRSDataTWinSocketStream.Write

А что показывать стандартный класс, стандартная функция))) (Delphi XE)

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


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  13.2.2013,  18:08 Найти цитируемый пост)
А что показывать стандартный класс, стандартная функция

Да. Я уже нашел.
Какой протокол?  TCP?


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


Опытный
**


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

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



может вобще стоит перевести ClientSocket в режим блокинга

Добавлено через 6 минут и 52 секунды
Цитата(Чучмек @  13.2.2013,  18:19 Найти цитируемый пост)
Какой протокол?  TCP?

ну наверно))) ведь TServerSocket/TClientSocket использует его вроде как

Добавлено через 11 минут и 54 секунды
И еще вот такой вопрос например если у меня клиент будет работать в режиме nonblocking я так понимаю это в основном потоке будут приниматься данные... Предположим что пакет принимался принимался а пользователь затем открыл какое нибудь окно ShowModal,
прием данных приостановится? если я размещу на форме компонент TidAntifreeze который собственно не относится к TClientSocket, будет ли пролжаться приём данных в таком случае?
PM MAIL   Вверх
Чучмек
Дата 13.2.2013, 18:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  13.2.2013,  18:08 Найти цитируемый пост)
Как тогда мне определить начало и конец нового пакета

По размеру передаваемой структуры.

Делаешь глобальными (или  свойство) буфер (размер твоей структуры) Buff
и размер данных записанных в буфер. L

В onRead,
 0. offset:=0;
 1. записываешь в буфер, с позиции L,принятые данные   c позиции offset размером count:=min(BuffSize-L,ReadLength-offset) ;
 2. L=L+count;
 3. If L=BuffSize -> Передаешь записанный буфер на обработку ; L:=0; offset:=offset+count; if offset< ReadLength then goto 1


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


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


Опытный
**


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

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



Жесть что попроще ни как ? может всё таки перевести клиента в режим блокинга и работать уже в потоках?
PM MAIL   Вверх
Чучмек
Дата 13.2.2013, 18:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



А какая разница?
Неблокирующий: пришел пакет -> событие
Блокирующий режим: пришел пакет ->поток вышел из Write.
Хоть так хоть эдак,  сколько пакетов, столько фрагментов.

Добавлено через 3 минуты и 58 секунд
Цитата(Antony41 @  13.2.2013,  18:20 Найти цитируемый пост)
а пользователь затем открыл какое нибудь окно ShowModal,
прием данных приостановится

Есть системный буфер. После выхода из Modal прочитаешь несколько пакетов сразу.

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


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


Опытный
**


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

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



Интерфейс не будет подвисать?

Добавлено через 1 минуту и 47 секунд
Цитата(Чучмек @  13.2.2013,  18:50 Найти цитируемый пост)
Есть системный буфер. После выхода из Modal прочитаешь несколько пакетов сразу

Тоесть они уже будут приняты останется только состыковать?

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


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  13.2.2013,  18:55 Найти цитируемый пост)
Тоесть они уже будут приняты останется только состыковать?

да


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


Опытный
**


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

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



ну вот смотрите вот как я сейчас всё переписал приблизительно... 
Подопните плиз в нужную сторону если что то не так.
Processing(@GlobalData); //там я уже работаю с пакетом

SIZE_OF_STRUCT это постоянный размер моей структуры
ReadedLen это кол-во прочитанных байт (глобальная Integer)
MS это TMemoryStream


Код

procedure TClient_Form.CSRead(Sender: TObject; Socket: TCustomWinSocket);
var I, RecCount, OutLen:Integer;
    ReadBuf: array [0..10000] of Byte;
begin
  RecCount := Socket.ReceiveLength;
  if RecCount = 0 then
    Exit;
  try
    I:=0;
    OutLen := SIZE_OF_STRUCT - ReadedLen;
    ZeroMemory(@ReadBuf, SizeOf(ReadBuf)); //чистим буфер
    RecCount := Socket.ReceiveLength;

    repeat
      OutLen := SIZE_OF_STRUCT - ReadedLen;
      if OutLen >= RecCount then
        I:=Socket.ReceiveBuf(ReadBuf, RecCount)
      else
        I:=Socket.ReceiveBuf(ReadBuf, OutLen);
      If I > 0 Then
        begin
          MS.Write(ReadBuf, I);
          ReadedLen := ReadedLen + I;
        end;
      RecCount := Socket.ReceiveLength;
    until (RecCount = 0) or (OutLen = 0);

    if OutLen = 0 then
      begin
        ReadedLen := 0; //сбрасываем общее кол-во принятых байт
        //обрабатываем MS
        MS.Seek(0, soFromBeginning); //Устанавливаем начальную позицию
        MS.Read(GlobalData, MS.Size); //Читаем данные в нашу структуру
        MS.Clear; //Очищаем поток
        //отправляем пакет на обработку
        Processing(@GlobalData);
      end;

  except
    on E: Exception do
    begin
      Memo1.Lines.Add(E.Message);
    end;
  end;
end;


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


НЭТ БИЛЭТ
**


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

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



Antony41, извини сразу не ответил, был занят.
Я с TClientSocket не работал. Работал с сокетами только через API
Но чтение я бы сделал так.
Код

procedure TForm1.CSRead(Sender: TObject; Socket: TCustomWinSocket);
type
 pr=^tr;
 tr=record
  count:integer;
  data:TData; //Твоя структура
 end;
var
 p:pr;
 rb,br:integer;
begin
if  socket.Data = nil then     //Если память для структуры tr не выделена - выделяем
  begin
  GetMem(p,sizeof(tr));        //По завершении работы(в OnDisconnect, наверное) необходимо вызвать FreeMem для socket.Data
  socket.Data:=p;
  p^.count:=0;
  end else p:=socket.Data;
repeat
 br:=sizeOf(TData)-p^.count;    //Сколько байт прочитать
 rb:=Socket.ReceiveBuf((PAnsiChar(@(p^.data))+p^.count)^,br); //только PAnsiChar !!! 
 p^.count:=p^.count+rb;         //Уже записанно байт в p^.data
 if p^.count=sizeOf(TData) then //Если p^.data полностью заполнена
  begin
  proc(p^.data);  //передаем p^.data в обработчик
  p^.count:=0;
  end;
until rb<br;    //Пока есть принятые данные - читаем
end;


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


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


Опытный
**


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

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



Цитата(Чучмек @  14.2.2013,  00:22 Найти цитируемый пост)
 if p^.count=sizeOf(TData) then //Если p^.data полностью заполнена
  begin
  proc(p^.data);  //передаем p^.data в обработчик
  p^.count:=0;
  end;
until rb<br;    //Пока есть принятые данные - читаем
end;

впринципе похоже, только вот как быть если данных принято больше чем сама структура.
тоесть если пришли данные еще и из слудующего пакета, надо вырезать нужное кол-во байт, а к оставшимся начинать дописывать следующий пакет. Или пакеты не мугут быть склеины?
PM MAIL   Вверх
Чучмек
Дата 14.2.2013, 12:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  14.2.2013,  09:55 Найти цитируемый пост)
впринципе похоже, только вот как быть если данных принято больше чем сама структура.

Вызовется несколько раз proc. Остаток (меньший, чем размер TData) ,будет хранится в выделенной памяти. Адрес  выделенной памяти хранится в связанном с сокетом указателе Data. На каждый сокет свой указатель, своя память. Это гарантирует, что при получении, данные разных соединений не будут смешиваться.
  


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


Опытный
**


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

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



Цитата(Чучмек @  14.2.2013,  12:42 Найти цитируемый пост)
Вызовется несколько раз proc. Остаток (меньший, чем размер TData) ,будет хранится в выделенной памяти. Адрес  выделенной памяти хранится в связанном с сокетом указателе Data. На каждый сокет свой указатель, своя память. Это гарантирует, что при получении, данные разных соединений не будут смешиваться.


тоесть если я правильно понимаю, например допустим мне осталось принять 10 байт до полного пакета данных, а пришло 
1000 байт (10 байт склееный со след. пакетом), и я сделаю вот так

обработчик OnRead...
Код

I:=Socket.ReceiveBuf(ReadBuf, 10);

затем отправлю пакет на обработку, то при следующем вызове обработчика в Socket.ReceiveLength будут оставшиеся байты (990 байт) ?

Добавлено через 2 минуты и 26 секунд
тоесть если пришло 1000 байт 
а я буду читать по 1 байту
Socket.ReceiveBuf(ReadBuf, 1);

то обработчик будет вызываться 1000 раз?
PM MAIL   Вверх
Чучмек
Дата 14.2.2013, 21:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Скажем так. 
Твоя структура 200 байт
Пришло 199 байт.
Событие OnRead.
Выделили память 4+200
В выделенную память записали 199 принятых байт и Integer=199
Пришло еще 399 байт
Событие OnRead.
В выделенную память дописали 1 байт
Вызвали proc, передали принятую структуру.
В выделенную память записали 200 байт
Вызвали proc, передали принятую структуру.
В выделенную память записали 198 байт и Integer=198
ждем еще.

Добавлено через 5 минут и 53 секунды
Че гадать, когда легко можно проверить.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls,winsock;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fr(Sender: TObject; Socket: TCustomWinSocket);
    procedure fc(Sender: TObject; Socket: TCustomWinSocket);
    procedure fd(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
 Host:PAnsiChar='ya.ru';
var
 vs:TClientSocket;

procedure TForm1.Button1Click(Sender: TObject);
var p:pansichar;
begin
vs:=TClientSocket.Create(form1);
p:=gethostbyname(Host)^.h_addr_list^;
vs.Host:= inttostr(PByte(p)^)+'.'+inttostr(PByte(p+1)^)+'.'+inttostr(PByte(p+2)^)+'.'+inttostr(PByte(p+3)^);
vs.Port:=80;
vs.Open;
vs.OnRead:=fr;
vs.OnConnect:=fc;
vs.OnDisconnect:=fd;
end;

procedure TForm1.fc(Sender: TObject; Socket: TCustomWinSocket);
var
 s:ansistring;
begin
s:='GET / HTTP/1.1'#13#10+
   'Connection: close'#13#10+
   'Host: '+Host+#13#10+
   #13#10;
Socket.SendBuf(s[1],length(s));
end;


type
 TDATA=array[0..0]of ansichar; //..0,..1,..10,..100
var
 s:string;

procedure proc(const d:TDATA);
begin
 s:=s+d;
 Form1.Memo1.Text:=s;
end;

procedure TForm1.fd(Sender: TObject; Socket: TCustomWinSocket);
begin
if Socket.Data<>nil then FreeMem(Socket.Data);
end;

procedure TForm1.fr(Sender: TObject; Socket: TCustomWinSocket);
type
 pr=^tr;
 tr=record
  count:integer;
  data:TData;
 end;
var
 p:pr;
 rb,br:integer;
begin
if  socket.Data = nil then     //Если память для структуры tr не выделенна - выделяем
  begin
  GetMem(p,sizeof(tr));        //По завершенни работы необходимо вызвать FreeMem для socket.Data
  socket.Data:=p;
  p^.count:=0;
  end else p:=socket.Data;
repeat
br:=sizeOf(TData)-p^.count;    //Сколько байт прочитать
rb:=Socket.ReceiveBuf((PAnsiChar(@(p^.data))+p^.count)^,br); //только Ansi !!!
p^.count:=p^.count+rb;         //Уже записанно байт в p^.data
if p^.count=sizeOf(TData) then //Если p^.data полностью заполненна
 begin
 proc(p^.data);  //передаем p^.data в обработчик
 p^.count:=0;
 end;
until rb<br;    //Пока есть принятые данные читаем
end;

end.



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


Опытный
**


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

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



Спасибо Вам огромное! smile  Вы все очень помогли и дали мне понять в чем заключалась моя ошибка, а главное разъяснили и разжевали всё надо! Пока тему не буду закрывать, возможно возникнет в ближайшие дни нерешимая без вашей помощи ситуация.
PM MAIL   Вверх
Чучмек
Дата 14.2.2013, 21:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Кроме того. Если ты не используешь устойчивое соединение, можешь прочитать сразу все данные в OnDisconnect.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls,winsock;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fc(Sender: TObject; Socket: TCustomWinSocket);
    procedure fd(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
 Host:PAnsiChar='ya.ru';
var
 vs:TClientSocket;

procedure TForm1.Button1Click(Sender: TObject);
var p:pansichar;
begin
vs:=TClientSocket.Create(form1);
p:=gethostbyname(Host)^.h_addr_list^;
vs.Host:= inttostr(PByte(p)^)+'.'+inttostr(PByte(p+1)^)+'.'+inttostr(PByte(p+2)^)+'.'+inttostr(PByte(p+3)^);
vs.Port:=80;
vs.Open;
vs.OnConnect:=fc;
vs.OnDisconnect:=fd;
end;

procedure TForm1.fc(Sender: TObject; Socket: TCustomWinSocket);
var
 s:ansistring;
begin
s:='GET / HTTP/1.1'#13#10+
   'Connection: close'#13#10+
   'Host: '+Host+#13#10+
   #13#10;
Socket.SendBuf(s[1],length(s));
end;


type
 TDATA=array[0..2]of ansichar;
var
 s:string;

procedure proc(const d:TDATA);
begin
 s:=s+d;
 Form1.Memo1.Text:=s;
end;

procedure TForm1.fd(Sender: TObject; Socket: TCustomWinSocket);
var d:TDATA;
begin
while sizeof(TDATA)=Socket.ReceiveBuf(d,sizeof(TDATA)) do proc(d);
end;


end.



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


Опытный
**


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

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



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

while (not Terminated) and (ClientSocket.Connected) do


И эта строка грузит мой цп на 25% при 1 подключенном клиенте, почему так как быть?
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 21:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



потому что процессор четырёхядерный


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


Опытный
**


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

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



ну 4 и что, его хватит только на 4 потока... и потом цп будет загружен на 100%. как быть то чтоб не такой прожерливый был? Может быстро  обрабатывать данные и выходить из потока, отключать клиента, а клиент будет слать серваку каждые например 30-40 сек команду что он на связи... серв же посчитает его отключенным только после 5 попыток поставит ему статус офлайн. А вот если выполняешь какой нибудь запрос от клиента то отсылать сразу его и если нужно соединиться например для передачи какого нибудь файла, стрима, затем снова отрубить.
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 21:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Ну самое простое, но неправильное, вставить в цикл sleep(100)


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


Опытный
**


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

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



ну а правильное ?
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Ну вот посмотри.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fl(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
 tmt=class (tthread)
  fs:TCustomWinSocket;
  procedure execute;override;
  constructor create(s:TCustomWinSocket);
 end;

var ss:TServerSocket;


procedure TForm1.fl(Sender: TObject; Socket: TCustomWinSocket);
begin
tmt.create(Socket);
end;

{ tmt }

constructor tmt.create(s: TCustomWinSocket);
begin
inherited create(true);
fs:=s;
Resume;
end;

procedure tmt.execute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=fs.ReceiveBuf(buf,sizeof(buf));
 s:=s+copy(buf,0,l);
until (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
sh:='HTTP/1.1 200 Ok'#13#10+
    'Connection: close'#13#10+
    #13#10;
fs.SendBuf(sh[1],length(sh));
fs.SendBuf(s[1],length(s));  
fs.Disconnect(fs.SocketHandle);
free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   ss:=TServerSocket.Create(form1);
   ss.ServerType :=stThreadBlocking;
   ss.Port:=3600;
   ss.OnAccept:=fl;
   ss.Open;
end;

end.

Скомпилируй запусти, и введи в браузере http://localhost:3600/


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


НЭТ БИЛЭТ
**


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

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



Antony41, я заврался. Так тоже не годится.
Оно конечно работает, но через костыль.

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


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


НЭТ БИЛЭТ
**


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

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



Antony41, советую разобраться с сокетами (API)
Вот как выглядит сервер на WinSock API. (без проверки ошибок)
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,winsock;

type
  TForm1 = class(TForm) 
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Mtsobj;

{$R *.dfm}

type
 tmt=class (tthread)
  fs:integer;
  procedure execute;override;
  constructor create(s:integer);
 end;

var
 thread_count:integer=0;  //Счетчик потоков

{ tmt }

constructor tmt.create(s: integer);
begin
inherited create(true);
fs:=s;
Resume;
end;

procedure tmt.execute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=recv(fs,buf,sizeof(buf),0);    //Читаем данные, пока не дойдем до пустой строки  (конец http GET запроса)
 s:=s+copy(buf,0,l);
until (l=0) or (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
if l>0 then   //Пока не разорвется соединение l>0
 begin
 sh:='HTTP/1.1 200 Ok'#13#10+
     'Content-type: text/plain;charset=windows-1251'#13#10+
     'Connection: close'#13#10+
     #13#10;
 s:='Прувет я Сэрвэр'#13#10'Заголовки запроса:'#13#10+s;
 send(fs,sh[1],length(sh),0);      //Отправляем заголовки
 send(fs,s[1],length(s),0);        //Отправляем страницу
 end;
closesocket(fs);                  //закрываем сокет
InterlockedDecrement(thread_count);  //счетчик потоков -1
free;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
 wsd:TWSAData;
 s,cs:integer;
 sa:TSockAddr;
 sl:TFDSet;
 timeout: TIMEVAL;
begin
WSAStartup(1,wsd);                         //Инициализируем библиотеку сокетов
s:=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); //Создаем сокет для работы с TCP/IP
with sa do                                 //Заполняем структуру адресса сокета
 begin
 sin_family:=PF_INET;
 sin_port:=htons(3600);
 sin_addr.S_addr:=INADDR_ANY;
 FillChar(sin_zero,sizeof(sin_zero),0);
 end;
bind(s,sa,sizeof(sa));                     //Привязываем сокет к адресу
listen(s,0);                               //Ставим сокет на ожидание подключения
while true do
 begin
 sl.fd_count:=1;                           //Добавляем сокет в список проверки готовности
 sl.fd_array[0]:=s;
 timeout.tv_sec:=0;
 timeout.tv_usec:=100000;                  //Время ожидания 100 мс
 if 0=select(0,@sl,nil,nil,@timeout) then  //Ожидаем подключение
  begin   //Если таймаут - проверяем  завершение работы сервера
  Application.ProcessMessages;
  if Application.Terminated then
   begin
   Application.Minimize;
   Application.ProcessMessages;
   while thread_count>0 do sleep(100); //ожидаем завершение всех потоков
   closesocket(s);
   WSACleanup;
   exit;
   end;
  end else
  begin    //Если подключение -
  cs:=accept(s,nil,nil);   //Получаем сокет обслуживающий соединение
  InterlockedIncrement(thread_count);
  TMT.Create(cs); //создаем поток обработки соединения
  end;
end;


end;


end.

p.s. T(Clent)ServerSocket всего лишь ООП обертка для WinSock API.

Вот тоже на TServerSocket
Код

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fg(Sender: TObject; ClientSocket: TServerClientWinSocket;var SocketThread: TServerClientThread);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
 tmt=class(TServerClientThread)
  procedure ClientExecute;override;
 end;

var ss:TServerSocket;

procedure TForm1.fg(Sender: TObject; ClientSocket: TServerClientWinSocket;
  var SocketThread: TServerClientThread);
begin
 SocketThread:=tmt.Create(false,ClientSocket);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
ss:=TServerSocket.Create(form1);
ss.Port:=3600;
ss.ServerType:=stThreadBlocking;
ss.OnGetThread:=fg;
ss.Open;
end;


{ tmt }

procedure tmt.ClientExecute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=ClientSocket.ReceiveBuf(buf,sizeof(buf));
 s:=s+copy(buf,0,l);
until (l=0) or (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
if l>0 then
 begin
 sh:='HTTP/1.1 200 Ok'#13#10+
     'Content-type: text/plain;charset=windows-1251'#13#10+
     'Connection: close'#13#10+
     #13#10;
 s:='Прувет я Сэрвэр'#13#10'Заголовки запроса:'#13#10+s;
 ClientSocket.SendBuf(sh[1],length(sh));
 ClientSocket.SendBuf(s[1],length(s));
 end;
ClientSocket.Close;
end;

p.p.s При закрытии, оба варианта будут ждать, пока не разорвется соединение. Только тогда   recv/ClientSocket.ReceiveBuf вернет управление.



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


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


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  17.2.2013,  20:47 Найти цитируемый пост)
1:
    
while (not Terminated) and (ClientSocket.Connected) do


И эта строка грузит мой цп на 25% при 1 подключенном клиенте, почему так как быть? 

Ответ один - внутри цикла нет, или не правильно используется функция ожидания.
Например. TWinSocketStream.WaitForData возвращает управление если в сокете остались непрочитанные данные.
TWinSocketStream - редкое г. Лучше не используй его. На мой взгляд - бесполезный компонент. В TWinSocketStream используются только read/write/WaitForData
Если при остановке сервера необходимо сразу остановить все потоки (ClientSocket.Connected возвращает false)-
вместо TWinSocketStream.WaitForData используй можно использовать
Код

uses ...,WinSock;
...
function WaitForData(socket:TCustomWinSocket;Timeout: Longint):boolean;
var
  FDSet: TFDSet;   
  TimeVal: TTimeVal;
begin
  TimeVal.tv_sec := Timeout div 1000;
  TimeVal.tv_usec := (Timeout mod 1000) * 1000;
  FDSet.fd_count:=1;
  FDSet.fd_array[0]:=socket.SocketHandle;
  Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;
end;
 
WaitForData(ClientSocket,timeout) -> true, если есть данные для чтения и false, если timeout

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


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


Опытный
**


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

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



Цитата(Чучмек @  18.2.2013,  14:38 Найти цитируемый пост)
TWinSocketStream.WaitForData

Оно вобще иногда не всегда корректно работает

Спасибо Тебе за ясность! щас буду испытывать

Добавлено через 4 минуты и 6 секунд
PS по сути то я и не использую WaitForData, а использую ожидание через GetTickCount - Tick >= TIME_OUT
PM MAIL   Вверх
Чучмек
Дата 18.2.2013, 16:36 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  18.2.2013,  16:28 Найти цитируемый пост)
Оно вобще иногда не всегда корректно работает

Да нет, должно работать.
Возвращает true, если есть данные для чтения и false, если таймаут или ошибка.
Однако если прочитано меньше чем пришло - всегда true и без ожидания.
Один байт не прочитал - и будет твой поток грузится на 100%.



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


Опытный
**


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

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



кстати мне sleep(10) очень помогло впринципе засыпание на 10 мс незаметно, это ни как не влияет на приём данных... а вот в цикле с GetticCount самое оно. Загрузка ЦП при 20 подключ. клиентов составила 0-1% при опросе клиентов каждые 3 сек. Провёл тестирование подключил 20 клиентов, и они стабильно обменивались данными с сервером 7-8 часов при закрытии серва все 20 клиентов были на связи. Пока ошибок не выявил.

Это сообщение отредактировал(а) Antony41 - 18.2.2013, 16:39
PM MAIL   Вверх
Antony41
Дата 9.4.2013, 22:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Короче тут такая ситуация вышла... Я запарился с этими vcl (TClientSocket) и решил с чистого листа на Socket API впринципе пока все прекрасно...
Вопросик образовался:
Я Выполняю прием данных в основном потоке тоесть
Код

I := recv(vSocket, Buff, iStillToReceive, 0);
 
пока выполняется эта функция, блокируется главный поток на время приёма данных. (ну я имею ввиду зависание интерфейса).
никакие тут ProcessMessages не помогают,
Что будет если я работу и прием данных напишу в отдельном потоке? ни каких конфликтов не возникнет при переключении потоков системой?
или что посоветуете.
PM MAIL   Вверх
Чучмек
Дата 10.4.2013, 09:10 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  9.4.2013,  22:25 Найти цитируемый пост)
пока выполняется эта функция, блокируется главный поток на время приёма данных. (ну я имею ввиду зависание интерфейса).
никакие тут ProcessMessages не помогают,

Antony41, есть неблокирующий режим, есть select.


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


Опытный
**


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

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



ок спс... щас поюзаем

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


Опытный
**


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

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



Ребят помогите чёто не пойму как надо. Вот процедура приема данных у клиента
проблема в том что иногда структура не верно заполняется, а иногда верно.
Если проверять когда данные от сервера ушли уже точно, то пакет приходит нормально, а вот если не ушли или ушли не полностью то иногда пакет поступает в обработку (processing()) и обрабатывается не верно.

Короче серв шлет тестовый пакет каждые 5 сек, если я буду проверять пришел пакет или нет каждые 5 сек, то ошибки обработки (тут не понятно данные вроде по размеру заполнятся полностью, может там какие нибудь спец симполы в начале или конце пакета добавляются?)  
А если я буду делать проверку ReceiveNBytes каждые напр. 6-7 сек то всё ОК. smile 

Код

function TCSocketThread.ReceiveNBytes: Integer;
var
  SelectResult, I, iStillToReceive, iStillToReceive2: Integer;
  Timeout: timeval;
  fds: TFDSet;
  TempData: TData;
  Buff: array [0..10000] of Byte;
  SIZE_OF_STRUCT: Integer;
  MS: TMemoryStream;
begin
  SelectResult := 0;
  I := 0;
  FD_ZERO(fds);
  FD_SET(vSocket, fds);
  Timeout.tv_sec := 0;
  Timeout.tv_usec := 500000; //Устанавливаем таймаут – 0,5сек.
  SIZE_OF_STRUCT := SizeOf(TempData);
  iStillToReceive := SIZE_OF_STRUCT; //от сюда будут отниматься принятые байты
  iStillToReceive2 := 0; //сюда будут суммироваться кол-во принятых байт
  repeat
      //Ждем прихода данных
      SelectResult := select(0, @fds, nil, nil, @Timeout);
      //Выход по таймауту
      if SelectResult = SOCKET_ERROR then
        begin
          Result := -1;
          Break;
        end
      else
        if SelectResult = 0 then //TimeOut
          begin
            Result := -1;
            Break;
          end
        else
          if SelectResult < 0 then //Произошла какая-то ошибка при исполнении select()
            begin
              //Result := WSAGetLastError;
              Result := -1;
              Break;
            end;
            //Прием нескольких байт
            I := recv(vSocket, Buff, iStillToReceive, 0);

            if I = 0 then //Произошла ошибка при вызове функции recv() или сокет был закрыт
            begin
              //Result := WSAGetLastError
              Result := -1;
              Break;
            end
            else
              if I = SOCKET_ERROR then
                begin
                  Result := -1;
                  Break;
                end
              else
                begin
                  //Изменяем величину счетчика и буфер
                  iStillToReceive := iStillToReceive - I;
                  iStillToReceive2 := iStillToReceive2 + I;
                end;
  until iStillToReceive = 0;

  if Result = -1 then
    Exit
  else
    begin
      MS := TMemoryStream.Create;
      MS.Write(Buff, SIZE_OF_STRUCT);
      MS.Seek(0, soFromBeginning); //Устанавливаем начальную позицию
      MS.Read(TempData, MS.Size); //Читаем данные в нашу структуру
      if MS.Size = iStillToReceive2 then
        Processing(@TempData);
      MS.Free;
      Result := 0;
    end;
end;

PM MAIL   Вверх
Чучмек
Дата 11.4.2013, 00:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  10.4.2013,  17:59 Найти цитируемый пост)
неблокирующий сокет создает отдельный поток на каждое подключение, 

Antony41, не выдумывай.
Код

...
const WinSockMsg=WM_USER+100;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure WinSockMSGProc(var msg:TMessage); message WinSockMsg;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
var
  wd:WSAData;

const
  port:word=80;
  host='forum.vingrad.ru';
  pg='forum/topic-362367/view-all.html';

procedure TForm1.Button1Click(Sender: TObject);
var
  s:TSocket;
  sa:TSockAddrIn;
  ip:Cardinal;
  pi:PHostEnt;
  str:ansistring;
begin
  s:=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
  pi:=gethostbyname(host);
  ip:= pbyte(pi.h_addr_list^+3)^ or
      (pbyte(pi.h_addr_list^+2)^ shl 8)  or
      (pbyte(pi.h_addr_list^+1)^ shl 16) or
      (pbyte(pi.h_addr_list^)^   shl 24);
  FillChar(sa,sizeof(sa),0);
  sa.sin_family:=PF_INET;
  sa.sin_port:=htons(port);
  sa.sin_addr.S_addr:=htonl(ip);
  connect(s,TSockAddr(sa),sizeof(sa));
  str:='GET /'+pg+' HTTP/1.1'#13#10+
       'Connection: close'#13#10+
       'Host: '+host+#13#10+
       #13#10;
  send(s,str[1],length(str),0);
  WSAAsyncSelect(s,Form1.Handle,Cardinal(WinSockMsg),FD_READ);
end;




procedure TForm1.FormCreate(Sender: TObject);
begin
  WSAStartup(1,wd);
end;

procedure TForm1.WinSockMSGProc(var msg: TMessage);
var
  s:TSocket;
  hEvent,
  error:word;
  rb:integer;
  buff:array[word] of ansichar;
begin
  s:=msg.WParam;
  hEvent:=msg.LParam and $FFFF ;
  error:=msg.LParam shr 16;
  if hEvent=FD_READ then
  begin
    rb:=recv(s,buff,sizeof(buff),0);
    Memo1.Text:=Memo1.Text+UTF8ToString(copy(buff,1,rb));
  end;
end;

end.


Добавлено через 13 минут и 52 секунды
Нажми несколько раз подряд на Button1.
Antony41,неблокирующий режим для того и существует, чтобы обойтись без дополнительных потоков.
Есть как минимум один способ на блокирующем режиме - проверять состояние сокета через  select (посмотри выше, я давал пример где проверялось подключение),
и есть несколько способов на неблокирующих.


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


Опытный
**


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

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



Просто терпения нет хочется быстрее выучить и написать уже рабочую нормальную прогу...))) Пошел снова читать)))
PM MAIL   Вверх
Antony41
Дата 11.4.2013, 20:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



завтра если будет время перепишу всё под неблокирующий. посмотрим что из этого выйдет.
тут я так понимаю select  timeout для recv задавать не нужно, так как прием данных будет возникать по событию FD_READ и вечного ожидания данных не будет? 
PM MAIL   Вверх
Чучмек
Дата 11.4.2013, 21:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  11.4.2013,  20:33 Найти цитируемый пост)
тут я так понимаю select  timeout для recv задавать не нужно, так как прием данных будет возникать по событию FD_READ и вечного ожидания данных не будет? 

Да.
WSAAsyncSelect "привязывает" оконное сообщение к сокету(или, точнее, события сокета к оконному сообщению) и переводит сокет в асинхронный(неблокирующий) режим. Можно привязать одно или несколько событий FD_xxx. 




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


Опытный
**


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

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



чот то не выходит,  
Чучмек, 
Цитата

Код

procedure TForm1.WinSockMSGProc(var msg: TMessage);
var
  s:TSocket;
  hEvent,
  error:word;
  rb:integer;
  buff:array[word] of ansichar;
begin
  s:=msg.WParam;
  hEvent:=msg.LParam and $FFFF ;
  error:=msg.LParam shr 16;
  if hEvent=FD_READ then
  begin
    rb:=recv(s,buff,sizeof(buff),0);
    Memo1.Text:=Memo1.Text+UTF8ToString(copy(buff,1,rb));
  end;
end;


Почему так то разве если пакет придут по частям то ведь эта функция сработает не корректно, ведь у нас   buff:array[word] of ansichar; определяется каждый раз заново

короче я не пойму я уже всё сделал в одном основном потоке, сделал привязал событие FD_READ к сообщению
всё ок при отправке данных, событие возникает нормально при чём сообщение которое отправляется обрабатывается несколько раз, и приходит нужное число байт, но буфер пустой(((( как правильно принимать рубленные пакеты в асинхронном режиме а именно буфер каких нибудь байт.... блин честное слово запарился, проект пользуется спросом а я не могу тут передачу данных наладить(((Что ж всё так печально то, уже и на стандартные компоненты забил засунулся в API а толку... smile

Добавлено через 4 минуты и 4 секунды
код не могу предоставить на работу нужно вечером помучаюсь еще на так я и не понял... блин Хелпинг...
PM MAIL   Вверх
Чучмек
Дата 15.4.2013, 10:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  15.4.2013,  10:22 Найти цитируемый пост)
Почему так то разве если пакет придут по частям то ведь эта функция сработает не корректно, ведь у нас   buff:array[word] of ansichar; определяется каждый раз заново

Ты все правильно понимаешь. Пример на неблокирующий режим и только.
Конечно, буфер для сборки пакета должен быть глобальным. Как глобально Memo1.  
Кроме того, если возможно несколько соединений одновременно - под каждый сокет должен быть свой буфер.



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


Опытный
**


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

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



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


Опытный
**


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

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



Код

var
  Buff: array [0..500] of char;
  iStillToReceive: integer = 0; 

procedure TMainForm.WinSockMSGProc(var msg: TMessage); //при FD_READ
var
  I: Integer;
  S: TSocket;
begin
  s:=msg.WParam;

          I := recv(S, Buff, 100, 0);
          iStillToReceive := iStillToReceive + I;


  if iStillToReceive = 100 then //Вот тут чё то не так
    begin
          Log.Lines.Add(Buff);
    end;

end;


если поставить например вместо if iStillToReceive = 100 then, iStillToReceive = SizeOf(Buff) или 200 то данных в структуре нет, мало того еще и ошибка, а вот если сто +- то читается нормально и там те данные которые я передал
PM MAIL   Вверх
Antony41
Дата 15.4.2013, 17:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



тоесть не не так, получается если я ставлю размер принимаемого буфера Buff: array [0..500] of Char; то всё пока нормально приходит 
а если поставить уже Buff: array [0..1000] of Char, то уже пустой буфер. как так

Код

procedure TMainForm.WinSockMSGProc(var msg: TMessage); //при FD_READ
var
  I: Integer;
  S: TSocket;
begin
  s:=msg.WParam;
          I := recv(S, Buff, SizeOf(Buff), 0);
          iStillToReceive := iStillToReceive + I;
  if iStillToReceive = SizeOf(Buff) then 
    begin
          Log.Lines.Add(Buff);
    end;
end;

PM MAIL   Вверх
Чучмек
Дата 15.4.2013, 17:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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




Цитата(Antony41 @  15.4.2013,  17:06 Найти цитируемый пост)
 I := recv(S, Buff, SizeOf(Buff), 0);

Ты ведь пакет собираешь.
Код

 I := recv(S, Buff+iStillToReceive , SizeOf(Buff)-iStillToReceive , 0)

Цитата(Antony41 @  15.4.2013,  16:44 Найти цитируемый пост)
Вот тут чё то не так

Пришел  первый кусок (меньше чем указываешь в recv) -сумма меньше. Пришел второй кусок -сумма уже больше.



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


Опытный
**


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

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



ну в общем я проверял браузером http://127.0.0.1:PORT
щас искать буду
PM MAIL   Вверх
Чучмек
Дата 19.4.2013, 16:48 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


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

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



Цитата(Antony41 @  19.4.2013,  15:51 Найти цитируемый пост)
ну в общем я проверял браузером http://127.0.0.1:PORT

Подожди, запустил сервер, открыл браузер, ввел адрес сервера и нажал enter?
А сервер первым делом ждет длину строки? 
Браузер передает 
Код

GET / HTTP/1.1...

G.E.T."пробел"
0x47,0x45,0x54,0x20
Или 0x20544547
Или 542393671
Пол га для ansistring
Четыре запроса положат сервер.


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


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


Опытный
**


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

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



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


Опытный
**


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

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



не могу понять где ошибка 3 пакета уходит потом вылетает ошибка по адресу...
вот так клиент отправляет данные
Код

    Data.fCommand := 50001;
    SetLength(SendBuf, SizeOf(Integer) + SizeOf(Data));
    PInteger(@SendBuf[0])^ := SizeOf(Data);
    Move(Data, SendBuf[SizeOf(Integer)], SizeOf(Data));
    BytesSent := 0;


    // Повторяем попытку отправить до тех пор, пока всё содержимое
    // буфера не будет отправлено серверу.
    while BytesSent < Length(SendBuf) do
    begin
      SendRes := send(vSocket, SendBuf[BytesSent], Length(SendBuf) - BytesSent, 0);
      if SendRes > 0 then
        Inc(BytesSent, SendRes)
      else
        if WSAGetLastError = WSAEWOULDBLOCK then
          Sleep(10)
        else
        begin
          MessageDlg('Ошибка при отправке данных серверу', mtError, [mbOK], 0);
          //OnDisconnect;
          Exit;
        end;
    end;

PM MAIL   Вверх
Antony41
Дата 21.4.2013, 18:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



разобрался но так и не понял
ошибка возникала из за того что я пытаюсь показать сколько сейчас соединений в TList при событии FD_CLOSE, вот так вроде гуд...
но стоит поставить после RemoveConnection и ошибка по адресу...
Код

FD_CLOSE:
      begin
         if WSAGetLastError = WSAEWOULDBLOCK then
           Exit;
        // Клиент вызвал функцию shutdown. Закрываем соединение.
        Logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение');
        Dec(OnLineCount, 1);
        MainForm.StatusBar2.Panels.Items[0].Text := 'Онлайн сейчас: '+IntToStr(OnLineCount);
        shutdown(Connection.ClientSocket, SD_BOTH);
        RemoveConnection;
        Beep;
      end;

PM MAIL   Вверх
Antony41
Дата 21.4.2013, 20:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



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


Опытный
**


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

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



Ладно ребят спасибо всем огромное при огромное... Вроде пока всё корректно работает.
Еще подскажите пожалуйста. Если у клиента прописан прокси, от куда будут браться настройки с IE? и как назначить connect + PROXY вручную
PM MAIL   Вверх
Antony41
  Дата 28.4.2013, 10:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



ответит кто или нет? smile 
PM MAIL   Вверх
MetalFan
Дата 29.4.2013, 17:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 14
Всего: 128



работу с проксей, афаик, если юзаешь сокеты, придется вручную реализовывать...
возможно ошибаюсь.


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

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

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

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

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

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


 




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


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

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