

| стрелялки |
| спорт |
| драки |
| стратегии |
| гонки |
| логические |
| азартные |
| аркады |
| эротические |
| фильмы онлайн |
| косметическое редактирование класса TClientSocketThread |
|
Вновь вернемся к листингу главной формы проекта. Следующая наша задача - косметическое редактирование класса TClientSocketThread (напомню, что этот класс отвечает за поведение потока обслуживания). Мы объявим свой поток TClientThread, который отличается от предка новым полем, предназначенным для хранения ссылки на дочернюю форму ThreadForm : Tfrm-Child, и несколько другой интерпретацией деструктора. unit main; interface uses .... ChildUnit; type TClientTh read=class(TClientSocketThread) public ThreadForm : TfrmChild; //для связи с дочерней формой destructor Destroy; override; end; type TfrmMain = class(TForm) implementation { TClientThread } destructor TClientThread.Destroy; begin ThreadForm.Close; //уничтожение соответствующей потоку формы inherited; end; Поле ThreadForm предназначено для связи потока обслуживания с объявленной ранее дочерней формой TfrmChild. А в деструкторе предусмотрено закрытие (и уничтожение) формы, принадлежащей потоку TClientThread. Спешу всех обрадовать: две трети проекта готовы и мы приступаем к самому интересному. Напомню, что в момент, предшествующий созданию потока обслуживания, у сервера TTCPServer возникает событие OnGetThread(). Именно здесь мы можем подменить создаваемый по умолчанию TClientSock-etThread своим потоком - TClientThread. Все было бы элементарно, если бы нам не требовалось одновременно с рождением потока обслуживания создавать соответствующую ему форму Tfrm-Child. Вполне сознательно приведу вариант лобового, а потому неработающего решения. Для того чтобы вы случайно не применили его в своих проектах, каждая строка процедуры помечена как комментарий: //procedure TfrmMain.TcpServer1GetThread(Sender: TObject; // var ClientSocketThread: TClientSocketThread); // var ClientThread : TClientThread; // frmChild : TfrmChild; // begin // frmChild:=TfrmChild.Create(frmMain); // ClientThread:=TClientThread.Create(TcpServer1.ServerSocketThread); // ClientThread.ThreadForm:=frmChild; // end; С точки зрения синтаксиса и семантики код идеален, но в нем не учтено, что событие OnGetThread() генерируется потоком управления и требует обязательной синхронизации с методами VCL. Поэтому в 99 случаях из 100 выполнение приложения приостановится на самой первой строке, создающей дочернюю форму. Вывод однозначный: конструктор формы надо выносить из события в отдельную процедуру и вызывать с помощью специализированного метода Synchronize(). С этой целью в секции частных объявлений главной формы проекта декларируем переменную ClientThread и процедуру NewClientThread(). private { Private declarations } ClientThread: TClientThread; procedure NewClientThread; procedure TfrmMain.NewClientThread; begin {ClientThread - переменная объявлена в секции private} ClientThread:=TClientThread.Create(TcpServer1.ServerSocketThread); ClientThread.ThreadForm:=TfrmChild.Create(frmMain); ClientThread.ThreadForm.Thread:=ClientThread; end; В процедуру NewClientThread() вынесены действия, связанные с созданием потока обслуживания и соответствующей ему дочерней формы. Созданный поток ассоциируется с переменной ClientThread. Теперь возвращаемся к событию OnGetThread() : procedure TfrmMain.TcpServer1GetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread); var ServerSocketThread: TServerSocketThread; begin ServerSocketThread:=TcpServer1.ServerSocketThread; {синхронизация потока управления с созданием нового экземпляра дочерней формы} TServerSocketThread.Synchronize(ServerSocketThread as TThread, NewClientThread); {результат процедуры NewClientThread находится в переменной ClientThread} ClientSocketThread:=ClientThread as TClientSocketThread; end; Теперь процедура, создающая нашу версию потока и связанную с ним форму, вызывается из метода Synchronize(), что соответствует правилам работы с потоками. Остался заключительный штрих. Опишем событие OnAccept(). В нем обрабатываются поступающие от клиентских приложений данные. procedure TfrmMain.TcpServer1Accept(Sender: TObject; ClientSocket: TCustomIpClient); var s : string; begin with TClientThread(ClientSocket.GetThreadObject) do begin //узнаем адрес сокета-клиента s:=Format('Клиент %s (%s)', [ClientSocket.LookupHostName(ClientSocket.RemoteHost), ClientSocket.RemoteHost]); ThreadForm.Memo1.Lines.Add(s); ThreadForm.Caption:=s; s := ClientSocket.Receiveln; //цикл получения данных while s <> do begin ThreadForm.Memo1.Lines.Add(s); s := ClientSocket.Receiveln; end; if Assigned(ThreadForm) then ThreadForm.Caption:='Клиент отключен'; end; end; Для получения данных используем цикл while..do, который выполняется до тех пор, пока метод ReceiveLn() возвращает текстовые строки. |
