Програмуємо під IIS на Visual C + + h2>
Однією з поширених завдань адміністрування
web-сайтів є аналіз log-файлів і збір даних з них. Поговоримо про цю
задачі на прикладі IIS 5.0 (Internet Information Service). У зв'язку з тим, що
даний HTTP сервер підтримує кілька форматів таких файлів (формат W3C,
формат NCSA, а також свій формат IIS), аналіз log-файлів перетворюється на
досить трудомістку задачу. Крім того для формату W3C список полів може
конфігуруватися, що завдання не полегшує. p>
Але на щастя разом з IIS 5.0 у складі інших
компонентів, поставляється компонент MSWC.IISLog, що призначений для
одержання даних з log-файлів і надає для цієї мети інтерфейс
ILogScripting. Знаходиться він у файлі% windir% system32inetsrvlogscrpt.dll. З
допомогою цього інтерфейсу можна читати дані з log-файлу, записувати
прочитані дані в інший файл. p>
Перед тим як почати роботу з будь-якими log-файлом,
його потрібно відкрити. Для цього служить метод OpenLogFile: p>
HRESULT
OpenLogFile ([in] BSTR szLogFileName, p>
[in] IOMode Mode, p>
[in] BSTR szServiceName, p>
[in] long iServiceInstance, p>
[in] BSTR szOutputLogFileFormat),
p>
де в параметрі szLogFileName задається повний шлях до
файлу; параметр Mode може приймати наступні значення: p>
ForReading - файл буде відкрито для читання p>
ForWriting - файл буде відкритий для запису; p>
в параметрі szServiceName задається назва служби,
якої був створений даний файл (наприклад "W3SVC"); парметр
iServiceInstance вказує номер примірника сервера даної служби (напр. 1); в
параметрі szOutputLogFileFormat задається назва формату, в якому будуть
читатися чи записуватися дані (наприклад "W3C"). Якщо метод
виконався успішно то повертається 0. p>
Для читання даних з файлу служить метод ReadLogRecord: p>
HRESULT ReadLogRecord (), p>
який читає рядок з поточної файлової позиції і
переміщує позиціонер файлу далі. Ми можемо отримати ці дані у вигляді
значень конкретних полів за допомогою безлічі методів, які повертають ці
значення: p>
get_DateTime p>
Відображення дати та часу
за Гринвічем (GMT) p>
get_ServiceName p>
Відображення імені служби p>
get_ServerName p>
Відображення імені сервера p>
get_ClientIP p>
Відображення імені вузла
клієнта p>
get_UserName p>
Відображення імені користувача p>
get_ServerIP p>
Відображення IP-адреси сервера p>
get_Method p>
Відображення типу операції p>
get_URIStem p>
Відображення адреси URL p>
get_URIQuery p>
Відображення всіх параметрів, що передаються з
запитом p>
get_TimeTaken p>
Відображення загального часу
обробки p>
get_BytesSent p>
Відображення числа переданих байтів p>
get_BytesReceived p>
Відображення числа отриманих байтів p>
get_Win32Status p>
Відображення коду стану Win32 p>
get_ProtocolStatus p>
Відображення стану протоколу p>
get_ServerPort p>
Відображення номера порту p>
get_ProtocolVersion p>
Відображення рядка версії p>
get_UserAgent p>
Відображення рядка агента користувача p>
get_Cookie p>
Відображення імені Cookie клієнта p>
get_Referer p>
Відображення сторінки джерела посилання p>
get_CustomFields p>
Відображення масиву
спеціальних заголовків p>
p>
Всі ці методи мають один формат: HRESULT get_MethodName (VARIANT * pValue). У параметрі pValue буде повернуто який нас цікавить
значення. Якщо значення запитуваної параметра дорівнює "-", то тип
pValue буде VT_EMPTY. Якщо ж з якихось причин параметр не буде знайдено в
log-файлі (напр. параметр вимкнено, або поточний рядок - незначущий), то тип
pValue буде VT_NULL. p>
Для того, щоб ми сильно не захопилися існує
метод AtEndOfLog, який подібно до звичного feof повертає (або не повертає
:)) Ознака досягнення кінця файлу і виглядає приблизно так: p>
HRESULT
AtEndOfLog ([out, retval] VARIANT_BOOL * pfEndOfRead) p>
У параметрі pfEndOfRead, власне, і повертається
що нас цікавить ознака, що дозволяє нам все таки коли-небудь завершити
обробку log-файлу. p>
Крім усього цього є ще метод WriteLogRecord,
який дозволяє записувати дані, отримані з одного log-файла в інший.
Формат його наступний: p>
HRESULT WriteLogRecord ([in] ILogScripting *
pILogScripting), p>
де p>
pILogScripting - це покажчик на екземпляр
ILogScripting, в якому містяться прочитані дані. Цей метод схожий на
ReadLogRecord тим, що записує в файл один рядок (нагадаю, що
ReadLogRecord читає один рядок). P>
Для закриття відкритих файлів служить метод
CloseLogFiles: p>
HRESULT
CloseLogFiles ([in] IOMode Mode), p>
в якому параметр Mode може приймати наступні
значення: p>
ForReading - будуть закриті файли, відкриті для читання p>
ForWriting - будуть закриті файли, відкриті для запису p>
AllOpenFiles - будуть закриті всі відкриті файли p>
Щоб доступитися до компоненту MSWC.IISLog з C + + треба
мати. h файл, що описує інтерфейс ILogScripting. Зробити його нам допоможе
утиліта OLEViewer, яка входить до складу Visual Studio. У меню
"File" цієї утиліти виберемо пункт "View TypeLib" і зазначимо
файл logscrpt.dll (природно повний шлях до нього). У новому вікні відкриється
бібліотека типів нашого об'єкта, яку збережено в якості. idl файлу. p>
Для цього в меню "File" виберемо пункт
"Save as", вкажемо тип файлу "idl", і збережемо його на якусь
місце (наприклад туди, де знаходиться проект), наприклад, назвавши його
"logscrpt.idl". Все б добре, та тільки в новоспеченому фото
доведеться зробити косметичні зміни. По-перше в самий початок файлу треба
вставити наступні рядки: p>
cpp_quote ( "DEFINE_GUID (CLSID_LogScripting,
0x26B9ED02, 0xA3D8, 0x11D1, 0x8B, 0x9C, 0x08, 0x00, 0x09, 0xDC, 0xC2,
0xFA );") p>
cpp_quote ( "DEFINE_GUID (IID_ILogScripting,
0x26B9ECFF, 0xA3D8, 0x11D1, 0x8B, 0x9C, 0x08, 0x00, 0x09, 0xDC, 0xC2,
0xFA );") p>
По-друге рядки p>
typedef enum ( p>
ForReading =
1, p>
ForWriting =
2, p>
AllOpenFiles =
32 p>
) IOMode; p>
треба перенести так, щоб вони перебували до
визначення ILogScripting інтерфейсу. p>
Тепер залишилося тільки згенерувати файл logscrpt.h з
допомогою команди midl.exe logscrpt.idl/h logscrpt.h (перевірте шлях до
компілятору midl). p>
На закінчення наведу приклад програми роботи з
інтерфейсом, яка отримує в якості параметрів командного рядка шлях до
log-файлу, назва служби, назву формату файлу, номер примірника сервера і
видає на екран список всіх URL адрес, до яких були зафіксовані звернення
в цьому файлі. Ось приклад виклику цієї програми: p>
logging.exe d: logsw3svc2ex01060515.log W3SVC W3C 2 p>
# include p>
# include p>
# include p>
# include p>
# include
"logscrpt.h" p>
# define
SOME_ERROR (lpszErrorText, nErrorNum) p>
printf ( "% s:% X p>
",
lpszErrorText, nErrorNum); p>
throw nErrorNum; p>
int main (int argc,
char * argv []) p>
( p>
HRESULT hres; p>
VARIANT vParam; p>
short nEndOfFile; p>
_bstr_t bstrLogFile; p>
_bstr_t
bstrServiceName; p>
_bstr_t
bstrLogFormat; p>
long
nServerInstance; p>
_bstr_t bstrUriStem; p>
// перевірка наявності параметрів p>
if (argc <5) p>
( p>
printf ( "Usage:
% s LogFileName ServiceNa p>
me LogFormatName ServerInstance p>
", argv [0 ]); p>
return 0; p>
) p>
// отримуємо параметри з командної строчки p>
try ( p>
bstrLogFile =
argv [1]; p>
bstrServiceName =
argv [2]; p>
bstrLogFormat =
argv [3]; p>
if (! (nServerInstance = atol (argv [4 ]))) p>
// екземпляр сервера не може бути 0 p>
nServerInstance =
1; p>
) p>
catch (...) ( p>
printf ( "Something
wrong in parameters! p>
"); p>
return 0; p>
) p>
// це буде посилання на інтерфейс p>
ILogScripting
* pLogScripting = NULL; p>
try ( p>
// ініціалізіруем COM p>
if (! SUCCEEDED (hres = CoInitialize (NULL ))) p>
( p>
SOME_ERROR ( "CoInitialize
error ", hres); p>
) p>
// тепер створимо екзепляр інтерфейсу p>
if (! SUCCEEDED (hres =
CoCreateInstance (CLSID_LogScripting, p>
NULL, CLSCTX_ALL,
IID_ILogScripting, p>
(void
**) & pLogScripting ))) p>
( p>
SOME_ERROR ( "CoCreateInstance
error ", hres); p>
) p>
// відкриваємо log-файл bstrLogFile для читання,
вказуючи, що цей p>
// фото відноситься до перших примірників сервера служби
W3SVC, а// p>
// формат у нього W3C p>
if (! SUCCEEDED (hres = p>
pLogScripting-OpenLogFile (BSTR (bstrLogFile),
ForReading, p>
BSTR (bstrServiceName),
1, BSTR (bstrLogFormat )))) p>
( p>
SOME_ERROR ( "OpenLogFile
error ", hres); p>
) p>
// тепер можна приступити до аналізу вмісту файлу p>
for (;;) ( p>
// перевіримо на досягнення кінця файлу p>
if (! SUCCEEDED (hres = p>
pLogScripting-AtEndOfLog (& nEndOfFile ))) p>
( p>
SOME_ERROR ( "AtEndOfLog error",
hres); p>
) p>
if (nEndOfFile) p>
// щасливо виходимо p>
break; p>
// читаємо такий запис файлу p>
if (! SUCCEEDED (hres =
pLogScripting-ReadLogRecord ())) p>
( p>
SOME_ERROR ( "ReadLogRecord error",
hres); p>
) p>
// отримуємо з неї параметр адреса URL p>
if (! SUCCEEDED (hres =
pLogScripting-get_URIStem (& vParam ))) p>
( p>
SOME_ERROR ( "ReadLogRecord error",
hres); p>
) p>
if (vParam.vt == VT_BSTR) p>
( p>
// якщо
параметр не порожній роздрукувати його на екрані p>
bstrUriStem =
vParam.bstrVal; p>
printf ( "Uri-stem:% s p>
",
LPSTR (bstrUriStem )); p>
) p>
) p>
// закриваємо файл p>
if (! SUCCEEDED (hres = pLogScripting-CloseLogFiles (ForReading ))) p>
( p>
SOME_ERROR ( "CloseLogFiles
error ", hres); p>
) p>
) p>
catch (...) () p>
// останні дії по деініціалізацію p>
if
(pLogScripting! = NULL) p>
pLogScripting-Release (); p>
CoUninitialize (); p>
return hres; p>
) p>
Dima Mukalov p>
Список літератури p>
Для підготовки даної роботи були використані
матеріали з сайту http://www.realcoding.net/ p>
Робота з WinSocket в Visual C + + p>
Socket (гніздо, роз'єм) - абстрактне програмне
поняття, що використовується для позначення в прикладній програмі кінцевої точки
каналу зв'язку з комунікаційним середовищем, утвореної обчислювальною мережею. При
використанні протоколів TCP/IP можна говорити, що socket є засобом
підключення прикладної програми до порту (див. вище) локального вузла мережі. p>
Socket-інтерфейс являє собою просто набір
системних викликів та/або бібліотечних функцій мови програмування СІ,
розділених на чотири групи: p>
Нижче розглядається підмножина функцій
socket-інтерфейсу, достатню для написання мережевих програм, що реалізують
модель "клієнт-сервер" в режимі з встановленням з'єднання. p>
1. Опції локального управління p>
Опції локального управління використовуються, головним
чином, для виконання підготовчих дій, необхідних для організації
взаємодії двох програм-партнерів. Опції носять таку назву, оскільки
їх виконання носить локальний для програми характер. p>
1.1 Створення socket'а p>
Створення socket'а здійснюється наступним системним
викликом # include int socket (domain, type, protocol) int
domain; int type; int protocol; p>
Аргумент domain задає використовуваний для взаємодії
набір протоколів (вид комунікаційної області), для стека протоколів TCP/IP він
повинен мати символьне значення AF_INET (визначено в sys/socket.h). p>
Аргумент type задає режим взаємодії: p>
SOCK_STREAM - з встановленням з'єднання; p>
SOCK_DGRAM - без встановлення з'єднання. p>
Аргумент protocolзадает конкретний протокол
транспортного рівня (з декількох можливих у стеку протоколів). Якщо цей
аргумент задано рівним 0, то буде використаний протокол "за замовчуванням"
(TCP для SOCK_STREAM і UDP для SOCK_DGRAM при використанні комплекту
протоколів TCP/IP). p>
При вдалому завершенні своєї роботи ця функція
повертає дескриптор socket'а - ціле невід'ємне число, однозначно його
ідентифікує. Дескриптор socket'а аналогічний дескриптор файлу ОС UNIX. p>
При виявленні помилки в ході своєї роботи функція
повертає число "-1". p>
1.2. Зв'язування socket'а p>
Для підключення socket'а до комунікаційне середовище,
утвореної обчислювальної мережі, необхідно виконати системний виклик bind,
визначальними у прийнятому для мережі форматі локальний адресу каналу зв'язку з
середовищем. У мережах TCP/IP socket зв'язується з локальним портом. Системний виклик
bind має наступний синтаксис: p>
# include
p>
# include p>
# include
p>
int bind (s, addr,
addrlen) p>
int s; p>
struct sockaddr
* addr; p>
int addrlen; p>
Аргумент s задає дескриптор пов'язують socket'а. p>
Аргумент addr в загальному випадку повинен вказувати на
структуру даних, яка містить локальний адресу, що приписується socket'у. Для мереж
TCP/IP такою структурою є sockaddr_in. p>
Аргумент addrlen задає розмір (в байтах) структури
даних, що вказується аргументом addr. p>
Структура sockaddr_in використовується декількома
системними викликами та функціями socket-інтерфейсу і визначено в include-файлі
in.h наступним чином: p>
struct sockaddr_in
( p>
short sin_family; p>
u_short sin_port; p>
struct in_addr
sin_addr; p>
char sin_zero [8]; p>
); p>
Поле sin_family визначає використовуваний формат адреси
(набір протоколів), у нашому випадку (для TCP/IP) вона повинна мати значення
AF_INET. p>
Поле sin_addr містить адресу (номер) вузла мережі. p>
Поле sin_port містить номер порту на вузлі мережі. p>
Поле sin_zero не використовується. p>
Визначення структури in_addr (з того ж
include-файлу) таке: p>
struct in_addr ( p>
union ( p>
u_long S_addr;/* p>
інші (не цікавлять нас) p>
члени об'єднання */ p>
) S_un; p>
# define s_addr
S_un.S_addr p>
); p>
Структура sockaddr_in повинна бути повністю заповнена
перед видачею системного виклику bind. При цьому, якщо поле sin_addr.s_addr має
значення INADDR_ANY, то системний виклик буде прив'язувати до socket'у номер
(адреса) локального вузла мережі. p>
У разі успіху bind повертає 0, інакше
- "-1". p>
2. Функції встановлення зв'язку p>
Для встановлення зв'язку "клієнт-сервер"
використовуються системні виклики listen і accept (на стороні сервера), а також
connect (на стороні клієнта). Для заповнення полів структури socaddr_in,
використовуваної у виклику connect, звичайно використовується бібліотечна функція
gethostbyname, що транслює символічне ім'я вузла мережі в його номер (адреса). p>
2.1. Очікування встановлення зв'язку p>
Системний виклик listen висловлює бажання видала його
програми-сервера очікувати запити до неї від програм-клієнтів і має наступний
вигляд: p>
# include
p>
int listen (s, n) p>
int s; p>
int n; p>
Аргумент s задає дескриптор socket'а, через який
програма буде очікувати запити до неї від клієнтів. Socket повинен бути
попередньо створений системним викликом socketі забезпечений адресою з допомогою
системного виклику bind. p>
Аргумент n визначає максимальну довжину черги
вхідних запитів на встановлення зв'язку. Якщо будь-який клієнт видасть запит
на встановлення зв'язку при повній черзі, то цей запит буде відкинутий. p>
Ознакою вдалого завершення системного виклику listen
служить нульовий код повернення. p>
2.2. Запит на встановлення з'єднання p>
Для звернення програми-клієнта до сервера з запитом
на встановлення логічної з'єднання використовується системний виклик connect,
має такий вигляд # include # include
# include int connect (s, addr,
addrlen) int s; struct sockaddr_in * addr; int addrlen; p>
Аргумент s задає дескриптор socket'а, через який
програма звертається до сервера з запитом на з'єднання. Socket повинен бути
попередньо створений системним викликом socketі забезпечений адресою з допомогою
системного виклику bind. p>
Аргумент addr повинен вказувати на структуру даних,
містить адресу, приписаний socket'у програми-сервера, до якої робиться
запит на з'єднання. Для мереж TCP/IP такою структурою є sockaddr_in.
Для формування значень полів структури sockaddr_in зручно використовувати
функцію gethostbyname. p>
Аргумент addrlen задає розмір (в байтах) структури
даних, що вказується аргументом addr. p>
Для того, щоб запит на з'єднання був успішним,
необхідно, принаймні, щоб програма-сервер виконала до цього моменту
системний виклик listen для socket'а з вказаною адресою. p>
При успішному виконанні запиту системний виклик connect
повертає 0, в іншому випадку - "-1" (встановлюючи код причини
неуспіху в глобальній змінній errno). p>
Примітка. Якщо до моменту виконання connect
використовуваний ним socket не був прив'язаний до адреси за допомогою bind, то така
прив'язка буде виконана автоматично. p>
Примітка. У режимі взаємодії без встановлення
з'єднання необхідності у виконанні системного виклику connect немає. Однак,
його виконання в такому режимі не є помилкою - просто міняється зміст
виконуваних при цьому дій: встановлюється адреса "за замовчуванням"
для всіх наступних посилок дейтаграм. p>
2.3. Прийом запиту на встановлення зв'язку p>
Для прийому запитів від програм-клієнтів на
встановлення зв'язку в програмах-серверах використовується системний виклик accept,
має такий вигляд: p>
# include
p>
# include
p>
int accept (s,
addr, p_addrlen) p>
int s; p>
struct sockaddr_in
* addr; p>
int * p_addrlen; p>
Аргумент s задає дескриптор socket'а, через який
програма-сервер отримала запит на з'єднання (за допомогою системного запиту
listen). p>
Аргумент addr повинен вказувати на область пам'яті,
розмір якої дозволяв би розмістити в ній структуру даних, що містить адресу
socket'а програми-клієнта, яка зробила запит на з'єднання. Ніякої
ініціалізації цій області не потрібно. p>
Аргумент p_addrlen повинен вказувати на область пам'яті
у вигляді цілого числа, що задає розмір (в байтах) області пам'яті, що вказується
аргументом addr. p>
Системний виклик accept витягує з черги,
організованою системним викликом listen, перший запит на з'єднання і
повертає дескриптор нового (автоматично створеного) socket'а з тими ж
властивості, що й socket, що задається аргументом s. Цей новий дескриптор
необхідно використовувати у всіх подальших операціях обміну даними. p>
Крім того після у?? ачного завершення accept: p>
область пам'яті, що вказується аргументом addr, буде
містити структуру даних (для мереж TCP/IP це sockaddr_in), що описує
адреса socket'а програми-клієнта, через який вона зробила свій запит на
з'єднання; p>
ціле число, на яке вказує аргумент p_addrlen,
дорівнюватиме розміру цієї структури даних. p>
Якщо чергу запитів на момент виконання accept
порожня, то програма переходить в стан очікування надходження запитів від
клієнтів на невизначений час (хоча така поведінка accept можна і
змінити). p>
Ознакою невдалого завершення accept служить
негативне повернене значення (дескриптор socket'а негативним бути не
може). p>
Примітка. Системний виклик accept використовується в
програмах-серверах, що функціонують тільки в режимі з встановленням
з'єднання. p>
2.4. Формування адреси вузла мережі p>
Для отримання адреси вузла мережі TCP/IP за його
символічному імені використовується бібліотечна функція p>
# include
p>
# include
p>
struct hostent
* gethostbyname (name) p>
char * name; p>
Аргумент name задає адреса послідовності літер,
утворять символічне ім'я вузла мережі. p>
При успішному завершенні функція повертає вказівник
на структуру hostent, визначену у include-файлі netdb.h і має наступний
вид struct hostent (char * h_name; char ** h_aliases; int h_addrtype; int
h_lenght; char * h_addr;); p>
Поле h_name вказує на офіційне (основне) ім'я
вузла. p>
Поле h_aliases вказує на список додаткових імен
вузла (синонімів), якщо вони є. p>
Поле h_addrtype містить ідентифікатор використовується
набору протоколів, для мереж TCP/IP це поле буде мати значення AF_INET. p>
Поле h_lenght містить довжину адреси вузла. p>
Поле h_addr вказує на область пам'яті, що містить
адреса вузла в тому вигляді, в якому його використовують системні виклики та функції
socket-інтерфейсу. p>
Приклад звернення до функції gethostbyname для отримання
адреси віддаленого вузла в програмі-клієнті, що використовує системний виклик
connect для формування запиту на підтримання зв'язку з
програмою-сервером на цьому сайті, розглядається нижче. p>
3. Функції обміну даними p>
У режимі зі встановленням логічного з'єднання після
вдалого виконання пари взаємопов'язаних системних викликів connect (у клієнта)
і accept (в сервер) стає можливим обмін даними. p>
Цей обмін може бути реалізований звичайними системними
викликами read і write, що використовуються для роботи з файлами (при цьому замість
дескрипторів файлів у них задаються дескриптори socket'ов). p>
Крім того можуть бути додатково використані
системні виклики send і recv, спеціально орієнтовані на роботу з
socket'амі. p>
Примітка. Для обміну даними в режимі без
встановлення логічного з'єднання використовуються, як правило, системні виклики
sendtoі recvfrom. Sendto дозволяє специфікувати разом з передаваними
даними (складовими дейтаграму) адреса їх одержувача. Recvfrom одночасно
з доставкою даних одержувачу інформує його і про адресу відправника. p>
3.1. Здійснення даних p>
Щоб здійснити даних партнеру з мережевого взаємодії
використовується системний виклик send, що має такий вигляд p>
# include
p>
# include
p>
int send (s, buf,
len, flags) p>
int s; p>
char * buf; p>
int len; p>
int flags; p>
Аргумент s задає дескриптор socket'а, через який
надсилаються дані. p>
Аргумент buf вказує на область пам'яті, що містить
передані дані. p>
Аргумент len задає довжину (у байтах) передаються
даних. p>
Аргумент flags модифікує виконання системного
виклику send. При нульовому значенні цього аргументу виклик send повністю
аналогічний системного виклику write. p>
При успішному завершенні send повертає кількість
переданих з області, зазначеної аргументом buf, байт даних. Якщо канал
даних, який визначається дескриптором s, виявляється "переповненим", то
send переводить програму в стан очікування до моменту його звільнення. p>
3.2. Отримання даних p>
Для отримання даних від партнера по мережному
взаємодії використовується системний виклик recv, що має такий вигляд p>
# include
p>
# include
p>
int recv (s, buf,
len, flags) p>
int s; p>
char * buf; p>
int len; p>
int flags; p>
Аргумент s задає дескриптор socket'а, через який
приймаються дані. p>
Аргумент buf вказує на область пам'яті,
призначену для розміщення прийнятих даних. p>
Аргумент len задає довжину (у байтах) цієї області. p>
Аргумент flags модифікує виконання системного
виклику recv. При нульовому значенні цього аргументу виклик recv повністю
аналогічний системного виклику read. p>
При успішному завершенні recv повертає кількість
прийнятих в область, зазначену аргументом buf, байт даних. Якщо канал даних,
обумовлений дескриптором s, виявляється "порожнім", то recv переводить
програму в стан очікування до моменту появи в ньому даних. p>
4. Опції закриття зв'язку p>
Для закриття зв'язку з партнером по мережному
взаємодії використовуються системні виклики close і shutdown. p>
4.1. Виклик close p>
Для закриття раніше створеного socket'а використовується
звичайний системний виклик close, що застосовується в ОС UNIX для закриття раніше
відкритих файлів і має такий вигляд p>
int close (s) p>
int s; p>
Аргумент s задає дескриптор раніше створеного
socket'а. p>
Проте в режимі з встановленням логічного з'єднання
(що забезпечує, як правило, надійну доставку даних) внутрішньосистемні
механізми обміну будуть намагатися передати/прийняти дані, що залишилися в каналі
передачі на момент закриття socket'а. Це може тривати значний
інтервал часу, неприйнятний для деяких додатків. У такій ситуації
необхідно використовувати описуваний далі системний виклик shutdown. p>
4.2. Скидання буферізованние даних p>
Для "екстреного" закриття зв'язку з партнером
(шляхом "скидання" ще не переданих даних) використовується системний
виклик shutdown, що виконується перед close і має такий вигляд p>
int shutdown (s,
how) p>
int s; p>
int how; p>
Аргумент s задає дескриптор раніше створеного
socket'а. p>
Аргумент how задає дії, що виконуються при очищенні
системних буферів socket'а: p>
0 - скинути і далі не брати дані для читання з
socket'а; p>
1 - скинути і далі не відправляти дані для посилки
через socket; p>
2 - скинути всі дані, передані через socket в
будь-якому напрямку. p>
5. Приклад використання socket-інтерфейсу p>
У цьому розділі розглядається використання
socket-інтерфейсу в режимі взаємодії зі встановленням логічного
з'єднання на дуже простому прикладі взаємодії двох програм (сервера і
клієнта), що функціонують у різних вузлах мережі TCP/IP. p>
Змістовна частина програм примітивна: p>
сервер, прийнявши запит на з'єднання, передає клієнту
питання "Who are you?"; p>
клієнт, отримавши питання, виводить його в стандартний
висновок і направляє серверу відповідь "I am your client" і завершує на
цьому свою роботу; p>
сервер виводить у стандартний вивід відповідь клієнта,
закриває з ним зв'язок і переходить в стан очікування наступного запиту до
нього. p>
Примітка. Пропоновані нижче тексти програм
призначені тільки для ілюстрації логіки взаємодії програм через мережу,
тому в них відсутні такі атрибути програм, призначених для
практичного застосування, як обробка кодів повернення системних викликів і
функцій, аналіз кодів помилок в глобальній змінній errno, реакція на
асинхронні події і т.п. p>
5.1. Програма-сервер p>
Текст програми-сервера на мові програмування СІ
виглядає наступним чином p>
1 # include p>
2 # include p>
3 # include p>
4 # include p>
5 # include p>
6 # define SRV_PORT 1234 p>
7 # define BUF_SIZE 64 p>
8 # define TXT_QUEST "Who are
you? n " p>
9 main () ( p>
10 int s, s_new; p>
11 int from_len; p>
12 char buf [BUF_SIZE]; p>
13 struct sockaddr_in sin, from_sin; p>
14 s = socket (AF_INET, SOCK_STREAM, 0); p>
15 memset ((char *) & sin,'', sizeof (sin));
p>
16 sin.sin_family = AF_INET; p>
17 sin.sin_addr.s_addr = INADDR_ANY; p>
18 sin.sin_port = SRV_PORT; p>
19 bind (s, (struct sockaddr *) & sin,
sizeof (sin)); p>
20 listen (s, 3); p>
21 while (1) ( p>
22 from_len = sizeof (from_sin); p>
23 s_new = accept (s, & from_sin,
& from_len); p>
24 write (s_new, TXT_QUEST,
sizeof (TXT_QUEST)); p>
25 from_len = read (s_new, buf, BUF_SIZE); p>
26 write (1, buf, from_len); p>
27 shutdown (s_new, 0); p>
28 close
(s_new); p>
29); p>
30) p>
Рядки 1 ... 5 описують включаються файли, що містять
визначення для всіх необхідних структур даних і символічних констант. p>
Рядок 6 приписує цілочисельний константі 1234
символічне ім'я SRV_PORT. Надалі ця константа буде використана в
як номер порту сервера. Значення цієї константи має бути відомо і
програми-клієнта. p>
Рядок 7 приписує цілочисельний константі 64
символічне ім'я BUF_SIZE. Ця константа буде визначати розмір буфера,
використовуваного для розміщення приймаються від клієнта даних. p>
Рядок 8 приписує послідовності символів,
складових текст питання клієнту, символічне ім'я TXT_QUEST. Останнім
символом у послідовності є символ переходу на новий рядок 'n'.
Зроблено це для спрощення перегляду тексту питання на стороні клієнта. p>
У рядку 14 створюється (відкривається) socket для
організації режиму взаємодії зі встановленням логічного з'єднання
(SOCK_STREAM) у мережі TCP/IP (AF_INET), при виборі протоколу транспортного
рівня використовується протокол "за умовчанням" (0). p>
У рядках 15 ... 18 спочатку обнуляється структура даних
sin, а потім заповнюються її окремі поля. Використання константи INADDR_ANY
спрощує текст програми, позбавляючи від необхідності використовувати функцію
gethostbyname для отримання адреси локального вузла, на якому запускається
сервер. p>
Рядок 19 за допомогою системного виклику bind
прив'язує socket, що задається дескриптором s, до порту з номером SRV_PORT на
локальному вузлі. Bind завершиться успішно за умови, що в момент його
виконання на тому ж сайті вже не функціонує програма, що використовує цей
номер порту. p>
Рядок 20 за допомогою системного виклику listen
організовує чергу на три входять до сервера запиту на з'єднання. p>
Рядок 21 служить заголовком нескінченного циклу
обслуговування запитів від клієнтів. p>
На рядку 23, що містить системний виклик accept,
виконання програми призупиняється на невизначений час, якщо чергу
запитів до сервера на встановлення зв'язку виявляється порожня. При появі
такого запиту accept успішно завершується, повертаючи в змінної s_new
дескриптор socket'а для обміну інформацією з клієнтом. p>
У рядку 24 сервер з допомогою системного виклику write
відправляє клієнту питання. p>
У рядку 25 за допомогою системного виклику read читається
відповідь клієнта. p>
У рядку 26 відповідь направляється у стандартний висновок,
що має дескриптор файлу номер 1. Так як рядок відповіді містить в собі символ
переходу на новий рядок, то текст відповіді буде розміщений на окремому рядку
дисплея. p>
Рядок 27 містить системний висновок shutdown,
забезпечує очищення системних буферів socket'а, що містять дані для читання
( "зайві" дані можуть там опинитися в результаті невірної роботи
клієнта). p>
У рядку 28 закривається (вилучається) socket,
використаний для обміну даними з черговим клієнтом. p>
Примітка. Дана програма (як і більшість
реальних програм-серверів) самостійно своєї роботи не завершує, перебуваючи
у нескінченному циклі обробки запитів клієнтів. Її виконання може бути
перервано тільки ззовні шляхом надсилання їй сигналів (переривань) завершення.
Правильно розроблена програма-сервер повинна обробляти такі сигнали,
коректно завершуючи роботу (закриваючи, зокрема, за допомогою close socket з
дескриптором s). p>
5.2. Програма-клієнт p>
Текст програми-клієнта на мові програмування СІ
виглядає наступним чином p>
1 # include p>
2 # include p>
3 # include p>
4 # include p>
5 # include p>
6 # define SRV_HOST "delta" p>
7 # define SRV_PORT 1234 p>
8 # define CLNT_PORT 1235 p>
9 # define BUF_SIZE 64 p>
10 # define TXT_ANSW "I am your
clientn " p>
11 main () ( p>
12 int s; p>
13 int from_len; p>
14 char buf [BUF_SIZE]; p>
15 struct hostent * hp; p>
16 struct sockaddr_in clnt_sin, srv_sin; p>
17 s = socket (AF_INET, SOCK_STREAM, 0); p>
18 memset ((char *) & clnt_sin,'', p>
sizeof (clnt_sin)); p>
19 clnt_sin.sin_family = AF_INET; p>
20 clnt_sin.sin_addr.s_addr = INADDR_ANY; p>
21 clnt_sin.sin_port = CLNT_PORT; p>
22 bind (s, (struct sockaddr *) & clnt_sin, p>
sizeof (clnt_sin)); p>
23 memset ((char *) & srv_sin,'', p>
sizeof (srv_sin)); p>
24 hp = gethostbyname (SRV_HOST); p>
25 srv_sin.sin_family = AF_INET; p>
26 memcpy ((char p>
*) & srv_sin.sin_addr, hp-> h_addr, hp-> h_length);
p>
27 srv_sin.sin_port = SRV_PORT; p>
28 connect (s, & srv_sin, sizeof (srv_sin));
p>
29 from_len = recv (s, buf, BUF_SIZE, 0); p>
30 write (1, buf, from_len); p>
31 send (s, TXT_ANSW, sizeof (TXT_ANSW), 0); p>
32 close (s);
p>
33 exit (0); p>
34) p>
У рядках 6 і 7 описуються константи SRV_HOST і
SRV_PORT, що визначають ім'я віддаленого вузла, на якому функціонує
програма-сервер, і номер порту, до якого прив'язаний socket сервера. p>
Рядок 8 приписує цілочисельний константі 1235
символічне ім'я CLNT_PORT. Надалі ця константа буде використана в
як номер порту клієнта. p>
У рядках 17 ... 22 створюється прив'язаний до порту на
локальному вузлі socket. p>
У рядку 24 за допомогою бібліотечної функції
gethostbyname транслюється символічне ім'я віддаленого вузла (в даному випадку
"delta"), на якому має функціонувати сервер, на адресу цього
вузла, розміщений у структурі типу hostent. p>
В рядку 26 адреса віддаленого вузла копіюється з
структури типу hostent у відповідне поле структури srv_sin, яка пізніше
(у рядку 28) використовується в системному виклику connect для ідентифікації
програми-сервера. p>
У рядках 29 ... 31 здійснюється обмін даними з
сервером і висновок питання, що надійшов від сервера, в стандартний вивід. p>
Рядок 32 за допомогою системного виклику close
закриває (видаляє) socket. p>
Список літератури h2>
Для підготовки даної роботи були використані
матеріали з сайту http://www.realcoding.net/
p>