Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.09.11;
Скачать: CL | DM;

Вниз

TSocketClient и TSocketServer работа с потоками   Найти похожие ветки 

 
Vladimir   (2009-03-01 10:27) [0]

Доброго времени суток всем! Заранее извиняюсь если данная тема уже поднималась, но в поиске ответа на свой вопрос я так и не нашел. Требуется написать клиент-серверное приложение. В данный момент написал пробные клиент и сервер на SocketClient и SocketServer соответсвенно работающие в синхронном режиме. Подскажите пожалуйста каким образом  организовать их работу в асинхронном режиме, чтобы сервер одновременно обслуживал несколько клиентов! И ещё есть такая проблема при посылке текста через SendText на сервер приходят иероглифы, причем  это только на Windows Vista, на Windows XP все работало нормально. Использую Delphi 2009. Из-за чего это может быть? Заранее спасибо.


 
Сергей М. ©   (2009-03-01 12:30) [1]

SocketServer в любом режиме способен обслуживать более одного клиента.
В режиме stNonBlocking обслуживание всех клиентов последовательное в одном потоке, в режиме stThreadBlocking каждое активное кл.соединение уже обслуживается в отдельном потоке.


 
Сергей М. ©   (2009-03-01 12:42) [2]

Режим работы ClientSocket м.б. любым и никаким образом не каксается режима работы ServerSocket


 
Vladimir   (2009-03-01 12:48) [3]

Спасибо конечно)) Но я и спрашиваю каким образом организовать работу сервера в режиме stThreadBlocking. Ведь для каждого клиента надо создавать отдельный поток, а когда клиент отключиться его надо уничтожать и т.д.


 
Сергей М. ©   (2009-03-01 12:53) [4]


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


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


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


TServerSocket все это делает сам


 
Vladimir   (2009-03-01 13:07) [5]

Сергей, видимо мы с вами друг друга немного не понимаем. Вы не могли бы выложить код самого простого многопоточного сервера и сделать пометки в тех местах где поток создаеться, где можно проделывать какие-либо действия с клиентом(принимать от него данные, посылать ему данные) и где поток уничтожаеться. Может быть тогда у меня получиться разобраться. И из-за чего может быть проблема с SendText?


 
Сергей М. ©   (2009-03-01 13:14) [6]

Вот не ахти какой, но достаточно показательный пример:

http://www.delphisources.ru/pages/faq/base/tserversocket.html


 
Slym ©   (2009-03-02 05:32) [7]

зачем далеко ходить? на этом форуме недалече второй страницы
http://delphimaster.net/view/6-1227604925/


 
Empleado ©   (2009-03-02 12:32) [8]


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

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


 
Vladimir   (2009-03-03 12:09) [9]


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

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


 
Сергей М. ©   (2009-03-03 12:27) [10]


> Vladimir   (03.03.09 12:09) [9]


Что ты послал, то и получишь.
Данные при их транспортировке под управлением TCP доставляются в гарантированно неизменном виде.


 
Empleado ©   (2009-03-03 12:29) [11]


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

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

Прочитать в хелпе об использовании Unicode в D2009. Там подробно написано, с примерами, как работать со строками.
Не использовать SendText, a SendBuf, например, либо TWinSocketStream.Read/Write.
При чтении из сокета, преобразовывать полученное в Unicode. Например с помощью класса TEncoding.Unicode.GetString [или же StringOf в случае telnet].


 
Vladimir   (2009-03-03 13:46) [12]

спасибо, попробую.


 
Vladimir   (2009-03-03 20:31) [13]

Спасибо, все получилось. Только появился ещё один вопрос. Когда клиент присоединяеться к серверу для него создаеться отдельный поток, а из потока вызываеться некая процедура которая работает с файлом(записывает в него данные) и закрывает файл... Дело в том что если сервер работает не в многопоточном режиме, то проблемы одновременного обращения к файлу не возникает, а как быть при моем раскладе с потоками? Как "объяснить" компьютеру  подождать пока один поток поработает с файлом и только потом самому приступать к работе с ним?


 
Сергей М. ©   (2009-03-03 20:46) [14]


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


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


 
medved_68 ©   (2009-03-04 08:56) [15]


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

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


 
Vladimir   (2009-03-04 09:06) [16]


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

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


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

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

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

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


 
Сергей М. ©   (2009-03-04 09:45) [17]


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


И какая СУБД при этом тобой используется ?
Или ты валишь данные прямо в файл, называя при этом "базой данных" именно этот файл ?


 
Vladimir   (2009-03-04 10:14) [18]


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


В чем то ты прав. СУБД я не использую, но и не валю все прямо в файл(есть некая структура)... но, думаю что сейчас объяснять все в подробностях ни к чему.


 
Сергей М. ©   (2009-03-04 10:22) [19]

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


 
Vladimir   (2009-03-04 10:43) [20]


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


Хорошо, спасибо, это я сделаю. Но мне помнилось что можно как то секцию кода блокировать или может быть называеться это как то по другому. Просто давно как то писал простенький многопоточный чат и там тоже  необходимо было предотвратить одновременное обращение как к файлу, так и к общим переменным. Исходников того чата не осталось и как именно прописывал это не помню.


 
Сергей М. ©   (2009-03-04 11:05) [21]


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


Вот как раз "критической секцией" это и называется.


 
Vladimir   (2009-03-04 11:13) [22]

спасибо Сергей. Буду вспоминать и разбираться.


 
Vladimir   (2009-03-08 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. В чем может быть дело?


 
Сергей М. ©   (2009-03-09 15:00) [24]


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


Нет, не должен.
Событие OnRead возбуждается только при условии ClientType = ctNonBlocking, а у тебя ctBlocking


 
Vladimir   (2009-03-12 20:59) [25]

И что же делать? Оставить тот вариант который я уже реализовал? Вроде он рабочий...


 
Сергей М. ©   (2009-03-12 21:22) [26]


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


А что, на событиях свет клином сошелся ?


 
Vladimir   (2009-03-12 22:01) [27]

Нет) просто немного напрягает когда часть отправки данных пишеться в событиях клиента, а часть приема данных пишеться "где-то" причем ручками. Не знаю почему, но есть какое-то чувство, что то что я сам дописал возьмет и не сработает. Вот и ответ на вопрос "сошелся ли свет клином на событиях")


 
Тын-Дын ©   (2009-03-13 00:16) [28]


> Vladimir   (12.03.09 22:01) [27]


Раз у тебя Borland-сокеты работают в режиме st/ctBlockinkg, то и используй их возможности для обработки входящих данных.


 
Сергей М. ©   (2009-03-13 20:55) [29]


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


Каких конкретно ?


 
Zalm ©   (2009-04-29 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;


Еще не понял как отправлять с клиента что-то на такой сервер...
Помогите пожалуйста


 
Сергей М. ©   (2009-04-29 09:16) [31]


> inherited FreeOnTerminate := TRUE;


Это убрать.


> FillChar( fRequest, REQUESTSIZE, 0 );


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


> Terminate;


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


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


Т.е. на какой-то другой ты понял, а вот именно на этот не понял ?


 
Zalm ©   (2009-04-29 17:46) [32]

обычних серверов я три штуки написал, в не блокирующем режиме, там всё просто, отправить и принять, а тут сложннее, либо же я просто пока не понял кк это делать :(

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

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


 
Сергей М. ©   (2009-04-29 19:28) [33]


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


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


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


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


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


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


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


Значит это я сам придумал от нечего делать)


 
Zalm ©   (2009-04-29 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
и ничего тут не так же... тут всё как-то по пакетам... ваще я пока не понимаю этого... клиет будет для такого блокирующего ервера будет еще одной бедой моей:(


 
Сергей М. ©   (2009-04-30 08:34) [35]


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


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


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


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


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


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


 
Zalm ©   (2009-04-30 20:27) [36]


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

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


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


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


 
Сергей М. ©   (2009-04-30 20:34) [37]


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


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


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


sendtext"у абсолютно фиолетово кто ее вызывает.
sendtext предпринимает попытку отправки некимх данных партнеру по соединению.
Для клиентов партнером является сервер, для сервера партнерами являются клиенты.


 
Zalm ©   (2009-04-30 23:59) [38]

так а как прочитать одну комманду? если читать по 1024 сразу то можно и на следующий запрос взезть...

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

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


 
Сергей М. ©   (2009-05-01 14:24) [39]


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


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


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


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


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


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

> ac := 0;


 
Zalm ©   (2009-05-01 18:45) [40]


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

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



Страницы: 1 2 вся ветка

Текущий архив: 2011.09.11;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.008 c
15-1306136137
clickmaker
2011-05-23 11:35
2011.09.11
как форматировать html


2-1306413330
Очень злой
2011-05-26 16:35
2011.09.11
Inherited


2-1306344649
xozain
2011-05-25 21:30
2011.09.11
Работа с ТХТ


15-1306044364
Архип
2011-05-22 10:06
2011.09.11
Использование заголовычных файлов С++


2-1306708852
R_R
2011-05-30 02:40
2011.09.11
Как посмотреть i-ую строчку в ADOTable?