Работаем 3484 день              Каталог программиста. Сайты Статьи Исходники Программы Скрипты Новости
Карта сайтаКарта сайта Форум DELPHI Basic Аsm Базы WebMaster Windows Железо Программы Культура Игры
Все разделы каталогаКаталог Новости C++# PHP FoxPro Безопасн. JavaScript Linux КПК Моб Документы Юмор Хостинг
RSS канал "Каталог программиста" канал Компьютерная жизньКЖизнь WinApi Java Сети Форматы Программеру Иные_ОС Каталоги Алгоритмы Форумы Разные
 
Архив сайта за 2001-2007 годыАрхив—> Архив сайта за 2001-2007 годы DELPHIDelphi Архив сайта за 2001-2007 годы C++#C++# Архив сайта за 2001-2007 годы PHPPHP Архив сайта за 2001-2007 годы СетиСети Архив сайта за 2001-2007 годы БазыБазы Архив сайта за 2001-2007 годы WebMasterWebMaster Архив сайта за 2001-2007 годы WindowsWindows Архив сайта за 2001-2007 годы ЖелезоЖелезо Архив сайта за 2001-2007 годы АлгоритмыАлгоритмы Архив сайта за 2001-2007 годы ЮморЮмор Архив сайта за 2001-2007 годы РазныеРазные
Поиск по сайтуПоиск по сайту: 
Сайт Google
В каталоге: 
В архиве: 
Новые ссылки
  • PHP скрипт скачивания файлов по временным ссылкам (7) Временные ссылки на php. Что это такое и с чем их едят? Все очень просто. Часто при построении каког
  • Анекдоты от 29.07.2010 (28) Кофе на работе - это напиток, который пьют, когда хотят есть.
  • Програмная эмуляция нажатия клавиш (29) Процедура для Delphi на ASM
  • Диагностика сети и мониторинг в Windows 7 (36) В помощь пользователям, столкнувшимся с проблемами работы сети, Microsoft еще в Windows Vista включи
  • Перетаскивание объектов (33) Свойства DragMode, DragCursor, методы BeginDrag, OnDragOver, OnDragDrop, OnEndDrag, OnStartDrag, пар
  • Как программно нажать на кнопку мыши? (57) Пример на Delphi
  • Cкрипт статистики поисковых запросов (50) В данной статье представлен PHP скрипт на базе которого легко можно будет создать модуль статистки п
  • Анекдоты от 22.07.2010 (174) Мальчик, которого на пасеке укусила коза, перестал верить в логику.
  • Настройка Windows 7: классическое меню Программы в стиле XP (135) Некоторые пользователи Windows 7 не довольны новым меню "Пуск" и хотели бы вернуться к его
  • В чём секрет защищённости Internet Explorer 8 (101) Браузер Internet Explorer в течение долгого времени имел дурную славу одного из самых уязвимых. С вы
  • Советы и рекомендации по защите Windows 7 (85) Есть несколько очевидных действий по защите компьютера: регулярно устанавливайте свежие исправления
  • Безопасная работа в Интернет: настройки Internet Explorer. Часть 3 (70) Веб-браузер Internet Explorer позволяет вам защитить свои личные данные в то время, пока вы находите
  • KDE SC 4.4.0 (117) KDE Software Compilation - коллекция программного обеспечения, портированная для Windows.
  • Вставка текста в StringGrid из Excel (122) Пример кода. Данные в StringGrid, заранее скопировав ячейки из экселя, методом Ctrl+C - Ctrl+V
  • Изменяем иконки Библиотек Windows 7 (120) Новые Библиотеки довольно удобны, однако вы не сможете найти простого встроенного способа изменения
  • Безопасная работа в Интернет: настройки Internet Explorer. Часть 3 (100) Internet Explorer позволяет вам защитить свои личные данные в то время, пока вы находитесь как оффла
  • Неактивная ячейка в StringGrid (88) Пример кода
  • Рейтинг@Mail.ru Истинное количество посещений сайта сегодня

    Архив > Delphi/Pascal > DELPHI FAQ (по темам) > Сети.Интернет.HTML.Почта

    [ Поиск в архиве ] [ Предыдущая страница ] [ Каталог ]

    Создание сетевых приложений на Delphi с использованием Windows Sockets API

    Сегодня уже никому не надо рассказывать, что такое компьютерные сети, - сети прочно вошли в нашу жизнь. Сейчас многие программисты занимаются разработкой приложений, которые физически расположены на разных компьютерах и взаимодействуют друг с другом посредством сетей.
    В delphi существуют встроенные классы для работы с сетью - это компоненты delphi 6 на закладке internet (tserversocket и tСlientcocket) и компоненты fastnet, либо компоненты indy в delphi 7. Но в этой статье рассматривается программирование сетевых приложений на низком уровне - с использованием winsock api.

    Зачем это нужно, если существует большой набор встроенных компонентов и большое количество бесплатно распространяемых классов?

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

    2. Программирование на более низком уровне традиционно требует больших затрат, но предоставляет более полный контроль за работой приложения.

    3. И, наконец, знание основ работы windows socket необходимо всем желающим разрабатывать сетевые приложения и будет являться хорошей практикой для более полного понимания всех происходящих процессов.

    Для поддержки сетевых приложений существует технология, названная "сокеты". Сокет - это модель одного конца соединения, со всеми присущими ему свойствами и методами. По сути, это прикладной программный интерфейс, входящий в состав многих операционных систем (ОС) и призванный для поддержки сетевых возможностей ОС. В стандарте структуры протоколов семиуровневой модели osi-сокеты лежат на так называемом транспортном уровне, ниже находится сетевой протокол ip, а выше - протоколы сеансового уровня, такие как ftp, pop3, smtp и т.д.

    В windows поддержка сокетов включена начиная с версии 3.11 и названа winsock. Для написания приложений с сетевой поддержкой существует специальный winsock api.

    Сокеты могут базироваться на tcp/ip, ipx/spx но в дальнейшем мы будем говорить только про соединения tcp/ip.

    Все сетевые приложения построены на технологии клиент-сервер; это значит, что в сети существует по крайней мере одно приложение, являющее сервером, типичная задача которого - это ожидание запроса на подключение от приложений-клиентов, которых может быть теоретически сколько угодно, и выполнение всевозможных процедур в ответ на запросы клиентов. Для клиент-серверной технологии абсолютно неважно, где расположены клиент и сервер - на одной машине или на разных. Конечно, для успешного соединения клиента с сервером клиенту необходимо иметь минимальный набор данных о расположении сервера - для сетей tcp/ip это ip-адрес компьютера, где расположен сервер, и адрес порта, на котором сервер ожидает запросы от клиентов.

    Каждый из компьютеров в сети tcp/ip имеет свой уникальный ip-адрес, который используется для обмена данными с другими компьютерами. Каждый посылаемый пакет от одного компьютера другому имеет адрес отправителя и получателя, что позволяет его однозначно идентифицировать. Однако в случае, если на компьютере работает множество приложений, одновременно использующих сеть, то такого набора атрибутов явно недостаточно.

    Для разрешения неоднозначности кроме адреса каждое соединение на каждом конце имеет идентификатор под названием "порт", этот идентификатор представляет число от 0 до 65535. Таким образом, пара адрес+порт представляет собой сокет-канал, по которому два компьютера обмениваются данными друг с другом. Только одно приложение на одном компьютере в одно и то же время может использовать конкретный порт, однако для серверных частей возможно создание нескольких сокетов на одном порту для работы с несколькими клиентами.

    Значение порта не обязательно должно совпадать на сервере и клиенте - клиенту для соединения важно только знать порт сервера, порт клиента может выбираться клиентом произвольно и становится известен серверу в момент запроса клиента на соединение. Когда соединение будет установлено, ОС создаст для серверного приложения соответствующий сокет, с которым и будет работать приложение, так что порт клиента для сервера совершенно не важен.

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

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

    Давайте теперь рассмотрим минимальный набор функций из winsock api, необходимых для написания элементарного клиента и сервера. Сами функции находятся в файле winsock.dll. Файл winsock.pas содержит необходимые объявления импортируемых функций winsock api и базовые структуры данных. К сожалению, этот файл импортирует не все необходимые нам функции, и позже мы напишем свой файл импорта.

    function wsastartup(wversionrequired: word; var wsdata: twsadata): integer; stdcall;

    Функция сообщает ОС, что в любом процессе приложения могут быть использованы функции winsock. Функция должна быть вызвана один раз при запуске приложения перед использованием любой функции winsock.

    function wsacleanup: integer; stdcall;

    Функция сообщает ОС, что приложение более не использует winsock. Должна быть вызвана перед завершением приложения.

    function socket(af, struct, protocol: integer): tsocket; stdcall;

    Функция создает сокет. Порт и адрес задается в функции bind (сервер) или connect (клиент). Входящий параметр af - спецификация семейства сокетов (af_inet, af_ipx и др.), struct - спецификация типа нового сокета (принимает значение sock_stream или sock_dgram), protocol - специфический протокол, который будет использоваться сокетом. Если функция выполнена без ошибок, она возвращает дескриптор на новый сокет, если ошибки есть, возвращается invalid_socket.

    function connect(s: tsocket; var name: tsockaddr; namelen: integer): integer; stdcall;

    Функция соединения для клиента. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для клиента необходимо привести из имени или спецификации ip4 - xxx.xxx.xxx.xxx).

    function bind(s: tsocket; var addr: tsockaddr; namelen: integer): integer; stdcall;

    Функция ассоциирует адрес с сокетом. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для сервера обычно указывается inaddr_any - любой).

    function send(s: tsocket; var buf; len, flags: integer): integer; stdcall;

    Функция отправки данных. Помещает в очередь сокета s кусок данных из buf, длиной len. Последний параметр отвечает за вид передачи сообщения. Может быть проигнорирован (0).

    function recv(s: tsocket; var buf; len, flags: integer): integer; stdcall;

    Функция получение данных.

    Итак, рассмотрим примеры элементарного сервера и клиента. Договоримся, что сервер будет работать в асинхронном (блокирующем режиме). Единственная функциональность сервера - это получение строк данных от клиента и вывод их на экран. Связь клиента с сервером разрывается после получения строки, состоящей из единственного символа 'q'.

    Для обеспечения возможности подключения к серверу множества клиентов сервер на каждое соединение запускает отдельный поток.

    program winsock_server;
    //Простейшее приложение-сервер.
    //Сокеты работают в блокирующем режиме.
    //На каждое соединение создается отдельный поток.
    {$apptype console}
    uses
    sysutils,
    winsock,
    windows;
    var
    vwsadata : twsadata;
    vlistensocket,vsocket : tsocket;
    vsockaddr : tsockaddr;
    trid : thandle;
    const
    cport = word(33);
    csigexit = 'q';
    //Процедура отдельного потока для каждого клиента.
    procedure socketthread;
    var sockname : tsockaddr;
    abuf : array of char;
    vbuf : string;
    vsize : integer;
    s :tsocket;
    bufsize : integer;
    begin
    s := vsocket;
    if s = invalid_socket then exit;
    vsize := sizeof(tsockaddr);
    getpeername(s, sockname, vsize);
    writeln(format('client accepted, remote address [%s].',[inet_ntoa (sockname.sin_addr)]));
    //Определяем размер буфера чтения для сокета
    vsize := sizeof(bufsize);
    getsockopt(s,sol_socket,so_rcvbuf,pchar(@
    bufsize),vsize);
    writeln(format('receive buffer size [%d]',[bufsize]));
    setlength(abuf,bufsize);
    repeat
    //Получаем данные. Процедура работает в блокирующем режиме,
    //таким образом следующая строка кода не получит управление,
    //пока не поступят данные от клиента.
    vsize := recv(s,abuf[0],bufsize,0);
    if vsize<=0 then break;
    setlength(vbuf,vsize);
    lstrcpyn(@vbuf[1],@abuf[0],vsize);
    writeln(format('received from cleint: %s',[vbuf]));
    until vbuf = 'q';
    writeln(format('client disconnected, remote address [%s].',[inet_ntoa(sockname.sin_addr)]));
    setlength(abuf,0);
    closesocket(s);
    end;

    begin
    writeln('starting application...');
    //Объявляем, что программа будет использовать windows sockets.
    if wsastartup($101,vwsadata)<>0 then halt(1);
    writeln('using windows sockets.');
    //Создаем прослушивающий сокет.
    vlistensocket := socket(af_inet,sock_stream,ipproto_ip);
    writeln(format('creating socket on port [%d].',[cport]));
    if vlistensocket = invalid_socket then halt(1);
    fillchar(vsockaddr,sizeof(tsockaddr),0);
    vsockaddr.sin_family := af_inet;
    vsockaddr.sin_port := htons(cport);
    vsockaddr.sin_addr.s_addr := inaddr_any;
    writeln('binding socket...');
    //Привязываем адрес и порт к сокету.
    if bind(vlistensocket,vsockaddr,sizeof(tsockaddr)) <> 0
    then halt(1);
    //Начинаем прослушивать.
    if listen(vlistensocket,somaxconn) <> 0
    then halt(1);
    writeln('socket status: listening.');
    repeat
    //Ожидаем подключения.
    vsocket := accept(vlistensocket,nil,nil);
    //Клиент подключился, запускаем новый процесс на соединение.
    createthread(nil,0,@socketthread,0,0,trid);
    until false;
    closesocket(vlistensocket);
    wsacleanup;
    end.

    Исходный текст снабжен подробными комментариями и, думаю, не требует никаких дальнейших пояснений. Единственное, что отмечу, - тут использовалась не описанная ранее функция getpeername(), которая возвращает информацию о канале, ассоциированном с сокетом. Я использовал ее для получения информации о ip-адресе подключившегося клиента. Подробно об этой функции, как и о всех других функциях winsock, вы можете прочитать в windows sockets 2 application program interface, входящем в состав win32 programmer's reference.

    Хочу обратить ваше внимание, что приведенный выше код можно использовать только как учебное пособие. Для того что использовать его в качестве основы для настоящего приложения, необходимо некоторое количество доработок, т.к. многие вещи, которые могут привести в будущем к серьезным ошибкам, были сознательно опущены для уменьшения размера кода и акцентирования внимания именно на аспектах использования winsock. Если вы недостаточно хорошо знакомы с понятием потоков (threads) в windows, то вам лучше использовать класс tthread, существующий в delphi специально для поддержки многопоточных приложений.

    Исходный код клиента не представляет из себя ничего интересного и не требует никаких комментариев:

    program winsock_client;
    {$apptype console}

    uses
    sysutils,
    winsock;
    const
    cport = 33;
    csigexit = 'q';
    var
    vwsadata : twsadata;
    vsocket : tsocket;
    vsockaddr : tsockaddr;
    buf : string;
    begin
    if wsastartup($101,vwsadata)<>0 then halt(1);
    vsocket := socket(af_inet,sock_stream,ipproto_ip);
    if vsocket = invalid_socket then halt(1);
    fillchar(vsockaddr,sizeof(tsockaddr),0);
    vsockaddr.sin_family := af_inet;
    vsockaddr.sin_port := htons(cport);
    vsockaddr.sin_addr.s_addr := inet_addr('127.0.0.1');
    if connect(vsocket,vsockaddr,sizeof(tsockaddr)) = socket_error then halt(1);
    repeat
    readln(buf);
    if send(vsocket,buf[1],length(buf),0) = socket_error then break;
    until buf = csigexit;
    closesocket(vsocket);
    wsacleanup;
    end.

    Итак, мы рассмотрели, как работают сокеты в асинхронном режиме, давайте посмотрим теперь, какие возможности winsock нам предоставляет для работы с неблокирующими сокетами.

    Для того чтобы перевести сокет в неблокирующий режим, используется функция ioctlsocket(...), позволяющая контролировать режимы работа сокета.

    Т.к. теперь функция recv сервера будет возвращать управление сразу, независимо от наличия данных в буфере сокета, то теперь нам нужен механизм, позволяющий определять какие-либо события, происходящие с сокетом. Для этого существует несколько механизмов. Первый, который мы рассмотрим, это использование функции select(...).

    function select(nfds: integer; readfds, writefds, exceptfds: pfdset; timeout: ptimeval): longint; stdcall;

    Эта функция позволяет контролировать состояние набора сокетов.

    Аргумент nfds игнорируется и оставлен только для совместимости. Должен быть равен 0. readfs,writefds,exceptfds - указатели на наборы сокетов, для которых нужно контролировать состояние чтения, отправки данных и ошибок соответственно. Наборы хранятся в структуре pfdset, управление которой осуществляется специальными макросами, описанными в winsock.pas:

    procedure fs_zero(var fdset: tfdset) - обнуляет структуру, устанавливает количество контролируемых сокетов в 0;

    procedure fd_set(socket: tsocket; var fdset: tfdset) - добавляет указанный сокет в структуру;

    procedure fd_clr(socket: tsocket; var fdset: tfdset) - удаляет указанный сокет из структуры;

    function fd_isset(socket: tsocket; var fdset: tfdset): boolean - возвращает true, если указанный сокет является членом указанной структуры.

    Аргумент timeout является ссылкой на структуру типа ptimeval, в которой можно указать время ожидания срабатывания функции select. В случае указания в качестве значения времени задержки 0 или nil в качестве аргумента timeout функция select будет ждать бесконечно, как при выполнении операции в блокирующем режиме.

    Как видно из описания, функция может следить сразу за несколькими сокетами, таким образом, теперь мы можем либо также запускать отдельный процесс на каждый открытый сокет, который будет следить за конкретным сокетом, либо создать один процесс, который будет следить за всеми открытыми сокетами. В первом случае нам потребуются лишь незначительные изменения процедуры socketthread, во втором случае потребуются довольно значительные изменения.

    Давайте рассмотрим логику работы во втором случае, так как первый случай банален.

    Для начала в основной части программы необходимо перевести вновь созданный сокет в неблокирующий режим:

    arg := 1;
    ioctlsocket(socket,fionbio,arg);

    Внимание, перед закрытием сокета его необходимо будет вернуть в блокирующий режим:

    arg := 0;
    ioctlsocket(socket,fionbio,arg);

    Затем необходимо реализовать возможность сохранения каждого сокета в некоторый массив sockarray. Далее, нужно обеспечить, чтобы поток, который будет заниматься обработкой клиентов, запускался только один раз. Это сделать несложно, зная, что процедура createthread возвращает в переменную ссылку на вновь созданный поток, которую и нужно использовать для проверки, был ли создан поток. В потоке обработки нужно внести следующие изменения для использования функции select:

    новые используемые переменные wfds :tfdset ; i : integer; tv : ttimeval;

    Затем определим основной цикл, в котором будем обрабатывать данные:

    repeat
    ...
    until connum>0;

    В этом цикле первым делом необходимо сформировать структуру wfds, содержащую набор контролируемых сокетов. Для этого мы переносим туда сокеты из массива sockarray:

    fd_zero(wfds);
    for i:=1 to connum do
    begin
    fd_set(sock[i],wfds);
    end;

    Далее, указываем в структуре tv время задержки для функции select:

    tv.tv_sec := 5;
    tv.tv_usec := 0;

    Теперь можно вызывать функцию select (т.к. мы следим только за приемом данных, то в качестве writefds, exceptfds мы указываем nil):

    select(0,@wfds,nil,nil,@tv);

    Теперь, когда функция select возвратит управление в переменной wfds, мы будем иметь набор сокетов, для которых необходимо произвести чтение, и можем обработать поступившие данные:

    if wfds.fd_count=0 then continue;
    for i:=0 to wfds.fd_count-1 do
    begin
    vsocket := wfds.fd_array[i];
    //Обработка поступивших данных с сокета vsocket.
    end;

    Условие выхода из основного цикла - отсутствие открытых сокетов в массиве sockarray. Условия закрытия сокета я оставляю на совести читателя, добавлю только лишь, что сокет попадет в обработку select и при наступлении события разрыва связи (для обработки можно использовать то, что количество принятых байт функцией recv будет равно нулю, а также функцию wsagetlasterror).

    Давайте теперь разберемся, для чего мы указали время ожидания функции send, а не сделали его бесконечным? Дело в том, что в текущей реализации, при создания нового сокета, он не попадет в обработку select, пока не будет установлен функцией fs_set, что, естественно, не пройдет в блокирующем режиме, пока select не возвратит управление по событию с одним из отслеживаемых клиентов. Установка значения timeout гарантирует, что сокет попадет в обработку независимо от состояния других сокетов.

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

    Помимо функции select существует еще два метода работы с асинхронными сокетами:

    function wsaasyncselect(s: tsocket; hwindow: hwnd; wmsg: u_int; levent: longint): integer; stdcall;

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

    const


    wm_mysocket = wm_user + 1;


    ...
    type
    tform1 = class(tform)
    ...
    private
    procedure socket_proc(var msg:tmessage);message wm_mysocket;
    ...
    wsaasyncselect(vsocket,form1.handle,wm_
    mysocket,fd_accept+fd_read);
    ....
    procedure tform1.socket_proc(var msg: tmessage);
    begin
    if ((msg.msg = wm_mysocket)
    and (msg.lparam = fd_accept))
    then showmessage('connected');
    end;

    Не будем задерживаться на обработке сообщений окна, а рассмотрим еще один способ, который пригодится, если у вас нет окна приложения; этот способ основан на системных событиях (events). К сожалению, файл winsock.pas не импортирует соответствующие функции, в результате чего многие программисты пренебрегают возможностями событий. Но для нас это не беда - напишем собственный импорт необходимых процедур:

    function wsaeventselect(s: tsocket; event: thandle; levent: longint):integer;stdcall;
    external 'ws2_32.dll' name 'wsaeventselect';
    function wsawaitformultipleevents(ncount: dword; lphandles: pwohandlearray;
    bwaitall: bool; dwmilliseconds: dword; falertable:bool):integer;stdcall;
    external 'ws2_32.dll' name 'wsawaitformultipleevents';
    function wsacreateevent:thandle;stdcall;
    external 'ws2_32.dll' name 'wsacreateevent';
    function wsaresetevent(event : thandle):bool;stdcall;
    external 'ws2_32.dll' name 'wsaresetevent';
    function wsaenumnetworkevents(const s : tsocket;
    const event : thandle; lpnetworkevents : lpwsanetworkevents): longint ;
    stdcall;far;
    external 'ws2_32.dll' name 'wsaenumnetworkevents';
    function wsacloseevent(event : thandle):integer;
    stdcall; external 'ws2_32.dll' name 'wsacloseevent';

    Также нам потребуется описание структуры wsanetworkevents

    const
    fd_max_events = 10;
    type
    twsanetworkevents = record
    lnetworkevents: longint;
    ierrorcode: array[0..fd_max_events-1] of integer;
    end;
    pwsanetworkevents = ^twsanetworkevents;
    lpwsanetworkevents = pwsanetworkevents;

    Принцип работы с этим набором функций состоит в создании специального объекта типа event, затем связывание этого события с сокетом с помощью функции wsaeventselect, в которой также указывается набор отслеживаемых состояний сокета. Один сокет может быть связан только с одним объектом типа event.

    Затем, в цикле обработки мы организуем ожидание поступления события от сокета; это реализуется с помощью api функций waitforsingleobject - для ожидания одного события, либо waitformultipleobjects - для ожидания набора событий. При наступлении события функция возвращает управление. Для однозначной идентификации, от какого сокета пришло уведомление, в связи с чем используется функция wsaenumnetworkevents, возвращающая структуру типа twsanetworkevents.

    var
    fevent : thandle;
    //Создаем серверный сокет
    ...
    feventclose := wsacreateevent;
    wsaeventselect(socket,fevent, fd_close + fd_read );
    repeat
    waitforsingleobject(fevent,infinite);
    wsaenumnetworkevents(fsocket,fevent,@ni);
    case ni.lnetworkevents of
    fd_close:break;
    fd_read: begin
    receivedata;
    end;
    end;
    wsaresetevent(feventclose);
    until false;
    wsacloseevent(feventclose);

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

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

    send(vsocket,@buf1,length(buf1),0);
    send(vsocket,@buf2,length(buf2),0);
    фактически будет идентичен одному вызову send с объединенным буфером buf1+buf2.

    Таким образом, при приеме данных, хотя мы послали две порции данных, мы получим одну. Совсем другой случай, когда мы посылаем порцию данных, большую, чем буфер сокета, тогда функция send отправит только часть данных из указанного буфера, ровно столько, сколько влезло в буфер сокета. Для того чтобы отследить такую ситуацию и отправить необработанную часть буфера, нужно воспользоваться тем, что функция send возвращает количество фактически отосланных данных. Функцию send нужно запускать в цикле, условием завершения которого будет полная отправка всего буфера.

    Таким образом, при чтении из сокета данных, мы можем наблюдать как бы "склейку" порций данных, либо, наоборот, фрагментацию (не путать с фрагментацией пакетов на уровне tcp/ip). Такие ситуации должна обрабатывать наша программа. Решить проблему можно добавлением сигнатуры признака конца блока данных. Это имеет смысл, если приложения часто обмениваются небольшими блоками данных, где чаще всего возникает эффект "склеивания", но неэффективно при больших объемах, так как сканирование большого буфера на предмет сигнатуры отнимает много времени. Обычно все же это решается таким способом - в начале каждого пакета добавляется 32-битное число, определяющее длину порции данных в байтах. Таким образом, принимающая часть, зная размер каждого блока, может распознать "склейку" и фрагментацию.

    Изложенной информации достаточно для понимания идеологии сетевых приложений. Об остальных аспектах сетевого программирования с использованием библиотеки winsock вы можете узнать из справки "windows sdk" в разделе "windows sockets 2 application program interface".

    Источник: http://ruconsult.org/articles?article=2

    Архив > Delphi/Pascal > DELPHI FAQ (по темам) > Сети.Интернет.HTML.Почта

    Добавлено: 18/05/06 Просмотров: 9311

    [ Предыдущая страница ][ Поиск в архиве | Архив ] [ Каталог ] [ Карта сайта ]

    Подписка на новости сайтаНовости на Email:
    Subscribe.Ru
    Rss2Email.ru
    Читать в Яндекс.Ленте FeedBurner

    This FAQ is powered by CascadianFAQ v4.1, developed by Summer S. Wilson at Eclectic Designs.


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

    Подписка на новостиНовости на Email
    Subscribe.Ru
    Rss2Email.ru
    Читать в Яндекс.Ленте
    FeedBurner
    Форум:
    DELPHI
    C/C++
    WEB
    Алгоритмы
    Прочие
    Новости сайта
    О публикациях
    Windows
    Программы
    Флейм
    Темы форумов:
  • kristart Помогите выбрать свободную ОС в форуме Windows Jul 29 2010, 14:44:07
  • FYAN Нужен программист в форуме Прочие Jul 28 2010, 07:36:26
  • gleb_sitnikov Предлагаю руководство в форуме Предложения по публикации в каталоге и комментарии. Jul 9 2010, 17:13:10
  • piter Очень нужен программист в форуме DELPHI Jul 1 2010, 16:13:02
  • piter БД - 3 нормальная форма. в форуме Прочие Jun 28 2010, 09:42:55
  • abee Join в Foxpro9. в форуме Прочие Jun 28 2010, 09:40:37
  • gleb_sitnikov Предлагаю статью в форуме Предложения по публикации в каталоге и комментарии. Jun 21 2010, 11:16:01
  • piter определение даты в форуме DELPHI Jun 7 2010, 16:01:27
  • gleb_sitnikov Предлагаю учебник в форуме Предложения по публикации в каталоге и комментарии. Jun 7 2010, 12:13:31
  • gogo пожалуйста, напишите программу в Visual C++ в форуме C++ Builder Jun 7 2010, 11:06:51
  • Новые комменты к
  • Process Explorer Бесплатная, компактная, программа для мониторинга в режиме реального времени сис...
  • RbControls Pack Компоненты анимируются при перемещении и нажатии мышки. Эти компоненты позволяют...
  • Библиотека BASS библиотека содержит большое количество примеров, в том числе и на дельфи, позвол...
  • Протокол TCP/IP или как работает Интернет (для чайников) В основе работы глобальной сети Интернет лежит набор (стек) протоколов TCP/IP. ...

  • Карта сайта   Каталог  Архив сайта за 2001-2007 годыАрхив  Форум  Блог  Начало страницы  Добавить в избранное  Предыдущая страница
    Поиск  Поиск по сайту Google Сайт Google
      
    Поиск по сайту Яндекс
    0.12с
    Каталог программиста Блог "Компьютеры и жизнь" Top 100 Borland Sites. Vote for us