Эффективное программирование TCP-IP

       

Реализация в Windows


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

#include <windows.h>

HANDLE  CreateMutex( LPSECURITY_ATTRIBUTES  Ipsa,

BOOL  flnitialOwner,   LPTSTR  IpszMutexName  );

Возвращаемое значение: описание мьютекса в случае успеха, NULL - в случае ошибки.

Параметр lpsa - это указатель на структуру с атрибутами защиты. Здесь эта возможность не нужна, так что вместо этого аргумента передадим NULL. Параметр flnitialOwner означает, будет ли создатель мьютекса его начальным владельцем, то есть следует ли сразу заблокировать мьютекс. Параметр lpszMutexName -эхо имя мьютекса, по которому к нему могут обратиться другие процессы. Если мьютекс уже существует, то CreateMutex просто вернет его описание.

Блокировка и разблокировка мьютекса выполняются соответственно с помо­щью функций WaitForSingleObject и ReleaseMutex.

#include  <windows.h>

DWORD WaitForSingleObject( HANDLE hObject, DWORD dwTimeout );

Возвращаемое значение: WAIT_OBJECT_0 (0) в случае успеха, ненулевое значение - в случае ошибки.

BOOL  ReleaseMutex(   HANDLE  hMutex );

Возвращаемое значение: TRUE в случае успеха, FALSE - в случает ошибки.

Параметр hObject функции WaitForSingleObject - это описание ожидаемого объекта (в данном случае мьютекса). Если объект, заданный с помощью hObject, не занят (signaled), то WaitForSingleObject занимает его и возвращает управление. Если же объект занят (not signaled), то обратившийся поток переводится в состояние ожидания до тех пор, пока объект не освободится. После этого WaitForSingleObject переведет объект в занятое состояние и вернет в работу «спящий» поток. Параметр dwTimeout задает время (в миллисекундах), в течение которого потоком ожидается освобождение объекта. Если тайм-аут истечет прежде, чем объект освободится, то WaitForSingleObject вернет код WAIT_TIMEOUT. Таймер можно подавить, задав в качестве dwTimeout значение INFINITE.




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

В Windows вы получаете сегмент разделяемой памяти, отображая файл на па­мять каждого процесса, которому нужен доступ к разделяемой памяти (в UNIX есть аналогичный системный вызов mmap). Для этого сначала создается обычный файл с помощью функции CreateFile, затем - отображение файла посредством вызова CreateFileMapping, а уже потом оно отображается на ваше адресное про-странство вызовом MapViewOf File.

Параметр hFile в вызове CreateFileMapping - это описание отображаемого файла. Параметр lpsa указывает на структуру с атрибутами безопасности, которые в данном случае не нужны. Параметр fdwProtect определяет права доступа к объекту в памяти. Он может принимать значения PAGE_READONLY, PAGE_READWRITE или E_WRITECOPY. Последнее значение заставляет ядро сделать отдельную копию данных, если процесс пытается записывать в страницу памяти. Здесь используется PAGE_READWRITE, так как будет производится и чтение, и запись в разделяемую память. Существуют также дополнительные флаги, объединяемые операцией побитового OR, которые служат для управления кэшированием страниц памяти но они не понадобятся. Параметры dwMaximumSizeHigh и dwMaximumSizeLowB совокупности дают 64-разрядный размер объекта в памяти. Параметр lpszMapNaitie -это имя объекта. Под данным именем объект известен другим процессам.

#include <windows,h>

HANDLE CreateFileMapping( HANDLE hFile, LPSECURITY_ATTRIBUTES lpsa/ DWORD  fdwProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPSTR IpszMapName );

Возвращаемое значение: описатель отображения файла в случае успеха, null -в случае ошибки.

LPVOID MapViewOfFile (   HANDLE hFileMapObject,   DWORD dwDesiredAccess, DWORD  dwFileOffsetHigh,   DWORD dwFileOffsetLow, DWORD  dwBytesToMap );

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



После создания объект в памяти отображается на адресное пространство каж­дого процесса с помощью функции MapViewOfFile. Параметр hFi1eMapObj - это описание, возвращенное после вызова CreateFileMapping. Требуемый уровень доступа следует задать с помощью dwDesiredAccess. Этот параметр может принимать следующие значения: FILE_MAP_WRITE (доступ на чтение и запись), FILE_MAP_READ (доступ только на чтение), FILE_MAP_ALL_ACCESS (то же, что FILE_MAP_WRITE) и FILE_MAP_COPY. Если присвоено последнее значение, то при попытке записи создается отдельная копия данных. Параметры dwFileOffsetHigh и dwFileOffsetLow задают смещение от начала файла, с которого следует начинать отображение. Нужно отобразить файл целиком, поэтому оба параметра будут равны 0. Размер отображаемой области памяти задается с помощью параметра dwBytesToMap.

Подробнее использование мьютексов и отображение памяти в Windows рассматриваются в книге [Richter 1997].

Теперь можно представить версию init_smb для Windows. Как видно из листинга 3.31, она очень напоминает версию для UNIX.

Листинг 3.31. Функция init_smb для Windows

1     #define FILENAME  "./smbfile"

2     #define lock_buf () if ( WaitForSingleObject ( mutex, INFINITE

3     ! = WAIT_OBJECT_0 )

4     error ( 1, errno, "ошибка вызова lock_buf " )

5     #define unlock_buf()  if ( !ReleaseMutex( mutex ) }

6     error( 1, errno, "ошибка вызова unlock_buf" )

7     HANDLE mutex;

8     void init_smb( int init_freelist )

9     {

10    HANDLE hfile;

11    HANDLE hmap;

12    int i;

13    mutex = CreateMutex ( NULL, FALSE, "smbmutex" );

14    if ( mutex == NULL )

15      error( 1, errno, "ошибка вызова CreateMutex" );

16    hfile = CreateFile( FILENAME,

17    GENERIC_READ | GENERIC_WRITE,

18    FILE_SHARE_READ | FILE_SHARE_WRITE,

19    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL };

20    if ( hfile == INVALID_HANDLE_VALUE )

21      error( 1, errno, "ошибка вызова CreateFile" );



22    hmap = CreateFileMapping( hfile, NULL, PAGE_READWRITE,

23      0, NSMB * sizeof( smb_t ) + sizeof( int ), "smbarray" );

24    smbarray = MapViewOfFile( hmap, FILE_MAP_WRITE, 0, 0, 0 );

25    if ( smbarray == NULL )

26      error( 1, errno, "ошибка вызова MapViewOfFile" );

27

28    if   (   init_freelist   )

29    {

30      for    (   i   =   0;    i   <  NSMB   -   1;   i++   )

31       smbarrayt   i   ].nexti  =  i  +  1;

32      smbarray [ NSMB  -   1 ].nexti  =  -1;

33      FREE_LIST  =   0;

34    }

35    }

Для тестирования всей системы следует написать небольшие программы клиентской (листинг 3.32) и серверной (листинг 3.33) частей.

Листинг 3.32. Клиент, использующий систему буферов в разделяемой памяти

1    #include   "etcp.h"

2    int main(   int  argc,   char  **argv  )

3    {

4    char  *bp;

5    SOCKET  s;

6    INIT();

7    s  =  tcp_client(   argv[   1   ],   argv[   2   ]   );

8    init_smb(   FALSE   );

9    bp = smballoc();

10   while ( fgets( bp, SMBUFSZ, stdin ) != NULL  )

11   {

12     smbsend( s, bp );

13     bp = smballocO;

14   }

15   EXIT(   0   ) ;

16   }

Листинг 3.33. Сервер, использующий систему буферов в разделяемой памяти

1    #include   "etcp.h"

2    int main(   int  argc,   char  **argv  )

3    {

4    char  *bp;

5    SOCKET  s;

6    SOCKET  s1;

7    INIT();

8    init_smb( TRUE );

9    s   =   tcp_server(   NULL, argv[   1   ]    );

10   s1   =   accept(   s,   NULL,   NULL   );

11   if   (   !isvalidsock(   s1 )   )

12     error (   1,   errno,   "ошибка вызова accept"   )

13   for    (    ;;    )

14   {

15     bp = smbrecv( s1 );

16     fputs( bp, stdout );

17     smbfree( bp );

18   }

19   EXIT( 0 );

20   }

Запустив эти программы, получите ожидаемый результат:

bsd: $ smbc  localhost  9000

Hello

Wolds!

^C

bsd:    $

bsd: $ smbs  9000

Hello

Wolds!

^C

bsd:    $

Обратите внимание, что smbc читает каждую строку из стандартного ввода прямо в буфер в разделяемой памяти, a smbs копирует каждую строку из буфер3 сразу на стандартный вывод, поэтому не возникает лишнего копирования данных.


Содержание раздела