Разработка приложений Winsock
Элемент Winsock упрощает работу с протоколами TCP (Transmission Control Protocol) и UDP (User Datagram Protocol). Вы можете использовать оба протокола в своих приложениях для разработки профессиональных коммуникационных программ и утилит. Для TCP необходимо существование сеанса, тогда как протокол UDP не ориентирован на соединение.
Сеансом (session) называется изолированный логический канал между двумя приложениями, по которому осуществляется их взаимодействие. Сеанс можно рассматривать как аналог телефонного звонка. Чтобы два приложения могли взаимодействовать друг с другом, приложение-клиент передает приложению-серверу запрос на соединение (подобно тому, как вы набираете телефонный номер своего друга). Приложение-сервер оповещается о поступившем запросе (раздается телефонный звонок) и решает, будет оно отвечать или нет. Если запрос принят, создается сеанс, и между приложениями устанавливается двухсторонняя связь. Взаимодействие продолжается до тех пор, пока одна из сторон не прервет сеанс (не повесит трубку).
Протокол UDP не ориентирован на соединение. По принципу действия он напоминает двухстороннюю радиосвязь. Вместо того чтобы создавать конкретный сеанс, приложения узнают о существовании друг друга. В UDP для этого указывается имя хоста или IP-адрес и номер порта другого приложения. После того как «заочное знакомство» состоится, приложения могут общаться друг с другом. В этом уроке мы воспользуемся протоколом TCP для создания приложения чат-комнаты, которое будет называться WebComm. С помощью этого приложения в интрасети или в Интернете можно организовать переговоры, совместное обсуждение проектов или просто дружескую беседу. Впрочем, для какой бы цели вы ни использовали приложение, описанный далее процесс демонстрирует подробности организации связи на основе протокола TCP/IP.
Начнем с построения сервера чат-комнаты, использующего элемент Winsock. Следующие действия закладывают фундамент работы приложения WebComm;
1. Создайте новый проект командой File > New Project. В диалоговом окне New Project выберите значок Standard EXE и нажмите кнопку ОК.
2. Задайте свойству Name проекта значение WebCommServer.
3. Задайте свойству Name формы Form1 значение frmMain, а свойству Caption — значение Сервер WebComm.
4. Дважды щелкните в строке Icon в окне свойств. Выберите файл W95mbx04.bmp из каталога \Common\Graphics\Icons\Computer. Нажмите кнопку Open, чтобы поместить значок на форму.
5. Запустите редактор меню командой Tools > Menu Editor.
6. Создайте меню верхнего уровня. Задайте его свойству Name значение mnuFile, а свойству Caption — значение &File.
7. Создайте в меню mnuFile команду с именем mnuFileExit. Задайте свойству Caption значение E&xit.
8. Закройте редактор меню кнопкой ОК.
9. Создайте на форме список с именем lstMessages.
10. Поместите на форму таймер, задайте его свойству Name значение tmrTimer, a свойству Interval — значение 1000.
11. Щелкните правой кнопкой мыши на панели элементов и выполните команду Components из контекстного меню.
12. Установите флажки рядом со строками Microsoft Windows Common Controls 6.0 и Microsoft Winsock Control 6.0. Закройте диалоговое окно кнопкой ОК. На панели появляются новые элементы.
13. Поместите на форму элемент Winsock. Для простоты задайте его свойству Name значение wsk, а свойству Index — значение 0. Позднее мы воспользуемся им для создания массива элементов. Не беспокойтесь об остальных свойствах, они будут заданы во время выполнения программы.
14. Поместите на форму frmMain строку состояния. Задайте ее свойству Name значение stsStatus и проследите за тем, чтобы свойство Align имело значение 2 -vbAlignBottom.
15. Дважды щелкните в строке (Custom) окна свойств, чтобы открыть диалоговое окно со страницами свойств строки состояния.
16. В открывшемся окне перейдите на вкладку Panels.
17. Введите в поле Text строку Сеансы: 0.
18. Нажмите кнопку Insert Panels, чтобы добавить в строку еще одну панель. Когда панель будет добавлена, введите в поле Index значение 2 — дальнейшие изменения будут относиться ко второй панели.
19. Выберите из списка Bevel строку 0 - sbrNobevel, а из списка AutoSize — строку 1 - sbrSpring.
20. Снова нажмите кнопку Insert Panel. Когда появится следующая панель, введите в поле Index значение 3 (работа с третьей панелью). Выберите из списка Style строку 6 - sbrDate.
21. Добавьте еще одну панель и введите в поле Index значение 4 (работа с четвертой панелью). Выберите из списка Style строку 5 - sbrTime.
22. После добавления всех панелей закройте диалоговое окно кнопкой ОК. Примерный вид формы показан на рис. 18.7.
Рис. 18.7. Сервер WebComm в режиме конструирования
23. Сохраните проект, чтобы не потерять внесенные изменения.
Перед нами лежат фрагменты головоломки. Давайте соберем их воедино и добавим в приложение необходимый код:
1. Дважды щелкните на форме frmMain, чтобы открыть окно программы.
2. Вставьте следующий фрагмент в секцию (General)(Declarations) формы:
Option Explicit
Private Const WSK_LOCAL_PORT = 12345 Private Const MAX_SESSIONS = 11
Первая константа сообщает серверу о том, что он должен использовать локальный порт с номером 12345. К этому порту подключаются клиенты WebComm, желающие вступить в разговор. Вторая константа определяет максимальное количество одновременно открытых сеансов — 11. На самом деле это число на 1 превышает количество пользователей, которые могут одновременно общаться друг с другом, -первый сеанс используется сервером для прослушивания запросов на подключение. При получении запроса сервер WebComm перенаправляет сеанс на один из 10 элементов Winsock и продолжает ждать поступления следующего запроса.
3. Каждый раз, когда на сервере что-нибудь происходит (например, подключается или отключается пользователь), обновляется строка состояния формы. Для этой цели мы используем процедуру UpdateStatus. Вставьте следующий фрагмент в секцию (General) (Declarations) формы frmMain:
Private Sub UpdateStatusO Dim i As Integer Dim actsess As Integer
' Подсчитать количество активных сеансов For i = 1 То MAX_SESSIONS With wsk(i)
If .State = sckConnected Then
Увеличить счетчик сеансов actsess = actsess + 1 End If End With Next
' Обновить строку состояния With stsStatus
.Panels(1),Text = "Сеансы: " & _
Trim$(Str$(actsess)) End With End Sub
Процедура UpdateStatus проверяет текущее состояние всех элементов Winsock в массиве. Если элемент в настоящий момент подключен к клиенту, она увеличивает переменную-счетчик actsess. После завершения цикла значение actsess отображается в первой панели строки состояния.
4. Следующий фрагмент создает массив элементов Winsock и настраивает протокол для каждого элемента. Вставьте следующий фрагмент в процедуру события Form_Load:
Private Sub Form_Load() Dim i As Integer
' Задать параметры получателя Winsock With wsk(0)
.Protocol = sckTCPProtocol
.LocalPort = .WSK_LOCAL_PORT
.Listen End With
' Загрузить еще 10 элементов Winsock For i = 1 To MAX.SESSIONS
Load wsk(i)
wsk(i).Protocol = sckTCPProtocol
wsk(i).LocalPort = WSK_LOCAL_PORT Next
Обновить строку состояния UpdateStatus End Sub
5. Вставьте следующий фрагмент в процедуру события Form_Resize:
Private Sub Form_Resize()
Растянуть список сообщений If WindowState <> vbMinimized Then IstMessages.Move 0, 0,
ScaleWidth, _
(ScaleHeight - stsStatus.Height) End If End Sub
6. Вставьте следующий фрагмент в процедуру события Click меню mnuFileExit:
Private Sub mnuFileExit_Click()
Завершить приложение Unload Me End Sub
7. Строка состояния должна обновляться каждую секунду. Вставьте следующий фрагмент в процедуру события tmrTimer_Timer():
Private Sub tmrTimer_Timer()
Обновить строку состояния UpdateStatus End Sub
Все основные функции сервера WebComm выполняются массивом элементов Winsock. Как будет видно из текста программы, мы используем многие события массива wsk.
Первое из событий, связанных с элементами Winsock, — ConnectionRequest. Следующий фрагмент отвечает за получение запроса, проверку наличия открытых сеансов Winsock и перенаправление запроса соответствующему сеансу.
8. Вставьте следующий фрагмент в процедуру события ConnectionRequest () массива элементов wsk:
Private Sub wsk_ConnectionRequest(Index As Integer, _
ByVal requestID As Long) Dim msg As String Dim i As Integer
Если это прослушивающий порт... If Index = 0 Then
Ищем открытый сеанс For i = 1 То MAX_SESSIONS With wsk(i)
If .State = sckClosed Then ' Подключиться к сеансу i .Accept requestID
Выйти из цикла Exit For End If End With Next End If End Sub
Параметр Index определяет, какой из элементов массива вызвал данное событие. Поскольку нам известно, что wsk(0) занят прослушиванием, программа проверяет, можно ли перенаправить запрос на соединение другому элементу. Параметр RequestID представляет собой уникальный идентификатор клиента, запросившего соединение. Он используется для подтверждения запроса на соединение конкретным элементом Winsock. После того как элемент Winsock согласится принять сеанс, инициируется событие Connect для данного элемента.
9. Вставьте следующий фрагмент в процедуру события Connection() массива элементов wsk. В нем происходит принудительное обновление строки состояния при подключении к серверу нового пользователя:
Private Sub wsk_Connect(Index As Integer)
' Обновить строку состояния
UpdateStatus End Sub
Вся основная работа происходит в следующем фрагменте. Он получает сообщение от клиента и перенаправляет его всем активным сеансам. Таким образом, приложение наделяется функциональными возможностями чат-комнаты. Когда от сеанса поступают данные, они заносятся в буфер и инициируется событие DataArrival(), которое сообщает элементу Winsock о наличии данных, подлежащих обработке. После этого следует написать обработчик для получения данных методом GetData.
10. Вставьте следующий фрагмент в процедуру события wsk_DataArrival:
Private Sub wsk_DataArrival(Index As Integer, _
ByVal bytesTotal As Long) Dim msg As String Dim rc As Integer Dim i As Integer
Получить сообщение wsk(Index).GetData msg, , bytesTotal
Занести в список IstMessages.AddItem msg
Передать сообщение остальным клиентам For i = 1 To MAX_SESSIONS With wsk(i)
If .State = sckConnected Then
Передать сообщение текущему клиенту .SendData msg
'Подождать, пока сообщение будет доставлено DoEvents End With Next End Sub
Этот фрагмент работает весьма прямолинейно. Строка
wsk(Index).GetData msg, , bytesTotal
извлекает из буфера данные, предназначенные для элемента wsk(Index). Используя метод GetData, она извлекает количество байт данных, не превышающее bytesTotal, и помещает их в строку msg. После извлечения данных из буфера сообщение заносится в список — администратор увидит, что говорят пользователи. При желании можно добавить код, который будет сохранять каждое сообщение в ASCII-файле или базе данных с помощью ADO. После того как сообщение будет зарегистрировано, сервер передаст его всем активным сеансам (получается своего рода широковещательная рассылка).
11. Последнее событие, происходящее во время сеанса, — событие Close(). При отключении пользователя мы просто обновляем строку состояния. Вставьте следующий фрагмент в процедуру события Close:
Private Sub wsk_Close(Index As Integer)
Обновить строку состояния UpdateStatus End Sub
12. Сохраните проект.
Запустите приложение клавишей F5 — необходимо убедиться в том, что оно работает без ошибок. Если приложение запускается успешно, откомпилируйте проект в ЕХЕ-файл (команда File > Make WebCommServer.exe в меню Visual Basic). EXE-файл понадобится нам в следующем разделе.
ПРИМЕЧАНИЕО компиляции приложений и построении выполняемых файлов (ЕХЕ-файлов) рассказано в уроке 14.
Хотя наше приложение запускается и в локальном режиме, оно выглядит более эффектно, если запустить его на отдельном компьютере и наблюдать за поступлением и регистрацией сообщений в списке. С точки зрения клиента, происхо-
дит нечто замечательное — данные пересылаются по сети на другой компьютер и обратно. Но давайте продолжим работу над нашим примером. Когда он будет готов, вы сможете запустить серверный и клиентский компоненты WebComm и начать общение!