|
||||||||||
|
||||||||||
|
||||||||||
|
Программирование сокетов в Delphi
Автор: Павел Введение Данная статья посвящена созданию приложений архитектуры клиент/сервер в Borland Delphi на основе сокетов ("sockets" - гнезда). А написал я эту статью не просто так, а потому что в последнее время этот вопрос очень многих стал интересовать. Пока что затронем лишь создание клиентской части сокетного приложения. Впервые я познакомился с сокетами, если не ошибаюсь, год или полтора назад. Тогда стояла задача разработать прикладной протокол, который бы передавал на серверную машину (работающую на ОС Unix/Linux) запрос и получал ответ по сокетному каналу. Надо заметить, что в отличие от любых других протоколов (FTP, POP, SMTP, HTTP, и т.д.), сокеты - это база для этих протоколов. Таким образом, пользуясь сокетами, можно самому создать (симитировать) и FTP, и POP, и любой другой протокол, причем не обязательно уже созданный, а даже свой собственный! Итак, начнем с теории. Если Вы убежденный практик (и в глаза не можете видеть всяких алгоритмов), то Вам следует пропустить этот раздел. Алгоритм работы с сокетными протоколамиТак что же позволяют нам делать сокеты?... Да все что угодно! И в этом одно из главных достоинств этого способа обмена данными в сети. Дело в том, что при работе с сокетом Вы просто посылаете другому компьютеру последовательность символов. Так что этим методом Вы можете посылать как простые сообщения, так и целые файлы! Причем, контролировать правильность передачи Вам не нужно (как это было при работе с COM-портами)! Ниже следует примерная схема работы с сокетами в Дельфи-приложениях Разберем схему подробнее:
Описание свойств и методов компонента TClientSocket Здесь мы познакомимся с основными свойствами, методами и событиями компонента TClientSocket.
Практика и примеры Легче всего (и полезней) изучать любые методы программирования на практике. Поэтому далее приведены примеры с некоторыми комментариями: Пример 1. Простейшая сокетная программа {... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1} {В форму нужно поместить кнопку TButton и два TEdit. При нажатии на кнопку вызывается обработчик события OnClick - Button1Click. Перед этим в первый из TEdit-ов нужно ввести хост-имя, а во второй - порт удаленного компьютера. НЕ ЗАБУДЬТЕ ПОМЕСТИТЬ В ФОРМУ КОМПОНЕНТ TClientSocket!} procedure Button1Click(Sender: TObject); begin {Присваиваем свойствам Host и Port нужные значения} ClientSocket1.Host := Edit1.Text; ClientSocket1.Port := StrToInt(Edit2.Text); {Пытаемся открыть сокет и установить соединение} ClientSocket1.Open; end; procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin {Как только произошло соединение - закрываем сокет и прерываем связь} ClientSocket1.Close; end; Если Вы думаете, что данный пример программы совершенно бесполезен и не может принести никакой пользы, то глубоко ошибаетесь. Приведенный код - простейший пример сканера портов (PortScanner). Суть такой утилиты в том, чтобы проверять, включен ли указанный порт и готов ли он к приему/передаче данных. Именно на таком принципе основан PortScanner из программы NetTools Pro. Далее следует другой пример, в котором по сокету передаются и принимаются текстовые сообщения: Пример 2. Посылка/прием текстовых сообщений по сокетам {... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1} {В форму нужно поместить две кнопки TButton и три TEdit. При нажатии на первую кнопку вызывается обработчик события OnClick - Button1Click. Перед этим в первый из TEdit-ов нужно ввести хост-имя, а во второй - порт удаленного компьютера. После установления соединения можно посылать текстовые сообщения, вводя текст в третий TEdit и нажимая вторую кнопку TButton. Чтобы отсоединиться, нужно еще раз нажать первую TButton. Еще нужно добавить TListBox, в который будем помещать принятые и отправленные сообщения. НЕ ЗАБУДЬТЕ ПОМЕСТИТЬ В ФОРМУ КОМПОНЕНТ TClientSocket!} procedure Button1Click(Sender: TObject); begin {Если соединение уже установлено - прерываем его.} if ClientSocket1.Active then begin ClientSocket1.Close; Exit; {...и выходим из обработчика} end; {Присваиваем свойствам Host и Port нужные значения} ClientSocket1.Host := Edit1.Text; ClientSocket1.Port := StrToInt(Edit2.Text); {Пытаемся открыть сокет и установить соединение} ClientSocket1.Open; end; procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin {Как только произошло соединение - посылаем приветствие} Socket.SendText('Hello!'); ListBox1.Items.Add('< Hello!'); end; procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); begin {Если пришло сообщение - добавляем его в ListBox} ListBox1.Items.Add('> ' + Socket.ReceiveText); end; procedure Button2Click(Sender: TObject); begin {Нажата кнопка - посылаем текст из третьего TEdit} ClientSocket1.Socket.SendText(Edit3.Text); ListBox1.Items.Add('< ' + Edit3.Text); end; ПРИМЕЧАНИЕ: В некоторых случаях (зависящих от сервера) нужно после каждого сообщения посылать перевод строки: ClientSocket1.Socket.SendText(Edit3.Text + #10); Работа с сокетным потоком "А как еще можно работать с сокетом?", - спросите Вы. Естественно, приведенный выше метод - не самое лучшее решение. Самих методов организации работы с сокетами очень много. Я приведу лишь еще один дополнительный - работа через поток. Наверняка, многие из Вас уже имеют опыт работы, если не с потоками (stream), то с файлами - точно. Для тех, кто не знает, поток - это канал для обмена данными, работа с которым аналогична работе с обычным файлом. Нижеприведенный пример показывает, как организовать поток для работы с сокетом: Пример 3. Поток для работы с сокетом procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var c: Char; MySocket: TWinSocketStream; begin {Как только произошло соединение - создаем поток и ассоциируем его с сокетом (60000 - таймаут в мсек)} MySocket := TWinSocketStream.Create(Socket, 60000); {Оператор WaitForData ждет данных из потока указанное время в мсек (в данном примере - 100) и возвращает True, если получен хотя бы один байт данных, False - если нет никаких данных из потока.} while not MySocket.WaitForData(100) do Application.ProcessMessages; {Application.ProcessMessages позволяет Windows перерисовать нужные элементы окна и дает время другим программам. Если бы этого оператора не было и данные бы довольно долго не поступали, то система бы слегка "подвисла".} MySocket.Read(c, 1); {Оператор Read читает указанное количество байт из потока (в данном примере - 1) в указанную переменную определенного типа (в примере - в переменную c типа Char). Обратите внимание на то, что Read, в отличие от ReadBuffer, не устанавливает строгих ограничений на количество принятой информации. Т.е. Read читает не больше n байтов из потока (где n - указанное число). Эта функция возвращает количество полученных байтов данных.} MySocket.Write(c, 1); {Оператор Write аналогичен оператору Read, за тем лишь исключением, что Write пишет данные в поток.} MySocket.Free; {Не забудем освободить память, выделенную под поток} end; ПРИМЕЧАНИЕ: Для использования потока не забудьте установить свойство ClientType в ctBlocking. Посылка и прием сложных данных Иногда необходимо пересылать по сети не только простые текстовые сообщения, но и сложные структуры (тип record в Паскале), или даже файлы. И тогда Вам необходимо использовать специальные операторы. Некоторые из них перечислены ниже: Методы TClientSocket.Socket (TCustomWinSocket, TClientWinSocket):
Всем перечисленным методам соответствуют методы Receive... Их описание можно посмотреть в справочном файле по Дельфи (VCL help). Авторизация на сервере Напоследок хочу привести несложный пример того, как можно реализовать авторизацию (вход на сервер). В данном примере пароль посылается нешифрованным текстом, так что если Вам нужен действительно надежный механизм входа, то Вам придется внести кое-какие изменения в исходник данного примера. Пример реализован как работа с сокетным потоком. Пример 4. Авторизация {В данном примере нужно добавить в форму еще два TEdit - Edit3 и Edit4 для ввода логина и пароля} procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var c: Char; MySocket: TWinSocketStream; login, password: string; begin MySocket := TWinSocketStream.Create(Socket, 60000); {Добавляем к логину и паролю символ перевода строки, чтобы сервер смог отделить логин и пароль.} login := Edit3.Text + #10; password := Edit4.Text + #10; MySocket.Write(login, Length(Edit3.Text) + 1); MySocket.Write(password, Length(Edit4.Text) + 1); while not MySocket.WaitForData(100) do Application.ProcessMessages; MySocket.Read(c, 1); {Здесь сервер посылает нам один байт, значение 1 которого соответствует подтверждению успешной авторизации, а 0 - ошибку (это лишь пример). Далее мы выполняем нужные действия (прием/пересылку данных) и закрываем поток.} MySocket.Free; end; Эпилог В этой статье я написал лишь самую малость из того, что можно было бы сказать про сокеты. Может быть, когда у меня появится новый прилив сил, я дополню эту статью еще более интересным материалом. В ближайшем будущем планирую также статью про сокетные серверы (TServerSocket). |
  |
![]() |
![]() |