-
Доброго времени суток всем! Заранее извиняюсь если данная тема уже поднималась, но в поиске ответа на свой вопрос я так и не нашел. Требуется написать клиент-серверное приложение. В данный момент написал пробные клиент и сервер на SocketClient и SocketServer соответсвенно работающие в синхронном режиме. Подскажите пожалуйста каким образом организовать их работу в асинхронном режиме, чтобы сервер одновременно обслуживал несколько клиентов! И ещё есть такая проблема при посылке текста через SendText на сервер приходят иероглифы, причем это только на Windows Vista, на Windows XP все работало нормально. Использую Delphi 2009. Из-за чего это может быть? Заранее спасибо.
-
SocketServer в любом режиме способен обслуживать более одного клиента.
В режиме stNonBlocking обслуживание всех клиентов последовательное в одном потоке, в режиме stThreadBlocking каждое активное кл.соединение уже обслуживается в отдельном потоке.
-
Режим работы ClientSocket м.б. любым и никаким образом не каксается режима работы ServerSocket
-
Спасибо конечно)) Но я и спрашиваю каким образом организовать работу сервера в режиме stThreadBlocking. Ведь для каждого клиента надо создавать отдельный поток, а когда клиент отключиться его надо уничтожать и т.д.
-
> Но я и спрашиваю каким образом организовать работу сервера
> в режиме stThreadBlocking
Это и есть синхронный режим. Только не самого компонента, а сокетов, которые он создает и использует в ходе работы.
> для каждого клиента надо создавать отдельный поток, а когда
> клиент отключиться его надо уничтожать
TServerSocket все это делает сам
-
Сергей, видимо мы с вами друг друга немного не понимаем. Вы не могли бы выложить код самого простого многопоточного сервера и сделать пометки в тех местах где поток создаеться, где можно проделывать какие-либо действия с клиентом(принимать от него данные, посылать ему данные) и где поток уничтожаеться. Может быть тогда у меня получиться разобраться. И из-за чего может быть проблема с SendText?
-
-
-
> И из-за чего может быть проблема с SendText?
Из-за Уникод?
-
> Из-за Уникод?
А как можно с этим разобраться? чтоб текст нормальный приходил?
-
> Vladimir (03.03.09 12:09) [9]
Что ты послал, то и получишь.
Данные при их транспортировке под управлением TCP доставляются в гарантированно неизменном виде.
-
> А как можно с этим разобраться? чтоб текст нормальный приходил?
Например, отправлять Уникод и принимать Уникод.
Прочитать в хелпе об использовании Unicode в D2009. Там подробно написано, с примерами, как работать со строками.
Не использовать SendText, a SendBuf, например, либо TWinSocketStream.Read/Write.
При чтении из сокета, преобразовывать полученное в Unicode. Например с помощью класса TEncoding.Unicode.GetString [или же StringOf в случае telnet].
-
спасибо, попробую.
-
Спасибо, все получилось. Только появился ещё один вопрос. Когда клиент присоединяеться к серверу для него создаеться отдельный поток, а из потока вызываеться некая процедура которая работает с файлом(записывает в него данные) и закрывает файл... Дело в том что если сервер работает не в многопоточном режиме, то проблемы одновременного обращения к файлу не возникает, а как быть при моем раскладе с потоками? Как "объяснить" компьютеру подождать пока один поток поработает с файлом и только потом самому приступать к работе с ним?
-
> процедура которая работает с файлом(записывает в него данные)
> и закрывает файл
Уж не файл ли протокола ?
-
> Как "объяснить" компьютеру подождать пока один поток поработает
> с файлом и только потом самому приступать к работе с ним?
>
Для этого придуманы объекты синхронизации и доступ к файлу в режиме ShareDenyNone
-
> Уж не файл ли протокола ?
нет, файл базы данных
> Для этого придуманы объекты синхронизации и доступ к файлу
> в режиме ShareDenyNone
я знаю... но хотел узнать нельзя ли каким то образом блокировать секцию кода, до тех пор пока она не будет выполнена... сделать что то вроде:
Lock;
выполняемый операции
UnLock;
чтобы если какой то поток уже начал обрабатывать этот участок кода, то другому потоку пришлось ждать пока первый закончит с ним работать.
-
> нет, файл базы данных
И какая СУБД при этом тобой используется ?
Или ты валишь данные прямо в файл, называя при этом "базой данных" именно этот файл ?
-
> Или ты валишь данные прямо в файл, называя при этом "базой
> данных" именно этот файл ?
В чем то ты прав. СУБД я не использую, но и не валю все прямо в файл(есть некая структура)... но, думаю что сейчас объяснять все в подробностях ни к чему.
-
Ясно.
Считай что валишь прямо в файл.
Ну тогда его надо бы защитить критической секцией (см. TCriticalSection).
-
> Ясно.
> Считай что валишь прямо в файл.
> Ну тогда его надо бы защитить критической секцией (см. TCriticalSection).
>
Хорошо, спасибо, это я сделаю. Но мне помнилось что можно как то секцию кода блокировать или может быть называеться это как то по другому. Просто давно как то писал простенький многопоточный чат и там тоже необходимо было предотвратить одновременное обращение как к файлу, так и к общим переменным. Исходников того чата не осталось и как именно прописывал это не помню.
-
> помнилось что можно как то секцию кода блокировать или может
> быть называеться это как то по другому
Вот как раз "критической секцией" это и называется.
-
спасибо Сергей. Буду вспоминать и разбираться.
-
С критическими секциями разобрался. Все ок. Но возникла новая проблема. 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. В чем может быть дело?
-
> ClientSocket должен автоматически входить в событие Read
> когда к нему приходят данные
Нет, не должен.
Событие OnRead возбуждается только при условии ClientType = ctNonBlocking, а у тебя ctBlocking
-
И что же делать? Оставить тот вариант который я уже реализовал? Вроде он рабочий...
-
> что же делать?
А что, на событиях свет клином сошелся ?
-
Нет) просто немного напрягает когда часть отправки данных пишеться в событиях клиента, а часть приема данных пишеться "где-то" причем ручками. Не знаю почему, но есть какое-то чувство, что то что я сам дописал возьмет и не сработает. Вот и ответ на вопрос "сошелся ли свет клином на событиях")
-
> Vladimir (12.03.09 22:01) [27]
Раз у тебя Borland-сокеты работают в режиме st/ctBlockinkg, то и используй их возможности для обработки входящих данных.
-
> часть отправки данных пишеться в событиях клиента
Каких конкретно ?
-
Вот попытался собрать то что указано по ссылке
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 );
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;
Terminate;
end;
end;
finally
fSocketStream.Free;
end;
except
on e:exception do
begin
ClientSocket.Close;
Terminate;
end;
end;
end;
Еще не понял как отправлять с клиента что-то на такой сервер...
Помогите пожалуйста
-
> inherited FreeOnTerminate := TRUE;
Это убрать.
> FillChar( fRequest, REQUESTSIZE, 0 );
Чему в этот момент равно значение переменной REQUESTSIZE ?
> Terminate;
Это тоже убрать.
> не понял как отправлять с клиента что-то на такой сервер
Т.е. на какой-то другой ты понял, а вот именно на этот не понял ?
-
обычних серверов я три штуки написал, в не блокирующем режиме, там всё просто, отправить и принять, а тут сложннее, либо же я просто пока не понял кк это делать :(
REQUESTSIZE походу ничему не равно, даже не инициализированно.
inherited FreeOnTerminate := TRUE; такого вроде у меня там и не было...
-
> обычних серверов я три штуки написал
Значит пора прекращать задавать откровенно дилетантские вопросы)
"Три штуки" - стаж как-никак солидный)
> в не блокирующем режиме, там всё просто, отправить и принять,
> а тут сложннее
И здесь все тоже самое - "отправить и принять". Никакой разницы.
> REQUESTSIZE походу ничему не равно, даже не инициализированно
"Походу" ты эти даже гордишься)
Было бы чем ..
> inherited FreeOnTerminate := TRUE; такого вроде у меня там
> и не было
Значит это я сам придумал от нечего делать)
-
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
и ничего тут не так же... тут всё как-то по пакетам... ваще я пока не понимаю этого... клиет будет для такого блокирующего ервера будет еще одной бедой моей:(
-
> REQUESTSIZE какое значение присвоить?
Размер буфера, который ты нулями заполняешь.
> тут всё как-то по пакетам
Что "всё" ? По каким таким "пакетам" ?
> клиет будет для такого блокирующего ервера
Клиент не может ничего знать о режиме сервера и об использовании им мультипоточности, равно как и сервер о режимах своих клиентов, среди которых м.б. как блокирующие, так и неблокирующие.
-
> Что "всё" ? По каким таким "пакетам" ?
ну а что тут тогда делается?
readlen := fSocketStream.read( fRequest[ac], 1024 );
> Клиент не может ничего знать о режиме сервера
то есть sendtext точно так же и работает с клиента?
-
> а что тут тогда делается?
Здесь предпринимается попытка чтения очередных 1024 байт из потока входящих данных. Никакими пакетами здесь даже не пахнет.
> sendtext точно так же и работает с клиента?
sendtext'у абсолютно фиолетово кто ее вызывает.
sendtext предпринимает попытку отправки некимх данных партнеру по соединению.
Для клиентов партнером является сервер, для сервера партнерами являются клиенты.
-
так а как прочитать одну комманду? если читать по 1024 сразу то можно и на следующий запрос взезть...
То есть то что я отправлю через сенд текст с клинета, прилетит на сервер как раз в тот самый СокетСтрим?
а переменную ас чем надо инициализировать?
-
> а как прочитать одну комманду?
А какое отношение это имеет к режиму ? Никакого абсолютно.
> можно и на следующий запрос взезть
Ну и что ? "Остаток", если он был обнаружен, никуда не пропал, если ты его, конечно, не выбросил собственноручно.. При следующем чтении к "остатку" приклеишь то что будет в очередной раз прочитано
> переменную ас чем надо инициализировать?
Ты же ее уже проинициализировал
> ac := 0;
-
> А какое отношение это имеет к режиму ? Никакого абсолютно.
как это никакого? у меня вобще проблема с пониманием этой процедуры, как она работает, то ли там крутится вечно в цикле, то ло еще не пойми как.
К какой момент мы что-то читаем из буфера и тд... :(
-
> как это никакого?
Да вот так - никакого и всё тут)
> К какой момент мы что-то читаем из буфера
В момент когда вы, в соответствии с вашим прикладным протоколом информационного обмена, ждете от партнера по соединению некие данные.
А что бы изменилось, если бы был неблокирующий режим ?
> и тд
Конкретнее ..
-
> А что бы изменилось, если бы был неблокирующий режим ?
В обычном режиме всё просто, пришло что-то, появился евент и поехали
обрабатывать, а тут ниче не ясно.
> В момент когда вы, в соответствии с вашим прикладным протоколом
> информационного обмена, ждете от партнера по соединению
> некие данные.
я их всегда жду))) Этот поток же ничего не сделает сам когда придут данные
и еще, мы же в ac складываем integer, как потом оттуда строку взять?))
-
> появился евент и поехали
> обрабатывать
Ну а если нет этого самого "евента" ? Чем занята в отсутствие "евентов" твоя программа ? Нашиша, спрашивается , было задействовать stThreadBlocking ? Ради моды что ли ?
> я их всегда жду
Почему ?
Протокол информационного обмена, может, соизволишь представить ?
> мы же в ac складываем integer, как потом оттуда строку взять?
О боже ..
Что за ахинею ты несешь ?
-
почему ахинею-то?
а вот это?
ac : integer;
readlen := fSocketStream.read( fRequest[ac], 1024 );
ac := ac+readlen;
> Протокол информационного обмена, может, соизволишь представить
> ?
не понял
-
> а вот это?
Ну и какую строку "оттуда", т.е. из ac, ты собрался "взять" ?
> не понял
Что ты не понял ?
HTTP, FTP, SMTP, SNMP, BGP, SCP и т.д. и т.п. - слышал когда-нить хоть краем уха эти аббревиатуры ?
Все это - протоколы информационного обмена.
Каждый из них устанавливает определенные соглашения и правила, по которым партнеры по соединению "разговаривают" между собой.
Посылая, скажем, сообщения в эту конференцию и получая из конференции сообщения, твой клиент-браузер обменивается с сервером сообщениями в соответствии с протоколом HTTP.
А у тебя что за протокол ?
-
да, это я всё знаю, почти все эти протоколы, только не понял снчала что спрашивается)) Socket у меня.
> Ну и какую строку "оттуда", т.е. из ac, ты собрался "взять"
> ?
Ну а откуда тогда брать строку?
я ж грю я ниче с этим потоком не понимаю :(
-
> Socket у меня
Сокет - объект, предназначенный для выполнения коммуникационных функций транспортного уровня. А строки - это уже прикладной уровень, сокет о них ничего не знает, ибо это не его задача.
> откуда тогда брать строку?
>
А откуда она возьмется, эта строка, прежде чем ее "брать" ?
> ниче с этим потоком не понимаю
Ну и не используй его, если "грю ниче не понимаю") ..Никто ж не заставляет ..
У объекта, представленного св-вом ClientSocket, есть знакомые тебе по неблок.режиму методы ReceiveText/Buf, SendText/Buf - их и пользуй
-
ну да, у клиента-то есть... а как принимать на сервер, и отправлять с сервера я пока из нашего разговора не понял(
-
> как принимать на сервер
ClientSocket.ReceiveText/Buf
> отправлять с сервера
ClientSocket.SendText/Buf/Stream
Что тут не понятного ?
-
это я использовал, это для обычного режима, а в той процедуре клиентского потока что тогда делается?....(
-
> это для обычного режима
Для какого такого "обычного" ?
Где ты узрел в ДОКУМЕНТАЦИИ фразу "обычный режим" ?
И блокирующий и неблокирующий режимы являются "обычными", иных режимов у этих компонентов попросту нет.
Для любого из этих режимов упомянутые методы применимы.
> в той процедуре клиентского потока что тогда делается?
Там осуществляются циклические попытки чтения из потока.