Конференция "Сети" » TSocketClient и TSocketServer работа с потоками
 
  • Vladimir (01.03.09 10:27) [0]
    Доброго времени суток всем! Заранее извиняюсь если данная тема уже поднималась, но в поиске ответа на свой вопрос я так и не нашел. Требуется написать клиент-серверное приложение. В данный момент написал пробные клиент и сервер на SocketClient и SocketServer соответсвенно работающие в синхронном режиме. Подскажите пожалуйста каким образом  организовать их работу в асинхронном режиме, чтобы сервер одновременно обслуживал несколько клиентов! И ещё есть такая проблема при посылке текста через SendText на сервер приходят иероглифы, причем  это только на Windows Vista, на Windows XP все работало нормально. Использую Delphi 2009. Из-за чего это может быть? Заранее спасибо.
  • Сергей М. © (01.03.09 12:30) [1]
    SocketServer в любом режиме способен обслуживать более одного клиента.
    В режиме stNonBlocking обслуживание всех клиентов последовательное в одном потоке, в режиме stThreadBlocking каждое активное кл.соединение уже обслуживается в отдельном потоке.
  • Сергей М. © (01.03.09 12:42) [2]
    Режим работы ClientSocket м.б. любым и никаким образом не каксается режима работы ServerSocket
  • Vladimir (01.03.09 12:48) [3]
    Спасибо конечно)) Но я и спрашиваю каким образом организовать работу сервера в режиме stThreadBlocking. Ведь для каждого клиента надо создавать отдельный поток, а когда клиент отключиться его надо уничтожать и т.д.
  • Сергей М. © (01.03.09 12:53) [4]

    > Но я и спрашиваю каким образом организовать работу сервера
    > в режиме stThreadBlocking


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


    > для каждого клиента надо создавать отдельный поток, а когда
    > клиент отключиться его надо уничтожать


    TServerSocket все это делает сам
  • Vladimir (01.03.09 13:07) [5]
    Сергей, видимо мы с вами друг друга немного не понимаем. Вы не могли бы выложить код самого простого многопоточного сервера и сделать пометки в тех местах где поток создаеться, где можно проделывать какие-либо действия с клиентом(принимать от него данные, посылать ему данные) и где поток уничтожаеться. Может быть тогда у меня получиться разобраться. И из-за чего может быть проблема с SendText?
  • Сергей М. © (01.03.09 13:14) [6]
    Вот не ахти какой, но достаточно показательный пример:

    http://www.delphisources.ru/pages/faq/base/tserversocket.html
  • Slym © (02.03.09 05:32) [7]
    зачем далеко ходить? на этом форуме недалече второй страницы
    http://pda.delphimaster.net/?id=1227604925&n=4
  • Empleado © (02.03.09 12:32) [8]

    > И из-за чего может быть проблема с SendText?

    Из-за Уникод?
  • Vladimir (03.03.09 12:09) [9]

    > Из-за Уникод?

    А как можно с этим разобраться? чтоб текст нормальный приходил?
  • Сергей М. © (03.03.09 12:27) [10]

    > Vladimir   (03.03.09 12:09) [9]


    Что ты послал, то и получишь.
    Данные при их транспортировке под управлением TCP доставляются в гарантированно неизменном виде.
  • Empleado © (03.03.09 12:29) [11]

    > А как можно с этим разобраться? чтоб текст нормальный приходил?

    Например, отправлять Уникод и принимать Уникод.

    Прочитать в хелпе об использовании Unicode в D2009. Там подробно написано, с примерами, как работать со строками.
    Не использовать SendText, a SendBuf, например, либо TWinSocketStream.Read/Write.
    При чтении из сокета, преобразовывать полученное в Unicode. Например с помощью класса TEncoding.Unicode.GetString [или же StringOf в случае telnet].
  • Vladimir (03.03.09 13:46) [12]
    спасибо, попробую.
  • Vladimir (03.03.09 20:31) [13]
    Спасибо, все получилось. Только появился ещё один вопрос. Когда клиент присоединяеться к серверу для него создаеться отдельный поток, а из потока вызываеться некая процедура которая работает с файлом(записывает в него данные) и закрывает файл... Дело в том что если сервер работает не в многопоточном режиме, то проблемы одновременного обращения к файлу не возникает, а как быть при моем раскладе с потоками? Как "объяснить" компьютеру  подождать пока один поток поработает с файлом и только потом самому приступать к работе с ним?
  • Сергей М. © (03.03.09 20:46) [14]

    > процедура которая работает с файлом(записывает в него данные)
    > и закрывает файл


    Уж не файл ли протокола ?
  • medved_68 © (04.03.09 08:56) [15]

    > Как "объяснить" компьютеру  подождать пока один поток поработает
    > с файлом и только потом самому приступать к работе с ним?
    >

    Для этого придуманы объекты синхронизации и доступ к файлу в режиме ShareDenyNone
  • Vladimir (04.03.09 09:06) [16]

    > Уж не файл ли протокола ?

    нет, файл базы данных


    > Для этого придуманы объекты синхронизации и доступ к файлу
    > в режиме ShareDenyNone

    я знаю... но хотел узнать нельзя ли каким то образом блокировать секцию кода, до тех пор пока она не будет выполнена... сделать что то вроде:

    Lock;
    выполняемый операции
    UnLock;

    чтобы если какой то поток уже начал обрабатывать этот участок кода, то другому потоку пришлось ждать пока первый закончит с ним работать.
  • Сергей М. © (04.03.09 09:45) [17]

    > нет, файл базы данных


    И какая СУБД при этом тобой используется ?
    Или ты валишь данные прямо в файл, называя при этом "базой данных" именно этот файл ?
  • Vladimir (04.03.09 10:14) [18]

    > Или ты валишь данные прямо в файл, называя при этом "базой
    > данных" именно этот файл ?


    В чем то ты прав. СУБД я не использую, но и не валю все прямо в файл(есть некая структура)... но, думаю что сейчас объяснять все в подробностях ни к чему.
  • Сергей М. © (04.03.09 10:22) [19]
    Ясно.
    Считай что валишь прямо в файл.
    Ну тогда его надо бы защитить критической секцией (см. TCriticalSection).
  • Vladimir (04.03.09 10:43) [20]

    > Ясно.
    > Считай что валишь прямо в файл.
    > Ну тогда его надо бы защитить критической секцией (см. TCriticalSection).
    >


    Хорошо, спасибо, это я сделаю. Но мне помнилось что можно как то секцию кода блокировать или может быть называеться это как то по другому. Просто давно как то писал простенький многопоточный чат и там тоже  необходимо было предотвратить одновременное обращение как к файлу, так и к общим переменным. Исходников того чата не осталось и как именно прописывал это не помню.
  • Сергей М. © (04.03.09 11:05) [21]

    > помнилось что можно как то секцию кода блокировать или может
    > быть называеться это как то по другому


    Вот как раз "критической секцией" это и называется.
  • Vladimir (04.03.09 11:13) [22]
    спасибо Сергей. Буду вспоминать и разбираться.
  • Vladimir (08.03.09 10:26) [23]
    С критическими секциями разобрался. Все ок. Но возникла новая проблема. ClientSocket должен автоматически входить в событие Read когда к нему приходят данные от сервера, но он почему то этого не делает. Пришлось сделать следующее. Создать глобальную переменную RegSocket: TWinSocketStream и на событие Connect клиента(RegClient) поставил следующий код RegSocket := TWinSocketStream.Create(Socket, 60000);

    А в том месте, где необходимо получить ответ от сервера поставил следующий код

    while not RegSocket.WaitForData(100) do
    Application.ProcessMessages;
    RegClient.OnRead(Sender, RegClient.Socket);

    То есть вручную проверяю пришли ли данные от сервера и если пришли, то вызываю процедуру Read клиента. Хотя по идеи клиент сам должен понять что сервер послал ему данные и сам войти в событие Read. В чем может быть дело?
  • Сергей М. © (09.03.09 15:00) [24]

    > ClientSocket должен автоматически входить в событие Read
    > когда к нему приходят данные


    Нет, не должен.
    Событие OnRead возбуждается только при условии ClientType = ctNonBlocking, а у тебя ctBlocking
  • Vladimir (12.03.09 20:59) [25]
    И что же делать? Оставить тот вариант который я уже реализовал? Вроде он рабочий...
  • Сергей М. © (12.03.09 21:22) [26]

    > что же делать?


    А что, на событиях свет клином сошелся ?
  • Vladimir (12.03.09 22:01) [27]
    Нет) просто немного напрягает когда часть отправки данных пишеться в событиях клиента, а часть приема данных пишеться "где-то" причем ручками. Не знаю почему, но есть какое-то чувство, что то что я сам дописал возьмет и не сработает. Вот и ответ на вопрос "сошелся ли свет клином на событиях")
  • Тын-Дын © (13.03.09 00:16) [28]

    > Vladimir   (12.03.09 22:01) [27]


    Раз у тебя Borland-сокеты работают в режиме st/ctBlockinkg, то и используй их возможности для обработки входящих данных.
  • Сергей М. © (13.03.09 20:55) [29]

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


    Каких конкретно ?
  • Zalm © (29.04.09 02:05) [30]
    Вот попытался собрать то что указано по ссылке
    http://www.delphisources.ru/pages/faq/base/tserversocket.html
    но ничего не получилось, выдает ошибки памяти.

    procedure TForm1.ServerGetThread(Sender: TObject;
     ClientSocket: TServerClientWinSocket;
     var SocketThread: TServerClientThread);
    begin
     SocketThread := CServerThread.Create( FALSE, ClientSocket );
    end;

    Procedure CServerThread.ClientExecute;
    var
    REQUESTSIZE : integer;
    fRequest : array [0..1024] of char;
    ac,readlen : integer;
    begin
     inherited FreeOnTerminate := TRUE;
     try
       fSocketStream := TWinSocketStream.Create( ClientSocket, 1000 );
       // 100000 - это таймаут в миллисекундах.
       try
         while ( not Terminated ) and ( ClientSocket.Connected ) do
           try
             // В это место обычно помещается код,
             // ожидающий входных данных, читающий из сокета или пишущий в него
             // Пример, приведённый ниже, показывает, что можно добавить в данную
             // секцию программы.
              FillChar( fRequest, REQUESTSIZE, 0 );
              ac := 0;
              repeat
               readlen := fSocketStream.read( fRequest[ac], 1024 );
               // считываем блоки по 1024 байт, до тех пор, пока буфер
               // не заполнится
               ac := ac+readlen;
              until
              (readlen = 0) or (ac = REQUESTSIZE);
           except
             on e:exception do
             begin
               // Если произошла ошибка, то закрываем сокет и выходим
               ClientSocket.Close;
               Terminate;
             end;
           end;
       finally
         fSocketStream.Free;
       end;
     except
       on e:exception do
       begin
         // Если произошла ошибка, то закрываем сокет и выходим
         ClientSocket.Close;
         Terminate;
       end;
     end;
    end;



    Еще не понял как отправлять с клиента что-то на такой сервер...
    Помогите пожалуйста
  • Сергей М. © (29.04.09 09:16) [31]

    > inherited FreeOnTerminate := TRUE;


    Это убрать.


    > FillChar( fRequest, REQUESTSIZE, 0 );


    Чему в этот момент равно значение переменной REQUESTSIZE ?


    > Terminate;


    Это тоже убрать.


    > не понял как отправлять с клиента что-то на такой сервер


    Т.е. на какой-то другой ты понял, а вот именно на этот не понял ?
  • Zalm © (29.04.09 17:46) [32]
    обычних серверов я три штуки написал, в не блокирующем режиме, там всё просто, отправить и принять, а тут сложннее, либо же я просто пока не понял кк это делать :(

    REQUESTSIZE походу ничему не равно, даже не инициализированно.

    inherited FreeOnTerminate := TRUE; такого вроде у меня там и не было...
  • Сергей М. © (29.04.09 19:28) [33]

    > обычних серверов я три штуки написал


    Значит пора прекращать задавать откровенно дилетантские вопросы)
    "Три штуки"  - стаж как-никак солидный)


    > в не блокирующем режиме, там всё просто, отправить и принять,
    >  а тут сложннее


    И здесь все тоже самое - "отправить и принять". Никакой разницы.


    > REQUESTSIZE походу ничему не равно, даже не инициализированно


    "Походу" ты эти даже гордишься)
    Было бы чем ..


    > inherited FreeOnTerminate := TRUE; такого вроде у меня там
    > и не было


    Значит это я сам придумал от нечего делать)
  • Zalm © (29.04.09 20:27) [34]

    Procedure CServerThread.ClientExecute;
    var
    REQUESTSIZE : integer;
    fRequest : array [0..1024] of char;
    ac,readlen : integer;
    begin
    try
      fSocketStream := TWinSocketStream.Create( ClientSocket, 1000 );
      try
        while ( not Terminated ) and ( ClientSocket.Connected ) do
          try
             FillChar( fRequest, REQUESTSIZE, 0 );
             ac := 0;
             repeat
              readlen := fSocketStream.read( fRequest[ac], 1024 );
              ac := ac+readlen;
             until
             (readlen = 0) or (ac = REQUESTSIZE);
          except
            on e:exception do
            begin
              ClientSocket.Close;
            end;
          end;
      finally
        fSocketStream.Free;
      end;
    except
      on e:exception do
      begin
        ClientSocket.Close;
      end;
    end;
    end;



    С исправлениями будет так, а REQUESTSIZE какое значение присвоить?


    > "Три штуки"  - стаж как-никак солидный)

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


    > Значит пора прекращать задавать откровенно дилетантские
    > вопросы)

    да всё плохо с этими потоками, всё страшно и не понятно xD
    и ничего тут не так же... тут всё как-то по пакетам... ваще я пока не понимаю этого... клиет будет для такого блокирующего ервера будет еще одной бедой моей:(
  • Сергей М. © (30.04.09 08:34) [35]

    > REQUESTSIZE какое значение присвоить?


    Размер буфера, который ты нулями заполняешь.


    > тут всё как-то по пакетам


    Что "всё" ? По каким таким "пакетам" ?


    > клиет будет для такого блокирующего ервера


    Клиент не может ничего знать о режиме сервера и об использовании им мультипоточности, равно как и сервер о режимах своих клиентов, среди которых м.б. как блокирующие, так и неблокирующие.
  • Zalm © (30.04.09 20:27) [36]

    > Что "всё" ? По каким таким "пакетам" ?

    ну а что тут тогда делается?
    readlen := fSocketStream.read( fRequest[ac], 1024 );


    > Клиент не может ничего знать о режиме сервера


    то есть sendtext точно так же и работает с клиента?
  • Сергей М. © (30.04.09 20:34) [37]

    > а что тут тогда делается?


    Здесь предпринимается попытка чтения  очередных 1024 байт из потока входящих данных. Никакими пакетами здесь даже не пахнет.


    > sendtext точно так же и работает с клиента?


    sendtext'у абсолютно фиолетово кто ее вызывает.
    sendtext предпринимает попытку отправки некимх данных партнеру по соединению.
    Для клиентов партнером является сервер, для сервера партнерами являются клиенты.
  • Zalm © (30.04.09 23:59) [38]
    так а как прочитать одну комманду? если читать по 1024 сразу то можно и на следующий запрос взезть...

    То есть то что я отправлю через сенд текст с клинета, прилетит на сервер как раз в тот самый СокетСтрим?

    а переменную ас чем надо инициализировать?
  • Сергей М. © (01.05.09 14:24) [39]

    > а как прочитать одну комманду?


    А какое отношение это имеет к режиму ? Никакого абсолютно.


    > можно и на следующий запрос взезть


    Ну и что ? "Остаток", если он был обнаружен, никуда не пропал, если ты его, конечно, не выбросил собственноручно.. При следующем чтении к "остатку" приклеишь то что будет в очередной раз прочитано


    > переменную ас чем надо инициализировать?


    Ты же ее уже проинициализировал

    > ac := 0;
  • Zalm © (01.05.09 18:45) [40]

    > А какое отношение это имеет к режиму ? Никакого абсолютно.

    как это никакого? у меня вобще проблема с пониманием этой процедуры, как она работает, то ли там крутится вечно в цикле, то ло еще не пойми как.
    К какой момент мы что-то читаем из буфера и тд... :(
  • Сергей М. © (01.05.09 21:42) [41]

    > как это никакого?


    Да вот так - никакого и всё тут)


    > К какой момент мы что-то читаем из буфера


    В момент когда вы, в соответствии с вашим прикладным протоколом информационного обмена, ждете от партнера по соединению некие данные.

    А что бы изменилось, если бы был неблокирующий режим ?


    > и тд


    Конкретнее ..
  • Zalm © (02.05.09 19:04) [42]

    > А что бы изменилось, если бы был неблокирующий режим ?

    В обычном режиме всё просто, пришло что-то, появился евент и поехали
    обрабатывать, а тут ниче не ясно.

    > В момент когда вы, в соответствии с вашим прикладным протоколом
    > информационного обмена, ждете от партнера по соединению
    > некие данные.

    я их всегда жду))) Этот поток же ничего не сделает сам когда придут данные

    и еще, мы же в ac складываем integer, как потом оттуда строку взять?))
  • Сергей М. © (02.05.09 21:07) [43]

    > появился евент и поехали
    > обрабатывать


    Ну а если нет этого самого "евента" ? Чем занята в отсутствие "евентов" твоя программа ? Нашиша, спрашивается , было задействовать stThreadBlocking ? Ради моды что ли ?


    > я их всегда жду


    Почему ?
    Протокол информационного обмена, может, соизволишь представить ?


    > мы же в ac складываем integer, как потом оттуда строку взять?


    О боже ..

    Что за ахинею ты несешь ?
  • Zalm © (03.05.09 20:42) [44]
    почему ахинею-то?

    а вот это?


    ac : integer;

                       readlen := fSocketStream.read( fRequest[ac], 1024 );
                       ac := ac+readlen;




    > Протокол информационного обмена, может, соизволишь представить
    > ?


    не понял
  • Сергей М. © (03.05.09 21:45) [45]

    > а вот это?


    Ну и какую строку "оттуда", т.е. из ac, ты собрался "взять" ?


    > не понял


    Что ты не понял ?
    HTTP, FTP, SMTP, SNMP, BGP, SCP и т.д. и т.п. - слышал когда-нить хоть краем уха эти аббревиатуры ?
    Все это - протоколы информационного обмена.
    Каждый из них устанавливает определенные соглашения и правила, по которым партнеры по соединению "разговаривают" между собой.
    Посылая, скажем, сообщения в эту конференцию и получая из конференции сообщения, твой клиент-браузер обменивается с сервером сообщениями в соответствии с протоколом HTTP.

    А у тебя что за протокол ?
  • Zalm © (04.05.09 23:05) [46]
    да, это я всё знаю, почти все эти протоколы, только не понял снчала что спрашивается)) Socket  у меня.


    > Ну и какую строку "оттуда", т.е. из ac, ты собрался "взять"
    > ?

    Ну а откуда тогда брать строку?

    я ж грю я ниче с этим потоком не понимаю :(
  • Сергей М. © (05.05.09 08:42) [47]

    > Socket  у меня


    Сокет - объект, предназначенный для выполнения коммуникационных функций транспортного уровня. А строки - это уже прикладной уровень, сокет о них ничего не знает, ибо это не его задача.


    > откуда тогда брать строку?
    >


    А откуда она возьмется, эта строка, прежде чем ее "брать" ?


    > ниче с этим потоком не понимаю


    Ну и не используй его, если "грю ниче не понимаю") ..Никто ж не заставляет ..

    У объекта, представленного св-вом ClientSocket, есть знакомые тебе по неблок.режиму методы ReceiveText/Buf, SendText/Buf - их и пользуй
  • Zalm © (07.05.09 12:47) [48]
    ну да, у клиента-то есть... а как принимать на сервер, и отправлять с сервера я пока  из нашего разговора не понял(
  • Сергей М. © (07.05.09 12:56) [49]

    > как принимать на сервер


    ClientSocket.ReceiveText/Buf


    > отправлять с сервера


    ClientSocket.SendText/Buf/Stream

    Что тут не понятного ?
  • Zalm © (08.05.09 18:31) [50]
    это я использовал, это для обычного режима, а в той процедуре клиентского потока что тогда делается?....(
  • Сергей М. © (08.05.09 20:05) [51]

    > это для обычного режима


    Для какого такого "обычного" ?
    Где ты узрел в ДОКУМЕНТАЦИИ фразу "обычный режим" ?
    И блокирующий и неблокирующий режимы являются "обычными", иных режимов у этих компонентов попросту нет.
    Для любого из этих режимов упомянутые методы применимы.


    > в той процедуре клиентского потока что тогда делается?


    Там осуществляются циклические попытки чтения из потока.
 
Конференция "Сети" » TSocketClient и TSocketServer работа с потоками
Есть новые Нет новых   [134435   +33][b:0][p:0.003]