Модераторы: 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   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

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

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

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

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

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


 




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


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

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