Архив метки: Windows Sockets

Фэйлом кончаются от 30% до 50% попыток внедрить штатную интеграцию сайта с 1С. Это коллеги рассказали, у меня-то в бизнес-плане заложено 75%. То есть, в трех случаях из четырех - придется что-то подкручивать напильником, а в одном - вообще вызывать эвакуатор или реанимацию. И чего бы это, ведь…

… Топовые производители современных отечественных систем управления в один голос заявляют, что умеют интегрироваться с 1С. Естественно, это касается по большей части типовых конфигураций - всего не предусмотришь, ага. Да и маркетинг заставляет говорить, что «это просто!». Слоган, который, наверное, никогда не умрет.

Рассмотрим процесс интеграции с точки зрения клиент-исполнитель. Сценарий продажи может превратиться в сущий адъ из-за пары неловких движений менеджера.

Так что знакомимся с горьким опытом и делимся своим:

Фаза предпродажи:


Анализ диалога:

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

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

Как правило, это гарантирует, что штатной интеграции не хватит.

Фаза утверждения технического задания:


Анализ диалога:

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

Договоритесь с заказчиком, чтобы он организовал вам переговоры с программистом, добейтесь того, чтобы программист либо дал вам доступ к выгрузке, либо 100% согласился обеспечить выгрузку и настройки под ваши требования. Зафиксируйте договоренности письменно.

Фаза разработки:


Анализ диалога:

Практически гарантированный фэйл. Программистам нужно спроектировать структуру каталога, но если выгрузка из базы будет принципиально другой, - это нужно учесть заранее. Пожалуй, тут еще все можно было бы и спасти, но…

Фаза сдачи проекта:


Анализ ситуации:

Ну все, фэйл случился. Теперь проджект-менежер будет пытаться «рулить» удаленным программистом, который не стоит у него в подчинении, и которого он не нанимал. Все зависит от того, какое чувство собственной важности у программиста на стороне клиента… И не дай бох оно будет >9000:-)

Фаза приемки проекта:


И - дальнейшее развитие событий:

Анализ диалога:

Если давить на программиста заказчика - можно выяснить, что он либо не разобрался в спецификации, либо пробовал, но у него не получилось, либо уже нагородил какого-то своего говнокода или какой-то свой «универсальный» формат, от которого теперь не хочет отступать. Особенная жесть начинается, если ваш клиент платит своему 1С-нику - почасовую ставку, и 1С-ник утверждает, что работа с его стороны займет неделю, из-за ваших «необоснованных» требований (или уникальности «нашей базы»).

Через неделю:



Анализ диалога:

Для краткости показаны только самые сильные ходы ПМ-а. На самом деле можно протрахаться значительно дольше. Вырулить можно, но о попадании в бюджет и срок - уже речи не идет. Виноват - клиент, (в договоре формально закреплено, что выгрузка будет предоставлена в требуемом формате ), но это неважно, поскольку цель ПМ-а - запустить проект, а не доказать «виноватость».

Главное, не вестись на разговоры вроде «вы же профессионалы, должны были предусмотреть». Вы предусмотрели и решили продолжить интеграцию, имея открытый риск. А риск, увы, - сработал.

Лечится, как правило, увеличением цены на 2-3-4 человеко-дня со стороны программистов студии, и еще часов 8-16 нервных переговоров со стороны менеджера проектов и клиента. Собственно, отсюда и разница в цене - $N за штатную интеграцию, $MMM - за нештатную.

Нервные клетки не восстанавливаются.

Итого, примерно такой расклад, по основным рискам:

Риск/ситуация Последствия Противодействие
Выгрузка запрошена на этапе пресейла.
  • Клиент поищет кого-то попроще.
  • Слишком много времени уйдет на пресейл, а проект — сорвется.
  • Вынести интеграцию на отдельный этап.
  • Дать «вилку» на лучший и худший случаи.
  • Сообщить заказчику о возможных рисках.
Выгрузка не предоставлена на этапе составления ТЗ.
  • Неправильно спроектирована структура каталога.
  • Срыв сроков и бюджета.
  • Настаивать на предоставлении выгрузки.
  • Вынести интеграцию в отдельный этап.
  • Письменно сообщить заказчику о возможных рисках.
Интеграция вынесена в отдельный этап.
  • Придется переделывать всю структуру каталога.
  • Получить выгрузку до начала проектирования.
1С был модифицирован сторонним разработчиком, имеет устаревшую версию или плохо структурированный каталог.
  • Невозможность выполнить штатную интеграцию.
  • Длительные переговоры с программистом заказчика, потеря времени.
  • Настаивать на соблюдении подписанных требований.
  • Выполнить настройку выгрузки на стороне клиента своими силами.
Выполнение настроек 1С на стороне клиента своими силами.
  • Непрогнозируемая трудоемкость и возможные сложности с нетиповой конфигурацией. Риск «закопаться» в проект.
  • Риск получить в нагрузку к сопровождению сайта — бесплатные консультации по 1С или попасть на исправление каких-то глюков в 1С, которых «не было до вас».
  • Настаивать на соблюдении подписанных требований к выгрузке.
  • Поручить настройку 1С надежному третьему лицу (к которому в случае чего будут все претензии). Кандидатуру согласовать с заказчиком.
Студия настаивает на соблюдении протокола.
  • Риск разрыва отношений по причине отсутствия возможности у клиента — реализовать требования самостоятельно.
  • Затягивание сроков сдачи проекта.
  • Вынести интеграцию с 1С на отдельную фазу.
  • Выполнить настройки 1С самостоятельно.
  • Принять данные в том формате, в котором их способен предоставить клиент.
Программист на стороне заказчика — неуправляем.
  • Длительные, тяжелые переговоры.
  • Срыв сроков.
  • Организовать ежедневные трехсторонние call-ы с заказчиком, его программистом и студией. Решить проблему на более высоком уровне (эскалировать).
Студия прогнулась и согласилась изменить требования протокола под любой формат.
  • Переделка структуры каталога, трудоемкое программирование по интеграции «за бесплатно», сорванные сроки.
  • Закладывать на интеграцию — очень много денег.
  • Запомнить полученный опыт и более не попадаться.

Описание ошибки

server_addr=tcp://<имясервера>:1562 descr=Ошибка сетевего доступа к серверу (Windows Sockets — 10054(0x00002746). Удаленный хост принудительно разорвал существующее подключение.) line=1031 file=.\src\DataExchangeTcpClientImpl.cpp

Как бороться с этой проблемой

Настроить Технологический журнал и разобрать его логи.
Наиболее частыми причинами бывают падения серверной части 1С:Предприятия.
В также можно убедиться, посмотрев — ане создаются ли дампы (смотреть путь logcfg.xml, если настройка dump-ов в нем отсутствует, то в каталоге %USERPROFILE%\Local Settings\Application Data\1C\1Cv81\Dumps, например C:\Documents and Settings\<Имя пользователя>\Local Settings\Application Data\1C\1Cv81\dumps. Падения платформы чаще всего могут возникать из-за запросов с нестандартными параметрами. Дампы отсылайте в техподдержку 1С email:[email protected].
1. Чаще всего мне встречалась проблема в журнале документов в отборах запросы были похожи на этот:

SELECT ALLOWED TOP 35 R.Date_Time A1,
R.Number A2,
R.Fld9608 A3,
R.Fld9613 A4,
R.Fld9606 A5,
R.Fld9610 A6,
R.Fld9611 A7,
R.Fld9607 A8,
R.Fld9612 A9,
R.Fld9615 A10,
R.Fld9614 A11,
R.Fld9609 A12,
R.Fld9605 A13,
R.Document A14,
R.Marked A15,
R.Posted A16,CAST(R.Fld9608 AS REF(Reference9)).Description
A17,CAST(R.Fld9606 AS REF(Reference52)).Description A18,CAST(R.Fld9611
AS REF(Reference93)).Description A19, CASE WHEN R.Fld9609 REFS
Reference53 THEN CAST(R.Fld9609 AS REF(Reference53)).Description WHEN
R.Fld9609 REFS Reference150 THEN CAST(R.Fld9609 AS
REF(Reference150)).Description WHEN R.Fld9609 REFS Reference63 THEN
CAST(R.Fld9609 AS REF(Reference63)).Description WHEN R.Fld9609 REFS
Reference114 THEN CAST(R.Fld9609 AS REF(Reference114)).Description END
A20,CAST(R.Fld9605 AS REF(Reference79)).Description A21
FROM DocumentJournal9604 R WHERE
((R.Fld9605=79:b63e000bcd6ad80811da7cf12c684266)) AND
(R.Date_Time > DATETIME(2006,12,31,12,0,0) OR (R.Date_Time =
DATETIME(2006,12,31,12,0,0) AND (R.Document >=
343:b654000bcd6ad80811dba49c7aabe269)))
ORDER BY A1 ASC, A14 ASC’

2. Пример лога ТЖ, показывающее причину падений сервера при обновлении полнотекстового поиска
11:40.9690-0,EXCP,1,process=rphost,p:processName=<база данных>,t:clientID=3, t:applicationName=BackgroundJob,t:connectID=27,Usr=DefUser,DumpFile=C:\Program Files (x86)\1cv81\dumps\rphost_8.1.13.41_7d4e2366_20090609021136_10236.mdmp,Context=’
ОбщийМодуль.МодульРегламентныхЗаданий: 46: ПолнотекстовыйПоиск.ОбновитьИндекс(Ложь, Истина);’

Итоговым решением в этом примере будет отключить фоновый процес в проблемной базе. Дождаться нового релиза платформы и обновиться.
Более подробно про падения платформы смотрите в моем блоге.
3. Пример ТЖ для циклический перезапуск процессов. Для анализа этого события на компьютере сервера 1С:Предприятия необходимо включить запись в технологический журнал событий PROC (пример файла logcfg.xml).
Когда процесс выключается, будет выведено событие PROC со свойством Txt=Process become disable.
Когда процесс останавливается, будет выведено событие PROC со свойством Txt=Process terminated. Any clients finished with error. Если аварийные завершения работы пользователей совпадают по времени с выводом этого события, то причиной является принудительная остановка рабочего процесса либо администратором (через консоль кластера), либо вследствие автоматического перезапуска.
4. Убедиться, что причиной являются/не являются действия администратора в консоли

—————————-

Ниже представлен вариант решения коллегой.

Всем заинтересованным в решении проблем с падением платформы с ошибками:

10051, 10053, 10054, 10064

Как показал разбор полетов по падениям платформы, с выше указанными ошибками:

— Большинство падений вызвано именно работой фоновых заданий, как и предполагалось в топике.

— Не хваткой дискового пространства

— Наличием большого числа не завершенных транзакций в журнале 1С

— Прежде чем заниматься разбором с технологическим журналом, проанализируйте используемые в конфигурации фоновые задания и отключите те, которые не требуются Вам для работы, конфигурации (банально, анализ 14 ГБ мусора можно считать времяпрепровождением, если Вам нечем заняться… :))))

— Проанализируйте и внесите исправления в дописанные Вами фоновые задания, убедитесь в том, что они завершаются с нормальным кодом завершения (без ошибок и не закрытых транзакций)

— Внесите в алгоритмы фоновых заданий фрагменты кода, ошищающие, принудительно , память используемую в ходе их работы (Не стоит надеяться на то, что 1С при завершении особождает использованную память)

— Проанализируйте и ИСПРАВЬТЕ ПРОБЛЕМЫ ФУНКЦИОНИРОВАНИЯ типовых фоновых заданий конфигурации

— Выполните регламентные процедуры с базой данных, через пункт меню Администрирование-Тестирование и исправление, не забудьтеобязательно , выполнить сжатие базы данных

— Проанализируйте объем используемого пространства сервером SQL, вероятно что серверу банально нехватает памяти

— Проверьте политки настройки Active Directory

— И также сожмите/очистите журнал транзакций SQL вот примерно таким кодом (для SQL 2000):

Вариант 1: DBCC SHRINKFILE(pubs_log, 2)
(Если нужный размер не достигнут попробуйте вариант 2)Вариант 2: BACKUP LOG pubs WITH TRUNCATE_ONLY
DBCC SHRINKFILE(pubs_log,2)

Где pub_log — имя Вашей базы данных

Вариант 3:
sp_detach_db — отключим с данной процедурой базу, а sp_attach_db — подключим снова. Журнал транзакций при этом очистится.
(ПОдробнее можно прочесть в разделах MSDN Q256650 (для SQL 7.0) и Q272318 (для SQL 2000).)

Вариант 4: (Для 7.0)
DBCC SHRINKFILE (file_name, target_size)
DBCC SHRINKDATABASE (database_name, target_percent)
BACKUP LOG database_name WITH TRUNCATE_ONLY

Если после этих операций падения продолжаются, тогда продолжайте следовать рекомендациям:

— Пробуйте внести изменения в файлы HOSTS операционной системы (вероятнее всего будет достаточно прописать ассоцирование только в файлы на одной/двух машинах, где падения происходят наиболее часто)

— Пробуйте разнести сервера 1С предприятия и SQL, если они у Вас на одной машине.

— Или наоборот установите их на одной машине (если хватает ресурсов) Отмечаются случаи, когда именно перенос серверов на один сервер помогало (На мой взгляд очень сомнительно и больше относится именно к причине начала работы, это сжатие журналов транзакций)

— Проверьте время отклика сервера (вероятнее всего, что все будет в пределах нормы, а редкие провалы во времени обслуживания, не могут столь сильно влиять на работу сервера предприятия)

— Проверьте работу маршрутизаторов в сети (Редко, но бывает, что именно их перенастройка влияет на количество падений)

— Проверьте конфликты оборудования в сети (это к вопросу, почему желательно иметь оборудование одного поставщика в сети. Кто хочет может проверить, например, в тех. документации 3COM написано: если сетевая карта обнаруживает, что взаимодействует с аналогичной сетевой картой, то она может быть переключена в более производительный режим, засчет перехода на оптимизированный алгоритм обработки сетевых пакетов, проверено на личном опыте скачок производительности до 50%)

— Проверьте уровни сигналов у потребителей/конечных компьютеров (может быть банально, низкий уровень сигналов, постоянные повторные запросы блоков, задержка очереди на обслуживание в сети, а следовательно в конце концов получение сообщения, что конечный серевер разорвал соединение, когда количество попыток превысит время ожидания поступления сигнала. Если хотите разобраться в данном вопросе обратитесь к протоколу работы Ethernet/CSMA CD/CSMA. Количество попыток в передаче пакета по данному протоколу не бесконечно…))) Да и буфер в картах тоже не беспределен.)

— Добавьте памяти на сервера

— Переведите часть/всех пользователей в терминальный режим (Т.е. обеспечьте то, что МНОГИЕ пользователи определеяют как ТОНКОГО КЛИЕНТА 1C). В качестве такого сервера я бы рекомендовал Citrix Metaframe или Terminal Server MS

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

Они решат многие, но не все проблемы.

И счастливы Вы, если у Вас нет таких проблем, у кого они есть, тот меня поймет.

———————————

Исследуйте роли «Пользователя», если они есть в типовой конфигурации конечно, и в частности, после того как вычислите проблемный документ с помощью , нужно найти проблемную роль (кто жалуется).
Далее для роли Пользователя смотрим РЛС документа, если дополнительных настроек нет (чисто), то правой кнопкой на нем — поиск ссылок на объект, и последовательно просматриваем РЛС для роли «Пользователь» для каждого объекта.

Ошибочное принятие высокой интенсивности пользователей за атаку на протокол в некоторых случаях Windows.
>Запустить программу regedit.exe, добавь новое значение типа DWORD с именем SynAttackProtect в раздел реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\ и присвой ему значение 00000000
Имеет смысл делать для ОС Windows 2003 SP1 (http://msdn.microsoft.com/ru-ru/library/ms189083.aspx

Сервер 1С и БД на одной машине под управлением Debian Squeeze.

Решение проблемы: установка параметра ядра tcp_syncookies в значение 0.

root@machine:~# echo «net.ipv4.tcp_syncookies = 0» >> /etc/sysctl.conf && sysctl -p
(автор Вадим Ивахин)

Приветствую!

Немного лирики. В один прекрасный момент мне поступила задача связать 1С и некую этикировочную машину. Как- не известно. Вроде бы где то есть люди, которые что то знают, но где эти люди - никто не знает. И вот путем терзания поставщика удалось вытрясти мануал на английском, в котором был описан формат взаимодействия через TCP порт. И в связи с этим пришлось написать такое счастье.

Ну и от слов к делу. Для связи по TCP я решил использовать родимую мелкомягкую технологию WinSock (http://ru.wikipedia.org/wiki/Winsock). На основе нее можно написать как и серверную часть, так и клиентскую. Все действа разделяются на несколько этапов:

Общая часть

1) Идем в родимый гугль и ищем winsock.ocx

2) Регистрируем ее в системе (regsvr32)

3) Прописываем регистрационные ветки в реестре (иначе ActiveX не лицензируется). Без этого у нас не будут доступны необходимые методы.

Я думаю что проблем с этими шагами не возникнет ни у кого. Если появились затруднения - в гугле огромное количество статей по этим вопросам.

4) Создаем обработку в 1С

  1. Добавляем на форму ActiveX
    я предпочитаю это делать программно:
    ЭлементыФормы.ДобавитьActiveX("MSWinsock.Winsock","WinSock",Ложь);
  2. Форма - вставить ActiveX - Microsoft WinSock Control, version 6.0
    только при данном варианте надо убрать видимость элемента, т.к. у него отсутствует графическое отображение.

5) Определяем необходимые методы объекта

  1. Error -если произошла какая либо ошибка .
    WinSocketError(Элемент, Number, Description, Scode, Source, HelpFile, HelpContext, CancelDisplay)
    Где:

i. Number - код ошибки,

ii. Description - Описание ошибки,

iii. Scode - Еще раз код ошибки, но уже в другом типе (LONG)

iv. Source - Источник ошибки,

vi. HelpContext - контекст справки

vii. CancelDisplay - флаг отмены отображения стандартного окна об ошибке. По умолчанию значение - Истина. Окно не выводится.

  1. DataArrival - поступление данных
    WinSocketDataArrival(Элемент, bytesTotal)
    где:

i. bytesTotal - количество принятой информации в байтах

  1. Connect - успешное подключение к серверу (возникает только на клиенте!)
    WinSocketConnect(Элемент)
  2. ConnectionRequest - запрос на подключение клиента (возникает на стороне сервера)
    WinSocketConnectionRequest(Элемент, requestID)
    где:

i. requestID - Идентификатор клиента

  1. Close- закрытие сессии
    WinsockClose(Элемент)
  2. SendProgress - Возникает при прогрессе передачи данных
    WinsockSendProgress(Элемент, bytesSent, bytesRemaining)
    где:

i. bytesSent - байт послано

ii. bytesRemaining - байт осталось

  1. SendComplete - завершение отправки данных

Серверная часть

1) Запускаем сам сервер:
WinSock=ЭлементыФормы.WinSock; // Наш ActiveX
WinSock.LocalPort = Port; // Порт на котором он будет работать
WinSock.Bind(Port, "127.0.0.1"); // IP на котором будем слушать
WinSock.listen(); // Собственно сам запуск на прослушку сервера

2) В процедуре ConnectionRequest указываем:
Если WinSocket.State <> 0 Тогда // Если у нас есть активный сокет, то перед принятием нового текущий необходимо закрыть
WinSocket.Close(); // закрывает сокет
КонецЕсли;
WinSocket.Accept(requestID); // Принимаем новый запрос

3) В процедура DataArrival:
ТекстСообщения = "";
WinSocket.GetData(ТекстСообщения); // Принимаем сообщение с сервера
WinSocket.SendData("Otvet server "); // Некий ответ сервера на получение информации

Вот и все. Простейший сервер готов. Можно стучать на него по телнету и тестировать. Сразу оговорюсь, что настройки кодовой страницы (отображение кириллицы) зависит именно от используемого клиента. 1С все отсылает в Cp1251.

Клиентская часть

1) Инициализируем подключение:
WinSocket.RemoteHost = СокрЛП(IP); // Адрес, на который соединяемя
WinSocket.RemotePort = СокрЛП(Port); // Порт, по которому соединяемя
WinSocket.Connect(); // Команда на соединение

Внимание! Статус в этой же процедуре WinSocket не изменит! Поэтому:

2) В процедуре Connect:
Сообщить(WinSocket.State) // Тут мы получаем текущий статус
Таблица статусов:

Состояние

Числовое значение

Описание

Default. Closed
Значение по умолчанию. Подключение закрыто.

Open
Подключение активно. Соединение установлено.

Listening
Режим "прослушки". Компонента ждет подключение по указанному порту.

sckConnectionPending

Connection pending
Ожидание подключения

sckResolvingHost

Resolving host
Получение адреса компьютера (хоста) по имени.

Host resolved
Адрес компьютера получен.

Connecting
Подключение

Connected
Подключен

Peer is closing the connection
Клиент закрыл подключение

Error
Ошибка

3) Процедура отправки данных:
Если WinSocket1.State = 7 тогда //Отправляем данные только при статусе «Подключен»
WinSocket1.SendData(СокрЛП(ТекстСообщения));
КонецЕсли;

Естественно я не первопроходец в данном разделе, но надеюсь что моя статейка кому то поможет.

Winsock для всех (часть 1)

И так, что же такое Winsock и с чем его едят? Если сказать в "двух словах", то Winsock это интерфейс, который упрощает разработку сетевых приложений под Windows. Всё что нам нужно знать, это то что Winsock представляет собою интерфейс между приложением и транспортным протоколом, выполняющим передачу данных.

Не будем вдаваться в детали внутренней архитектуры, ведь нас интересует не то, как он устроен внутри, а то, как использовать функции, предоставляемые Winsock пользователю для работы. Наша задача - на конкретных примерах разобраться с механизмом действия WinsockAPI. "Для чего это можно использовать? Ведь существуют библиотеки, упрощающие работу с сетями и имеющие простой интерфейс?" - спросите вы. Я отчасти согласен с этим утверждением, но по-моему полностью универсальных библиотек, ориентированных под все задачи существовать не может. Да и к тому же, намного приятней разобраться во всём самому, не чувствуя неловкости перед "чёрным ящиком" принципа работы которого не понимаешь, а лишь используешь как инструмент:) Весь материал рассчитан на новичков. Я думаю с его освоением не будет никаких проблем. Если вопросы всё-таки возникнут, пишите на [email protected] . Отвечу всем. Для иллюстрации примеров будем использовать фрагменты кода Microsoft VC++. Итак, приступим!

Winsock - с чего начать?

Итак, первый вопрос - если есть Winsock, то как его использовать? На деле всё не так уж и сложно. Этап первый - подключение библиотек и заголовков.

#include "winsock.h" или #include "winsock2.h" - в зависимости от того, какую версию Winsock вы будете использовать
Так же в проект должны быть включены все соответствующие lib-файлы (Ws2_32.lib или Wsock32.lib)

Шаг 2 - инициализация.

Теперь мы можем спокойно использовать функции WinsockAPI. (полный список функций можно найти в соответствующих разделах MSDN).

Для инициализации Winsock вызываем функцию WSAStartup

int WSAStartup(WORD wVersionRequested, (in) LPWSADATA lpWSAData (out));


Параметр WORD wVersionRequested - младший байт - версия, старший байт - под.версия, интерфейса Winsock. Возможные версии - 1.0, 1.1, 2.0, 2.2... Для "сборки" этого параметра используем макрос MAKEWORD. Например: MAKEWORD (1, 1) - версия 1.1. Более поздние версии отличаются наличием новых функций и механизмов расширений. Параметр lpWSAData - указатель на структуру WSADATA. При возврате из функции данная структура содержит информацию о проинициализированной нами версии WinsockAPI. В принципе, ёё можно игнорировать, но если кому-то будет интересно что же там внутри - не поленитесь, откройте документацию;)

Так это выглядит на практике:

WSADATA ws;
//...
if (FAILED (WSAStartup (MAKEWORD(1, 1), &ws)))
{
// Error...
error = WSAGetLastError();
//...
}


В таком случае можно получить расширенную информацию об ошибке используя вызов WSAGetLastError(). Данная функция возвращает код ошибки (тип int)

Шаг 3 - создание сокета.

Итак, мы можем приступить к следующему этапу - создания основного средства коммуникации в Winsock- сокета (socket). С точки зрения WinsockAPI сокет - это дескриптор, который может получать или отправлять данные. На практике всё выглядит так: мы создаём сокет с определёнными свойствами и используем его для подключения, приёма/передачи данных и т.п. А теперь сделаем небольшое отступление... Итак, создавая сокет мы должны указать его параметры: сокет использует TCP/IP протокол или IPX (если TCP/IP, то какой тип и т.д.). Так как следующие разделы данной статьи будут ориентированы на TCP/IP протокол, то остановимся на особенностях сокетов использующих этот протокол. Мы можем создать два основных типа сокетов работающих по TCP/IP протоколу - SOCK_STREAM и SOCK_DGRAM (RAW socket пока оставим в покое:)). Разница в том, что для первого типа сокетов (их еще называют TCP или connection-based socket), для отправки данных сокет должен постоянно поддерживать соединение с адресатом, при этом доставка пакета адресату гарантирована. Во втором случае наличие постоянного соединения не нужно, но информацию о том, дошел ли пакет, или нет - получить невозможно (так называемые UDP или connectionless sockets). И первый и второй типы сокетов имеют своё практическое применение. Начнём наше знакомство с сокетами с TCP (connection-based) сокетов.

Для начала объявим его:

Создать сокет можно с помощью функции socket

SOCKET socket (int af (in), // протокол (TCP/IP, IPX...)
int type (in), // тип сокета (SOCK_STREAM/SOCK_DGRAM)
int protocol (in) // для Windows приложений может быть 0
);


Пример:

if (INVALID_SOCKET == (s = socket (AF_INET, SOCK_STREAM, 0)))
{
// Error...
error = WSAGetLastError();
// ...
}


При ошибке функция возвращает INVALID_SOCKET. В таком случае можно получить расширенную информацию об ошибке используя вызов WSAGetLastError().

Шаг 4 -устанавливаем соединение.

В предыдущем примере мы создали сокет. Что же теперь с ним делать? :) Теперь мы можем использовать этот сокет для обмена данными с другими клиентами winsock-клиентами и не только. Для того, что бы установить соединение с другой машиной необходимо знать ее IP адрес и порт. Удалённая машина должна "слушать" этот порт на предмет входящих соединений (т.е. она выступает в качестве сервера). В таком случае наше приложение это клиент.

Для установки соединения используем функцию connect.

int connect(SOCKET s, // сокет (наш сокет)
const struct sockaddr FAR *name, // адрес
int namelen // длинна адреса
);


Пример:

// Объявим переменную для хранения адреса
sockaddr_in s_addr;

// Заполним ее:
ZeorMemory (&s_addr, sizeof (s_addr));
// тип адреса (TCP/IP)
s_addr.sin_family = AF_INET;
//адрес сервера. Т.к. TCP/IP представляет адреса в числовом виде, то для перевода
// адреса используем функцию inet_addr.
s_addr.sin_addr.S_un.S_addr = inet_addr ("193.108.128.226");
// Порт. Используем функцию htons для перевода номера порта из обычного в //TCP/IP представление.
s_addr.sin_port = htons (1234);


При ошибке функция возвращает SOCKET_ERROR.
Теперь сокет s связан с удаленной машиной и может посылать/принимать данные только с нее.

Шаг 5 - посылаем данные.

Для того что бы послать данные используем функцию send

int send(SOCKET s, // сокет- отправитель
const char FAR *buf, // указатель на буффер с данными
int len, // длинна данных
);


Пример использования данной функции:

if (SOCKET_ERROR == (send (s, (char*) & buff), 512, 0))
{
// Error...
error = WSAGetLastError();
// ...
}


При ошибке функция возвращает SOCKET_ERROR.
Длинна пакета данных ограничена самим протоколом. Как узнать максимальную длину пакета данных мы рассмотрим в следующий раз. Возврат из функции не происходит до тех пор, пока данные не будут отправлены.

Шаг 6 -принимаем данные.

Принять данные от машины с которой мы предварительно установили соединение позволяет функция recv.

int recv(SOCKET s, // сокет- получатель
char FAR *buf, // адрес буфера для приёма данных
int len, // длинна буфера для приёма данных
int flags // флаги (может быть 0)
);


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

int actual_len = 0;

If (SOCKET_ERROR == (actual_len = recv (s, (char*) & buff), max_packet_size, 0))
{
// Error...
error = WSAGetLastError();
// ...
}


Если данные получены, то функция возвращает размер полученного пакета данных (а примере - actual_len) При ошибке функция возвращает SOCKET_ERROR. Заметьте, что функции send/recv будут ждать пока не выйдет тайм-аут или не отправится/придет пакет данных. Это соответственно вызывает задержку в работе программы. Как этого избежать читайте в следующих выпусках.

Шаг 6 -закрываем соединение.

Процедура закрытия активного соединения происходит с помощью функций shutdown и closesocket. Различают два типа закрытия соединений: abortive и graceful. Первый вид - это экстренное закрытие сокета (closesocket). В таком случае соединение разрывается моментально. Вызов closesocket имеет мгновенный еффект. После вызова closesocket сокет уже недоступен. Как закрыть сокет с помощью shutdown/closesocket читайте в следующих выпусках, так как эта тема требует более полного знания Winsock.

int shutdown(SOCKET s, // Закрываемый сокет
int how // Способ закрытия
);


int closesocket(SOCKET s // Закрываемый сокет
);


Пример:

closesocket (s);

Как видите, рассмотренный нами Winsock-механизм обмена данными очень прост. От программиста требуется лишь выработать свой "протокол" общения удалённых машин и реализовать его с помощью данных функций. Конечно, рассмотренные нами примеры не отражают всех возможностей Winsock. В наших статьях мы постараемся рассмотреть наиболее важные на наш взгляд особенности работы с Winsock. Stay tuned. :)
В следующем выпуске читайте:

  • Пишем простейшее winsock приложение.
  • UDP сокеты - приём/доставка негарантированных пакетов
  • Решаем проблему "блокировки" сокетов.