Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
12
Добавлен:
20.04.2024
Размер:
8.91 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

IN PDEVICE_OBJECT DeviceObject,

IN OUT PIRP Irp

)

{

Irp->CurrentLocation--;

irpSp = IoGetNextIrpStackLocation( Irp );

Irp->Tail.Overlay.CurrentStackLocation = irpSp;

irpSp->DeviceObject = DeviceObject;

driverObject = DeviceObject->DriverObject;

status = driverObject->MajorFunction[irpSp->MajorFunction](

DeviceObject, Irp );

return status;

}

Кстати,IoCallDriver—этопсевдонимвстречаемоговлитературе названияфункцииIofCallDriver,которая,всвоюочередь,всеголишь являетсязаглушкойдлявызоваIopfCallDriver.Можносразуотметить, чтоеслифильтроватьданныевядревышеописаннымобразом,то всё,чембудетсебявыдаватьтакаяфильтрация,—этоодинперехват IoCallDriver.Ивсё!Небудетсуществоватькаких-топодозритель- ныхустройств,прицепленныхвстек,небудетперехватовmajor- обработчиковвсистеме.Всё,чтоможнобудетнайти,—этоизменен- ныйадресфункцииIoCallDriver,авотужеопределить,чтоименно фильтруетсяикакиеданныепроходятчерезнутротвоегонепростого драйвера(еслион,конечно,существуетотдельнымфайлом,ночто мешаетзаразитьдругойдрайвер?),будеточеньиоченьсложно.

ОПРЕДЕЛЯЕМ,ОТКУДАИКУДА

Теперьсамоеглавноеисамоесложное.ПростойперехватIRP-пакета, основанныйнаединственномхукеIoCallDriver,внашейситуации

непоможет,ивотпочему.Скажем,MJ-кодIRP_MJ_CREATEможет посылатьсяприсозданиифайланадискеилиприсозданиисетевого соединения.Чтобысделатьчто-тополезное,получивконтрольнад IRP-пакетомстакимкодом,твойдрайвердолженобладатьопреде- леннойлогикой,позволяющейопределить,кудаименноискакой цельюпосылаетсяэтотIRP-пакет,авотэтоужедостаточногеморрно, новсёжеосуществимо.Всёделовтом,чтоприперехватеIoCallDriver внаширукипопадаетиуказательнаDeviceObject,распарсивкоторый,

можнодостаточноточноопределить,комупредназначаетсяIRP.Возьмем конкретныйпример.Причтениисканкодаклавиатурыпостекуподклю- ченных«устройствклавиатуры»прогоняетсяIRP-пакетскодомIRP_MJ_ READ.МыперехватываемIoCallDriver,получаемуказательнаIRP-пакет, MajorFunctionкоторогоравняетсяIRP_MJ_READ.Нокакопределить,что этотпакетименноотклавиатуры?Дляэтого,какяужеговорил,намнужнопроанализироватьтакжеперехваченныйуказательнаDeviceObject.В случаесклавиатуройэтоможносделатьследующимобразом.

Таккакустройстввстекеможетбытьнесколько,можно(каквариант)простопросмотретьназваниетехдрайверов,которыесоздалиэти устройства:

BOOLEAN IsKeybordDevice( DEVICE_OBJECT * topDevice )

{

UNICODE_STRING driverName = {0};

DEVICE_OBJECT * device = 0;

RtlInitUnicodeString( & driverName, L"\\Driver\\Kbdclass");

for (device = TopDevice;

device;

device = device->DeviceObjectExtension->AttachedTo)

{

if ( !RtlCompareUnicodeString(

&device->DriverObject->DriverName, &driverName, TRUE))

return TRUE;

}

return FALSE;

}

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Универсальныйфильтрw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

КлючевыммоментомдляфильтрацииявляетсяпарсингструктурыDeviceObject.Получивуказательнанее,мылегкополучимуказа- тельнапринадлежащийструктуреописательдрайвера—структуру DRIVER_OBJECT.Можнотакжепроанализироватьстек,гдебылпойманзахвостIRP,дляэтого,еслипомнишь,нужновызыватьIO_STACK_ LOCATION *stack = IoGetCurrentIrpStackLocation(pIrp).Далеепо указателюнастектыужесможешьпроанализироватьуказательна «объект-файл»—stack->FileObject.Тоестьтакимивотнехитрыми действиямитысможешьопределить,кудаикомупредназначается IRP-пакет,нуачтоснимделать—делосугуботвое.Так,например, можнозапретитьчтениекаких-либофайловcдиска.Ненужнописать сложныефильтрыфайловойсистемыдляэтого!

ДостаточноперехватитьIoCallDriver,организоватьфильтрацию IRP-пакетовскодомIRP_MJ_CREATE,поуказателюstack->FileObject получитьимяфайла:

OBJECT_NAME_INFORMATION *fileNameInformation = 0;

status = ObQueryNameString( stack->FileObject,

fileNameInformation, 1024, &retSize);

wcscat(fileNameInformation->Name.Buffer,

stack->FileObject->FileName.Buffer);

DbgPrint("file name now is: %ws \n", fileNameInformation->Name.Buffer);

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

ULONG CreateDisposition =

(stack->Parameters.Create.Options>> 24)& 0x000000ff;

if((CreateDisposition==FILE_CREATE)||

(CreateDisposition==FILE_OPEN_IF )||

(CreateDisposition==FILE_OVERWRITE_IF))

{

Irp->IoStatus.Status = STATUS_ACCESS_DENIED;

Irp->IoStatus.Information = 0;

IoCompleteRequest(Irp, IO_NO_INCREMENT);

ExFreePool(fileNameInformation);

return STATUS_ACCESS_DENIED;

}

СЛОЖНОСТИ

ВспособефильтрацииIRP-данных,основанномнаперехвате IoCallDriver,естьоднасерьезнаясложность.Деловтом,чтоIoCallDriver являетсячрезвычайноиспользуемойфункциейвядреWindows’а.Это связаносочевиднымфактом—функциятолькоиделает,чтоотправля- етIRP-пакетынаправо-налево,авзаимодействиемеждудрайверами- устройствамивядреоснованоименнонапосылкеIRP-пакетов.Правда, приобслуживаниифайловойсистемытакжеиногдаиспользуется механизмFastIO,нообэтомпозже.Еслипредставить,чтовсяэтакуча драйверовначинаетслатьдругдругуIRP-пакетыиихобрабатывать,то можнолегкопредставить,какуюнагрузкуиспытываетIoCallDriver— речьможетидтионесколькихсотняхвызовахвсекунду.Поэтомууста- новка/снятиеперехватанаIoCallDriver—делотонкоеилегкорушащее системуввидеBSOD’aскодомDRIVER_UNLOADED_WITHOUT_CANCELLING_ PENDING_OPERATIONS.Этопроисходитобычно,еслиначинаешьпри перехватенеоченьаккуратнообрабатыватьтеданные,которыенесутс собойIRP-пакеты.Спешупредостеречь:необходимымусловиемтвоего драйвера,которыйбудетперехватыватьIoCallDriver,должнабытьгра- мотнаяобработкаизавершениевсехпросмотренныхIRP-пакетов.

ЗАКЛЮЧЕНИЕ

Какоказывается,сделатьуниверсальныйинестандартныйфильтр довольнолегко.Аеслидостаточноопытакодингадрайверов,тосозданныйфильтрбудетобладатьещеитоликойнадежности.Возможностейпримененияуэтойнехитройтехникиоченьмного,азатратына реализациюминимальные—этохорошийстимулдляразвитиятемыв домашнихусловиях.Удачногокомпилирования,идапребудетстобой Сила! z

ХАКЕР 10/153/2011

099

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

КОДИНГm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

Всеволод Захаров (seva@vingrad.ru)w Click

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Ядромпо Макинтошу

ИЗУЧАЕМKERNEL-КОДИНГПОДMACOSX

РазработкаприкладныхпрограммподMacOSX иiOSотносительнонеплохоосвещенавлитературеиинтернете.Акакнасчеттемысоздания кода,которыйдолженвыполнятьсяврежиме ядра?Сэтим—глухо.Вэтойстатьемыпопробу- емданнуюнесправедливостьликвидировать.

Сеть

BSD

Файловые

системы

NKE

I/OKit

Mach

Драйверы

XNUвнутри

ЯДРОMACOSX

Дляначала—немноготеории,котораяпоможеттебеприсозданиирас- ширенийядра.MacOSXосновананаядреXNU(XNUisnotUNIX).XNU—это гибридноеядро,котороесостоитизнесколькихкомпонентов:подсисте- мыMach,BSDиобъектно-ориентированногофреймворкадлясоздания драйверовIOKit.

Mach—этомикроядро,созданноеврезультатеисследованийв университетеКарнегивсередине80-хгодов.Микроядернаяархитекту- ра,какизвестно,неплохосмотритсявпрезентациях,но,ксожалению, приводитксерьезнымпроблемамспроизводительностьюиотладкой. Видимо,поэтомупроектGNU/HURD,вкоторомсистемаGNUпостроенане намонолитномLinux,анамикроядре,всеещедалекотвыпускастабиль- нойверсии.Нет-нет,неволнуйся,проMacOSXмыподобноенескажем,и вовсенепотому,чтобоимсяместияблочныхфанатов:).Деловтом,чтов MacOSXподсистемаMachиспользуетсявнесколькоизмененномвиде: микроядроивсекомпоненты,которыеонодолжнообслуживать,собраны вединомадресномпространствемонолитногоядра.Такойход,содной стороны,позволяетиметьболееструктурированноеядро,асдругой— избавляетотпроблемсотладкойипроизводительностью.

Mach-кодвXNUотвечаетзабольшинствонизкоуровневыхфункций:

вытесняющаямногозадачность;

виртуальнаяпамять;

межпроцессноевзаимодействие;

100

ХАКЕР 10/153/2011

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

ЯдромпоМакинтошуw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

kextудачнособрался

прерывания;

консольныйввод-вывод.

ПодсистемаBSDядраMacOSXосновананакодеизFreeBSDиотвечаетзавыполнениеследующихзадач:

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

POSIXAPI,системныевызовыBSD;

TCP/IPBSD-сокеты;

файловыесистемы;

различныемеханизмысинхронизации;

поддержкуреальноговремени.

ДлявзаимодействиясоборудованиемвядреMacOSXприменяется объектно-ориентированныйфреймворкIOKit.Ониспользуетограни- ченноеподмножествоC++вкачествеязыкапрограммирования.Внем, например,отсутствуюттакиеэлементы,какисключения,множественноенаследование,шаблоныиRTTI. Каждыйтипсервисаядраили устройствапредставленвIOKitввидеклассаC++,акаждыйконкретный сервисилиустройство—ввидеобъектаэтогокласса. Корочеговоря, драйверIOKit—этообъект,которыйуправляетустройствомилишиной, представляяабстрактныйинтерфейскнимдлядругихчастейсистемы.

МОДУЛИЯДРА

MacOSXпредоставляетвозможностьрасширятьядроспомощью динамическойзагрузкимодулейядра,которыеназываютkext'ами(от kernelextension).ЕслитынесобираешьсяправитьисходникиXNU, тоединственнаявозможностьвыполнитькодврежимеядра—это написаниеизагрузкарасширения.Так,драйвераIOKitреализованы именноввидеkext’ов.Kext’ы—этобандлы,какиобычныепользова- тельскиеприложенияMacOSX,ионисодержат:

plist-файл,вкоторомописаносодержимоебандла,установкииза- висимостимодуля;

бинарныефайлымодуляядра—файлывMach-O-формате,вкоторых содержитсякод,подгружаемыйвадресноепространствоядра;

ресурсы—иконки,данныедлялокализацииит.п.

Когдаkextсоздан,загрузитьеговядроможноспомощьюкоманды kextloadизтерминала.Приэтомнеобходимоиметьправаадминистратора.Отлаживатьkext'ысложнеепользовательскихприложений.В первуюочередь,нужноразрешитьотладкуядра.Дляотладкиядра,

какводится,необходимоиспользоватьдвемашины:наоднойработа- етядро,анадругой—отладчик.ДляотладкииспользуетсяGDB.Когда kextотлажен,егоможноустанавливатьвсистему.Тогдазазагрузку расширенияпризапускесистемыбудетотвечатьkext-manager.Для этогобандлkext'анужнопоместитьв/System/Library/Extensions.

Присозданииkext'ов(какиостальногософта)вMacOSXиспользуется XCode.ПопробуемсоздатьпростоерасширениеядраспомощьюэтойIDE.

Дляэтогонамнужно:

1.ЗапуститьXCode:).

2.СоздатьновыйпроектисредишаблоноввыбратьGenericKernel Extension,посколькумыбудемсоздаватьпростоерасширениенаC, анеIOKit-драйвернаC++.

3.ВкачествеименипроектауказатьSampleKext(да,янастаиваю:)).

Еслитыправильнореализовалописанныйвышенехитрый алгоритм,товкачествеегологичногозавершениясистемасоздаст занасфайлSampleKext.c.Внембудутсодержатьсядвефункции: SampleKext_startиSampleKext_stop.Кактыужедогадался,этифункциивызываютсяпризагрузке/выгрузкерасширения.Вшаблонеэти функцииничегополезногонеделают,авреальномkext'еониотвечают

Выбираемшаблондляпроекта

ХАКЕР 10/153/2011

101

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

КОДИНГm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Подгружаемkextдлятестирования

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

SampleKext.c

#include <sys/systm.h>

#include <mach/mach_types.h>

kern_return_t MyKext_start (kmod_info_t * ki, void * d)

{

printf("Kext loaded.\n");

return KERN_SUCCESS;

}

kern_return_t MyKext_stop (kmod_info_t * ki, void * d)

{

printf("Kext unloaded.\n");

return KERN_SUCCESS;

}

Сборкапроектапослеэтогопройдетбезпроблем,ноеслитыпопробуешьзагрузитьSampleKext.kext,тоничегохорошегонеполучится, посколькумыещенепозаботилисьосодержимомplist-файла.Каждый kextобязательнодолженсодержатьInfo.plistвXML-формате.Посмотрим важныедляправильнойзагрузкирасширенияключивэтомфайле:

CFBundleIdentifier—идентификаторkext'а.Например,com.apple. driver.AppleUSBMergeNub.

CFBundleExecutable—бинарныйфайлkext'а.Егоможетинебытьв случаеIOKit-драйверов.

ДЛЯКОРРЕКТНОЙЗАГРУЗКИ KEXT'АДОСТАТОЧНО БУДЕТПОПРАВИТЬ OSBUNDLELIBRARIES

CFBundleVersion—версиябандла.

OSBundleLibraries—либы,откоторыхзависитkext.• IOKitPersonalities—объектыIOKit,длякоторыхнужноподгружать этотkext.Еслиэтодрайвер,конечно.

Посколькумысоздаемнедрайвер,аобычноерасширение ядра,длякорректнойзагрузкиkext'адостаточнобудетпоправить

OSBundleLibraries.Длятогочтобыавтоматомполучитьсписоквсехкомпонентов,откоторыхзависитрасширение,можноиспользоватьутилиту kextlibs.Запустивеесключом-xml,тысразуполучишьXML-код,который нужнодобавитьвplist-файл:

kextlibs -xml MyKext.kext

<key>OSBundleLibraries</key>

<dict>

<key>com.apple.kpi.libkern</key>

<string>9.2.2</string>

</dict>

Нашkextзависитвсегоотодногоэлемента.Скопируемэтоткодв Info.plist.Длятогочтобынашkextзаработал,осталсяодинважныйшаг: бандлkext'адолженпринадлежатьадминистратору.Длятестирования скопируемегов/tmpсправамирута:

sudo cp -R SampleKext.kext /tmp

Воттеперьможнозагружатьнашмодуль:

sudo kextload /tmp/SampleKext.kext

Послеэтогов/var/log/system.logтыможешьувидетьвыводнашего kext'a.Чтобывыгрузитьkext,используемkextunload:sudokextunload/ tmp/SampleKext.kext.

OUTRO

МысоздалипростейшиймодульядрадляMacOSX.Все,чтоонумеет насегодня,—этокорректнымобразомзагружатьсяивыгружаться.В идеалежемодулиядрамогутиспользоватьсянетолькодлясоздания драйверов,нои,например,длявынесениячастоиспользуемоймногимиприкладнымипрограммамифункциональностивсервисядра. Удачногоkernel-кодинга!z

102

ХАКЕР 10/153/2011

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

КОДИНГm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

СергейКононенко(kononencheg@gmail.com)w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

JavaScript

длясервера

ПИШЕМСЕРВЕР МГНОВЕННЫХ СООБЩЕНИЙ НАNODE.JS

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

вдивахиспанах,атысячи

итысячивходящихзапросов.Мысегоднянетолько

представим,ноиразработаемнаJSсвойсервер!

LINKS

 

INFO

 

nodejs.org—сайт

Масштабируемость—

проекта,здесьже

этобольнойвопрос

документацияи

дляNode.js.

примеры.

Основнаяпроблема—

 

 

взаимодействие

 

 

междупроцессами.

 

 

Ноестьизящное

 

 

решениеспомощью

 

 

publish/subscribe-

 

 

взаимодействия,

 

 

использующегобазу

 

 

данныхRedis.Воз-

 

 

можно,мыобэтомеще

 

 

напишем.

104

ХАКЕР 10/153/2011

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

ЧТОТАКОЕNODE.JS

Раньшедлясозданиякастомногосерверанеобходимобылоприложить немалоусилий,аразработкаасинхронноймоделиуправлениявводом ивыводомдоставляланемалопроблем.ДажеPythonсTwistedспасал слабо.ОднакоспоявлениемNode.jsзадачаневероятноупростилась.

Еслиговоритьвикипедийно,тоNode.js—этособытийно- ориентированнаяI/OсервернаясредадляJavaScript.Агрубоговоря, специальныйJavaScript-фреймворк,позволяющийделатьвещи,в общем,дляяваскриптанехарактерные,вродеработысфайловой системой,процессамиилисозданияHTTP-серверовбезовсякихбрау- зеровивлюбомокружении.

Чтобыбылопонятнее,поcмотринакод,которыйсоздастwebсервер,здоровающийсясомной:

var http = require('http'); http.createServer(function(request, response) {

response.writeHead(200, {'Content-Type': 'text/plain'});

response.end('Привет, Кононенка!'); }).listen(8080, '127.0.0.1');

console.log('Сервер запущен http://127.0.0.1:8080/');

Авоттакмыегозапустим:

%node имя_файла_скрипта.js

Сервер запущен http://127.0.0.1:8080/

Понятноедело,чтовместоприветствияможновыдавать<html> <head><title>...итакдалее.;)Кромебыстройотдачизабавныхприветствийистатичнойверсткиможнореализоватьпрактическичтоугодно! «Какойбыстрой?Яваскрипт—этожемедленно!»—вспомнишь

тыибудешьотчастиправ.Ноитутямогутебяпорадовать!Вкачестве JavaScript-движкавNodeиспользуетсянепревзойденныйгугловскийV8, амодельввода/выводавовнутренностяхисключительноасинхронная. Вместеэтодаетблестящуюскорость,достойнуювысоконагруженного сервиса.Доказательствомэтогоявляетсято,чтоименнонаNodeуже успешнореализовансерверобменамгновеннымисообщениямина «ВКонтакте»,аужгде-где,атамнагрузкаого-го.Кслову,немногопохо- жийсервермыинапишем,новначаленемногомануальнойтеории.

НОВОВВЕДЕНИЯВNODE

Первое,чтомоглоброситьсявглазавприведенномвышескрипте,— этоrequire.Нодавсеинтересненькоеиготовенькое(тоестьпочтивсе) держитвспециальныхмодулях.Именноихиподгружаетглобальныйэтот метод.ЧастьмодулейнаписанынаС++слибамиV8,частьнепосредствен- нонаJavaScript’е.СамостоятельноJS-модульсделатьоченьпросто:

Наш модуль circle.js

exports.area = function (r) {

Взглядсосторонысервера

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

JavaScriptдлясервераw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Взглядсостороныклиента

return Math.PI • r • r; };

exports.circumference = function (r) {

return 2 • Math.PI • r;

};

Здесьвстречаетсявторойглобальныйобъект—exports.Внутри своегомодулямыопределяемвнемлюбыесвоиметоды,которые послебудутдоступныизвне(areaиcircumference—впримере).Вос- пользуемсяэтиммодулем:

var circle = require('./circle.js');

console.log( 'Площадь круга с радиусом 4 равна %d', circle.area(4));

Чтобывывестиплощадькруганаэкран,мыпользуемсятретьим глобальнымобъектом—console,предназначеннымдлявыводав stdoutиstderr.Методlog()—этоprintf-подобныйметод(Классический форматированныйвыводвJavaScript—этосчастье.—Прим.ред.) длявыводавstdout,методerror()—тожесамоедляstderr.Последний интересныйглобальныйобъект—этоprocess,дающийнамполный контрольнадисполнениемтекущегопроцесса.Воттольконекоторые изегометодовиобъектов,которыесовершенноточнопригодятсяпри болееилименеесерьезномпрограммированиисервера:

process.stdout,process.stderrиprocess.stdin—потокистан- дартноговвода/вывода.Дляпервыхдвухметодwriteвруки,и получитсяconcole.logиconsole.error,авотметодstdin—этоуже интереснее.Node—платформасобытийно-ориентированная, асинхронная.Поэтомудляполучениястандартноговводаопределяетсяпростаяфункцияксобытию:

process.stdin.on('data', function (chunk) {

// печатаем полученный символ

process.stdout.write('data: ' + chunk);

});

Функции,которыеуказываютсявкачествеаргумента,—это callback-функции.Онисрабатываютпринекоторомсобытии.В этом,кстати,ивсясутьсобытийнойориентированности,ноэтоты всепрекраснознаешь.

process.argv—массивсаргументамикоманднойстроки,заданны- мипризапуске.

// печатаем аргументы

process.argv.forEach(function (val, index, array) {

console.log(index + ': ' + val);

});

ХАКЕР 10/153/2011

105

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

КОДИНГm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

process.cwd()—рабочаядиректорияпроцесса.

process.pid,process.getgid()process.getuid()—текущийidпроцес- са,атакжеgidиuid.

process.kill(pid,signal='SIGTERM')—убьетзаданныйпроцесс.

process.exit(num)—завершиттекущийпроцесс.Кстати,процесс завершенияможноотследить,зарегистрировавсоответствующее событие:

process.on('exit', function () { console.log('Я таю... таю...'); });

Прочиеглобальныеобъектыиихметодыуженетакиеинтересные, заточтоинтересно,такэтомодули,которыеможноприкрутитькNode.

Кэтомумоментутыдолженсовершенноточнопонять,чтоNode.js

этонереальнокруто.Я,например,вспомнил,чтовсюжизньтолько имечтал,чтонаписатьсвойвеб-сервер!Ибезкаких-нибудьтам унылыхCGI-скриптов,ассокетами,пустьдаже«веб»!Теперьэтолегко реализуемо.

БИБЛИОТЕКИДЛЯЭМУЛЯЦИИ СОКЕТНОГОСОЕДИНЕНИЯ

Мгновенностьсообщенийнашегосерверабудетзаключатьсявтом, чтоданныемеждупользователямибудутпередаватьсябеззадержек встилеreal-timeweb.Обеспечиватьсяэтобудетспомощьюэмуляции постоянногосоединениясбраузером.Самаясовершеннаятехнология, постоянноедвухстороннеесоединениесбраузером,ксожалению, поддерживаетсятольконовымибраузерами.Анашбудущийсервер долженсуметьдатьнастоящийреалтайм-веблюбомуклиенту!Для созданияреалтайм-веб-приложенийсуществуетмногоразличных решений,нодлянашегослучаяихвсегоничего:

ОченьпопулярнаябиблиотекаSoket.IOпорадуеткрасивымдизайномглавнойстраницыпроектаhttp://socket.io/иприличным количествомплюшекисвистелок.Онаобещаеточеньмногоесделатьзавас.Иона,конечно,делает.Поддерживаеткучуспособов соединения:отновыхвеб-сокетовдовечныхайфреймов.Новая версияпозволитгенерироватьсерверныесобытия,сопровождаемыеданными.ВСетилегкогуглитсякучатуториаловипримеров, описывающихто,какэтабиблиотекаудобнаикрута.

Гораздоменеепопулярна(азря!)библиотекаBeseda,которая делаетпримернотожесамое,однакоимееттолькорепозитарийна гитхабе(goo.gl/9SoJR).ОнаподдерживаетгибкийпротоколBayeux итриспособасоединенияссервером.

Достоинствомобеихбиблиотекявляетсято,чтоихдействительно легкоиспользовать.Правда,Soket.IOплохоработаетсовсемиспосо- бамиподдержкисоединения,кромевеб-сокетов.Такжеслучаются утечкипамятиинеобъяснимыепадениясервераприотносительно небольшомколичествеклиентов(более5000).АвотBesedaбезвеб- сокетовнеплохосправляетсяис20000клиентов.Даисвеб-сокетами онаработаетпошустрее.

НАПИШЕМЛУЧШЕ!

Чтобылучшевъехатьвновуютехнологию,всегдаполезноизобрести велосипед.Аособеннохорошо,есливелосипедбудетбыстрый.

КакпоказалмойопытработысNode.jsиJavaScript,чемпроще— тембыстрее.Поэтомудажедляреальногоприменениясвоябиблиотекаможеточеньдажесгодиться.Состоятьнашабиблиотекабудет изодногофайлаio.js.Адляпроверкииотладкиещедва—сценарий сервераserver.jsистраницаклиентаindex.html.

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

После долгих и упорных ковыряний в исходниках, отладки и профилирования Soket.IO причина падений и тормозов, о которых я говорил выше, была найдена! Это тайм-ауты, обрабатывающие

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

СТАНДАРТНЫЕМОДУЛИNODE

абормодулейNode.jsудовлетворяетпотребностипринаписа-

Ннииоченьбольшогоколичествасофта. Смотрисам:

fs—модульдляработысфайловойсистемой.Естьметодыдлялюбых операций,отчтенияизаписидосозданиясимволическихссылок.

fs.readFile('/etc/passwd', function (err, data) { if (err) throw err;

console.log(data);

});

crypto—криптографическиеметоды:хэшированиелюбымиал- горитмами(sha1,md5,sha256,sha512ит.п.),конвертацияданных и,собственно,любоешифрование.

net—модульдляасинхроннойработысTCP.

var net = require('net');

var server = net.createServer(function (c) { c.write('hello\r\n');

c.pipe(c);

});

server.listen(8124, 'localhost');

dgram—дляасинхроннойработысUDP-идругимидатаграмны- мипротоколами.

events—дляреализациисвоейсобытийнойсистемы.

util—наборполезныхметодов:форматированиестроки,при- нудительноенаследование.

tls—имплементацияOpenSSL,тоестьудобныеSSL-соединения длятвоихпрограмм.

vm—виртуальнаяJavaScript-машина,т.е.модульдлязапускаи компиляции(evalэтогонеумеет)js-скриптоввнутрисвоейпро- граммы.

var localVar = 123, usingscript, evaled, vm = require('vm');

usingscript = vm.runInThisContext('localVar = 1;', 'myfile.vm');

console.log('localVar: ' + localVar + ', usingscript: ' + usingscript);

//localVar: 123, usingscript: 1

dns—резолвинглюбыхзаписей(MXит.п.).

http,https—добротныеибыстрыереализацииweb-серверов.

child_process—работасдочернимипроцессами,форк,exec, spawnит.п.

os—все,чтонужноотоперационнойсистемы.

Естьещекучаистандартныхмодулей,инестандартных,написан-

ныхстороннимиразработчиками:отмодулейдляработыMySQLдо полноценныхвеб-фреймворков.

ВКАЧЕСТВЕJS-ДВИЖКА

ВNODEИСПОЛЬЗУЕТСЯ НЕПРЕВЗОЙДЕННЫЙ ГУГЛОВСКИЙV8

106

ХАКЕР 10/153/2011

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

JavaScriptдлясервераw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Процессразработки

каждоесоединениесклиентом.Каждыйасинхронныйцикл,под- держивающийлогикуобработкиlong-polling-соединения,естмно- горесурсов.Поэтомунадопостаратьсяужатьвсеводинцикл.Для этогокаждоесоединениепредставим«плоским»наборомданных, которыйбудетсодержатьинформациюотом,вкакоймоментего необходимообнулить,отправивсодержимоеклиенту.Дляэтогов файлеio.jsнапишемследующийкод:

//Интервал проверки наличия новых данных var CHECK_INTERVAL = 1000;

//Максимальньное количество итераций перед

//обязательным сбросом

var MAX_LOOP_COUNT = 10;

//Таблица соединений var connections = {};

//Данные соединения

var LongPollingData = function() { this.loopCount = MAX_LOOP_COUNT;

this.dataQueue = [];

this.response = null; };

// Итерация основного цикла

function mainLoopIteration() {

var pollingData;

for (var id in connections) {

pollingData = connections[id];

if (pollingData.response) { // Если клиент подключен

pollingData.loopCount--;

// ...и имеет данные либо долго висит без дела

if (pollingData.dataQueue.length

|| pollingData.loopCount === 0)

flush(pollingData); // ...сбрасываем его

}

}

}

function flush(pollingData) {

pollingData.response.end(pollingData.dataQueue.join('|'));

pollingData.dataQueue = [];

pollingData.response = null;

}

setInterval(mainLoopIteration, CHECK_INTERVAL);

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

запросданных,атакжезаписыватьданныевсоединение.

var lastID = 0; // Идентификатор последнего соединения // Регистрация клиента и возвращение его идентификатора var init = exports.init = function(request, response) {

var id = 'connection_' + ++lastID;

connections[id] = new LongPollingData();

response.end(id);

};

ХАКЕР 10/153/2011

107

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

КОДИНГm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

// «Удержание» запроса данных соединения

var hold = exports.hold = function(id, request, response) { var pollingData = connections[id];

if (pollingData.response !== null)

flush(pollingData);

pollingData.response = response;

pollingData.loopCount = MAX_LOOP_COUNT; }; // Запись данных в соединение

var write = exports.write = function(id, data) { var pollingData = connections[id];

pollingData.dataQueue.push(new Buffer(data.toString()));

}

// Запись данных во все соединения

var broadcast = exports.broadcast = function(data) { for (var id in connections) write(id, data);

}

Чтобывоспользоватьсянашейбиблиотекой,используемуже знакомоесловоrequire:

var io = require('./mycoollibrary/io.js'); io.broadcast('прекрасное сообщение');

Теперьдавайпопробуемнашубиблиотекуприменитькасательно основнойцелистатьи,тоестьдавайуженапишемсервермгновенных сообщений.Вфайлеserver.jsзапишем:

var http = require('http'),

fs = require('fs'),

io = require('./io.js'); // наша!

var server = http.createServer();

server.addListener('request', handleRequest);

server.listen(80, 'localhost');

function handleRequest(request, response) {

if (request.method === 'POST') {

io.init(request, response);

} else {

if (request.url === '/' || request.url === '/index.html') {

fs.readFile('./index.html', function (err, content) {

response.end(content);

});

} else {

io.hold(request.url.split('/').pop(), request, response);

}

}

}

// Кто-то всех часто оповещает! setInterval(function() { console.log("Отсылаю сообщение!");

io.broadcast("Сообщение для всех!"); }, 500);

POST-запросбудетвосприниматьсянашимсерверомкакрегистра- цияновогоклиента,аGET-запросвида«/идентификатор_клиента» будетвосприняткак«долгоиграющий»запрос.Нуадляэмуляции интерактивностикаждые500миллисекундбудетпосылатьсясообщениевсемпользователям.

Отклиентаожидается,чтоонбудетрегистрироваться,держать соединениеипереподключаться.Иникакихспециальныхклиентских библиотек.Что,кстати,тожеможнорасценитькакнедостатокописанныхвышебиблиотек.

Клиентскийкоднапишемпрямовindex.html(заранееподключив jQuery):

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ТИПЫПОСТОЯННОГОСОЕДИНЕНИЯ СБРАУЗЕРОМ

аданныймоментсуществует

Нпятьспособовподдержкипостоянного соединениясбраузером:

WebSocket’ы—возможностьновыхбраузеровподдерживатьсо- кетноесоединениепопротоколуHTTP.

FlashSocket’ы—эмуляцияWebSoket’овприпомощиспециальной флешки.Способвозможентолькоприналичиифлеш-плеера.

Longpolling—«долгоиграющие»запросы.Способ,прикоторомна обычныйHTTP-запроссервернеспешитотвечать,аудерживаетего какое-товремядопоявленияновыхданных,послечегоотвечаети закрываетсоединение.Такжевозможенвариантсjsonp-форматом ответа.Способподдерживаетсявсемибраузерами.

Multipartstreaming—дозаписывающийсяответсервера.HTTP- запрос,которыйможетчитатьсянезакрываясь.Поддерживается толькоFirefox’ом.

ForeverIframe—вечныйайфрейм,вкоторыйдописываютсяновые порцииданных.Способподдерживаетсявсемибраузерами.

<script>

var conectionID;

function connect() {

$.post("http://localhost/", function(data) {

conectionID = data;

poll();

});

}

function handleData(data) {

data = data.split('|');

while(data.length > 0)

$('body').append(data.shift() + '\n');

poll();

}

function poll() {

$.get("http://localhost/" + conectionID, handleData);

}

connect();

</script>

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

ВЫВОДЫ

Можновполнепонять,почемуNode.jsтакбыстронабираетпопулярность,сообществорастет,абиблиотекиплодятсякакхомячки.

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

108

ХАКЕР 10/153/2011

Соседние файлы в папке журнал хакер