Разработка приложения. Инициализация Winsock . Структура серверного и клиентского приложений — КиберПедия 

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...

Разработка приложения. Инициализация Winsock . Структура серверного и клиентского приложений

2019-09-17 318
Разработка приложения. Инициализация Winsock . Структура серверного и клиентского приложений 0.00 из 5.00 0 оценок
Заказать работу

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

Хотя как уже говорилось выше, сокеты позволяют работать со множеством протоколов и являются удобным средством межпроцессорного взаимодействия, но в данной работе речь будет идти только о сокетах семейства протоколов TCP/IP, использующихся для обмена данными между узлами сети Интернет.

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

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

Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые на TCP.

    Перед вызовом любой функции WinSock необходимо загрузить правильную версию библиотеки. Функция инициализации WinSock – WSAStartup:

    Int WSAStartup(WORD wVersion, LPWSADATA lpWSAData);

Первый параметр – версия требуемой библиотеки. На современных платформах используется версия 2.2. Для её загрузки необходимо либо указать значение 0х0202, либо макрос MAKEWORD(2,2).

    Второй параметр – структура WSADATA, возвращаемая по завершению вызова. Она содержит информацию о версии загруженной библиотеки.

    Индивидуальные поля стуктуры таковы:

wVersion – версия, вызываемой WinSock,

wHighVersion – высшая версия, поддерживаемая загруженной библиотекой,

szDescription – текстовое описание загруженной библиотеки,

szSystemStatus – текстовая строка о состоянии или конфигурации,

iMaxSockets - максимальное количество сокетов (для версий 2 и выше пропускается),

iMaxUdpDg – максимальный размер дейтограммы,

lpVendorInfo – информация об изготовителе (для версий 2 и выше пропускается).

    По завершению работы с библиотекой необходимо вызвать функцию int WSACleanup(void) для выгрузки библиотеки и освобождения ресурсов. Для каждого вызова WSAStartup() необходимо согласованно вызывать WSACleanup(), так как каждый стартовый вызов увеличивает значение эталонного счетчика ссылок на загруженные DLL. Чтобы уменьшить значение счетчика, требуется равное количество вызовов WSACleanup().

    Вторая версия полностью совместима с предыдущими версиями библиотек WinSock.

Структура приложения WinSock для серверной и клиентской частей содержит как общие, так и отличные функции и представлена далее.

1. Для работы с библиотекой Winsock 2.х в исходный тест программы необходимо включить директиву "#include <winsock2.h>", а в командной строке линкера указать "ws2_32.lib".

2. Создание объекта "сокет". Сокет – это описатель поставщика транспорта, в Win32 сокет отличается от описателя файла и поэтому представлен отдельным типом SOCKET. Сокет создается одной из двух функций SOCKET socket (int af, int type, int protocol) или SOCKET WSAsocket (int af, int type, int protocol, LWSAPROTOKOL_INFO lpProtocoInfo, GROUP g, DWORD dwFlags).

3. Сервер: прежде, чем сервер сможет использовать сокет, он должен связать его с локальным адресом. Локальный, как, впрочем, и любой другой адрес Интернета, состоит из IP-адреса узла и номера порта. Если сервер имеет несколько IP адресов, то сокет может быть связан как со всеми ними сразу (для этого вместо IP-адреса следует указать константу INADDR_ANY, равную нулю), так и с каким-то конкретным одним.

Связывание осуществляется вызовом функции int bind (SOCKET s, const struct sockaddr FAR* name, int namelen).

4. Сервер: выполнив связывание, потоковый сервер переходит в режим ожидания подключений или прослушивания, вызывая функцию int listen (SOCKET s, int backlog).

5. Сервер: установление соединения с клиентом осуществляется функцией SOCKET accept(SOCKET s, struct sockaddr FAR* name, int *addrlen) или SOCKET WSAAccept(SOCKET s, struct sockaddr FAR* name, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD dwCallbackData).

6. Клиент: инициирование соединения с сервером осуществляется функциями int connect(SOCKET s, const struct sockaddr FAR* name, int namelen) или int WSAConnect(SOCKET s, const struct sockaddr FAR* name, int namelen, LPWSBUF lpCallerData, LPWSBUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS).

7. Передача и прием данных осуществляется с помощью функций int recv(SOCKET s, const char FAR * buf, int len, int flags)Б int send(SOCKET s, const char FAR * buf, int len, int flags)) или расширенными  int WSAsend(), int WSArecv().

Основные функции WinSock

Структура WSADATA

В структуре WSADATA содержится информация о реализации Windows Sockets.

typedef struct WSAData {

WORD wVersion;

WORD wHighVersion;

char szDescription[WSADESCRIPTION_LEN+1];

char szSystemStatus[WSASYS_STATUS_LEN+1];

unsigned short iMaxSockets;

unsigned short iMaxUdpDg;

char FAR* lpVendorInfo;

} WSADATA,

 *LPWSADATA;

 

 

Параметры:

wVersion

Указывается версия Windows Sockets спецификации Ws2_32.dll. Старший байт определяет младшее число версии; байт младшего разряда определяет главное число версии.

wHighVersion

Указывается самая высокая версия Windows Sockets, спецификации Ws2_32.dll, которая может поддерживаться. Старший байт определяет младшее число версии; байт младшего разряда определяет главное число версии.

Это тоже самое значение параметра wVersion, которое требуется в параметре wVersionRequested, который передают в функции WSAStartup - самая высокая версия спецификации Windows Sockets, которую Ws2_32.dll может поддерживать.

szDescription terminated

NULL - законченный ASCII строки, в который Ws2_32.dll копирует описание выполнения Windows Sockets. Текст (до 256 символов в длине) может содержать любые символы, кроме символов форматирования и контроля. Наиболее вероятное использование - это показ в статусе сообщения.

szSystemStatus

NULL - законченный ASCII строки, в который Ws2_32.dll копирует соответствующий статус или информацию о конфигурации. В Ws2_32.dll следует использовать этот параметр, только если информация может быть полезной для пользователей. Этот параметр не следует рассматривать как расширение параметра szDescription.

iMaxSockets

Максимальное число сокетов, которые могут быть открыты. Этот член должен игнорироваться для версии 2 Windows Sockets и более поздних.

Параметр IMaxSockets сохранен для совместимости со спецификацией 1.1 Windows Sockets, но не должен использоваться при разработке новых приложений.

iMaxUdpDg

Максимальный размер дейтаграммного сообщения. Этот параметр игнорируется для Windows Sockets версии 2 или более поздней.

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

lpVendorInfo

Указатель, зависящий от фирмы изготовителя. Этот параметр следует игнорировать для Windows Sockets версии 2 или более поздней.

Параметр lpVendorInfo сохраняется для совместимости с Windows Sockets спецификации 1,1.

Функция WSAStartup

int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData);

В параметре wVersionRequested указывается версия интерфейса Windows Sockets, необходимая для работы приложения. Старший байт параметра указывает младший номер версии (minor version), младший байт - старший номер версии (major version).

Перед вызовом функции WSAStartup параметр lpWSAData должен содержать указатель на структуру типа WSADATA, в которую будут записаны сведения о конкретной реализации интерфейса Windows Sockets.

В случае успеха функция WSAStartup возвращает нулевое значение. Если происходит ошибка, возвращается одно из следующих значений:

Значение                                                          Описание

WSASYSNOTREADY           Сетевое программное обеспечение не готово для работы

WSAVERNOTSUPPORTED  Функция не поддерживается данной реализацией интерфейса Windows Sockets

WSAEINVAL                        Библиотека DLL, обеспечивающая ин-терфейсe Windows Sockets, не соответствует версии, указанной приложением в параметре wVersionRequested

Функция WSACleanup

int WSACleanup (void);

Эта функция может возвратить нулевое значение при успехе или значение SOCKET_ERROR в случае ошибки.

Для получения кода ошибки можно воспользоваться функцией WSAGetLastError.

Функция socket

Эта функция используется для создания сокета. Прототип:

SOCKET socket (int af, int type, int protocol) 

Первый параметр определяет семейство адресов протоколов. Мы будем рассматривать только сокеты AF_INET для TCP\IP.

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

1. SOCK_STREAM - при этом типе связи поступающим в канал байтам информации гарантируется "доставка" в порядке их поступления; пока непрерывный поток байтов не прекратится, никакие другие данные приниматься каналом не будут (аналогом такой связи является pipe-механизм);

2. SOCK_DGRAM - этот тип связи используется для посылки отдельных пакетов информации, называемых datagrams; при этом не гарантируется, что пакеты будут доставлены на место назначения в порядке поступления, а в действительности не гарантируется, что они все вообще будут доставлены (пример такого типа связи - обычная почтовая связь).

Третий параметр позволяет программисту выбрать нужный протокол для канала связи. Если этот параметр равен нулю, ОС выберет нужный протокол автоматически.

Функция socket возвращает описатель сокета (который можно использовать, например, в функциях read и write аналогично файловому дескриптору). Если же сокет по каким-либо причинам не был создан (например, очень много открытых файлов), возвращается ошибка.

Функция bind

    После создания сокета определённого протокола функция bind связывает его со стандартным адресом.

int bind (SOCKET s, const struct sockaddr FAR* name, int namelen).

    Первый аргумент - сокет, ожидающий соединения клиента. Второй аргумент - указатель на адрес структуры SOCKADDR_IN. Последний параметр – размер передаваемой структуры адреса, зависящей от протокола.

Функция listen

Функция listen используется сервером, чтобы информировать ОС, что он ожидает ("слушает") запросы связи на данном сокете. Без такой функции всякое требование связи с этим сокетом будет отвергнуто.

int listen (SOCKET s, int backlog).

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

Функция accept

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

SOCKET accept(SOCKET s, struct sockaddr FAR* name, int *addrlen)

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

Функция connect

Функция connect используется процессом-клиентом для установления связи с сервером.

int connect(SOCKET s, struct sockaddr FAR* name, int namelen);

Первый аргумент –действительный сокет для установления соединения. Второй аргумент - указатель на адрес сервера (структура SOCKADDR_IN). Третий аргумент - целое число - длина структуры адреса.

Функция возвращает 0, если вызов успешный, и -1 иначе.

Функция send

Функция служит для записи данных в сокет.

int send(SOCKET s,, char * buf, int len, int flags);

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

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

Функция recv

Функция служит для чтения данных из сокета.

int recv(SOCKET s, char * buf, int len, int flags);

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

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

Функция shutdown

Эта функция используется для немедленного закрытия всех или части связей на сокет.

int shutdown(SOCKET s, int how);

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

SD_RECEIVE - сокет закрывается для приема,

SD_SEND - сокет закрывается для отправки,

SD_BOTH - сокет закрывается для приема и отправки.

Функция close socket

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

int close socket (SOCKET s);

Аргумент функции - закрываемый сокет.

Более подробную информацию можно найти в документации, которая поставляется в составе библиотеки разработчика Microsoft Development Library (MSDN).

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

Приложение 3

Листинг. Приложение - простой клиент на основе Windows Socket

#include "stdafx.h"

#include <stdio.h>

#include <winsock.h>

void StreamClient(char *szServer, short nPort);

#define PRINTERROR(s) fprintf(stderr,"\n%: %d\n", s, WSAGetLastError())

void main(int argc, char **argv)

{

WORD wVersionRequested = MAKEWORD(1,1);

WSADATA wsaData;

char ServName [256];

char Port [256];

int nRet;

short nPort;

printf("\nVvedite imya servera: ");

gets(ServName);

// printf("\n %s ", ServName); 

         printf("\nVvedite nomer porta: ");

gets(Port);

// printf("\n %s ", Port);

nPort = atoi(Port);

nRet = WSAStartup(wVersionRequested, &wsaData);

if (wsaData.wVersion!= wVersionRequested)

{

  fprintf(stderr,"\n Wrong version\n");

  return;

}

StreamClient(ServName, nPort);

WSACleanup();

}

void StreamClient(char *szServer, short nPort)

{

char szBuf[256];

printf("\nStream Client connecting to server: %s on port: %d", szServer, nPort);

LPHOSTENT lpHostEntry;

lpHostEntry = gethostbyname(szServer);

   

if (lpHostEntry == NULL)

         {

    PRINTERROR("gethostbyname()");

    return;

         }

   

SOCKET theSocket;

   

theSocket = socket(AF_INET,

                  SOCK_STREAM,

                  IPPROTO_TCP);

if (theSocket == INVALID_SOCKET)

         {

    PRINTERROR("socket()");

    return;

         }

SOCKADDR_IN saServer;

saServer.sin_family = AF_INET;

saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list);

saServer.sin_port = htons(nPort);

   

int nRet;

nRet = connect(theSocket,(LPSOCKADDR)&saServer,sizeof(struct sockaddr));

if (nRet == SOCKET_ERROR)

         {

    PRINTERROR("socket()");

    closesocket(theSocket);

    return;

         }

while(1)

{

memset(szBuf, 0, sizeof(szBuf));

printf("\nVvedite soobshenie, ili 'exit' dlya vihoda: ");

gets(szBuf);

if(strcmp(szBuf, "exit")==0)

         {closesocket(theSocket); return;}

nRet = send(theSocket,szBuf,strlen(szBuf), 0);

   

if (nRet == SOCKET_ERROR)

         {

    PRINTERROR("send()");

    closesocket(theSocket);

    return;

         }

nRet = recv(theSocket, szBuf, sizeof(szBuf), 0);

if (nRet == SOCKET_ERROR)

         {

    PRINTERROR("recv()");

    closesocket(theSocket);

    return;

         }

printf("\nMessage received: %s", szBuf);

}

closesocket(theSocket);

return;

}

Листинг. Приложение - простой сервер на основе Windows Socket

#include "stdafx.h"

#include <stdio.h>

#include <winsock.h>

void StreamServer(short nPort);

#define PRINTERROR(s) fprintf(stderr,"\n%: %d\n", s, WSAGetLastError())

void main(int argc, char **argv)

{

WORD wVersionRequested = MAKEWORD(1,1);

WSADATA wsaData;

int nRet;

    char Port[256];

short nPort;

    printf("\nVvedite nomer porta: ");

    gets(Port);

// printf("\n %s ", Port);

nPort = atoi(Port);

nRet = WSAStartup(wVersionRequested, &wsaData);

if (wsaData.wVersion!= wVersionRequested)

{

  fprintf(stderr,"\n Wrong version\n");

  return;

}

StreamServer(nPort);

WSACleanup();

}

void StreamServer(short nPort)

{

    char szBuf[256];

SOCKET listenSocket;

listenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if (listenSocket == INVALID_SOCKET)

              {

   PRINTERROR("socket()");

   return;

              }

    SOCKADDR_IN saServer;

saServer.sin_family = AF_INET;

saServer.sin_addr.s_addr = INADDR_ANY;

saServer.sin_port = htons(nPort);

    int nRet;

    nRet = bind(listenSocket,(LPSOCKADDR)&saServer, sizeof(struct sockaddr));

if (nRet == SOCKET_ERROR)

              {

    PRINTERROR("bind()");

    closesocket(listenSocket);

    return;

              }

 

int nLen;

nLen = sizeof(SOCKADDR);

   

 

nRet = gethostname(szBuf, sizeof(szBuf));

if (nRet == SOCKET_ERROR)

              {

    PRINTERROR("gethostname()");

    closesocket(listenSocket);

    return;

              }

 

printf("\nServer named %s waiting on port %d\n", szBuf, nPort);

printf("\nlisten()");

    nRet = listen(listenSocket, SOMAXCONN);

if (nRet == SOCKET_ERROR)

              {

    PRINTERROR("listen()");

    closesocket(listenSocket);

    return;

              }

    SOCKET remoteSocket;

    remoteSocket = accept(listenSocket, NULL, NULL);

    if (remoteSocket == INVALID_SOCKET)

              {

    PRINTERROR("accept()");

    closesocket(listenSocket);

    return;

              }

        

    while(1)

    {

memset(szBuf, 0, sizeof(szBuf));

nRet = recv(remoteSocket, szBuf,sizeof(szBuf),0);

if (nRet == INVALID_SOCKET)

              {

    PRINTERROR("recv()");

    closesocket(listenSocket);

    closesocket(remoteSocket);

    return;

              }

    printf("\nMessage received: %s", szBuf);

    

memset(szBuf, 0, sizeof(szBuf));

printf("\nVvedite soobshenie: ");

    gets(szBuf);

    nRet = send(remoteSocket, szBuf, strlen(szBuf),0);

    }

closesocket(remoteSocket);

closesocket(listenSocket);

return;

}

 

Листинг. Приложение – получение информации о протоколах

/#include "stdafx.h"

#include <stdio.h>

#include <winsock.h>

int Printhostent(LPCSTR lpServerNameOrAddress);

void main(int argc, char **argv)

{

WORD wVersionRequested = MAKEWORD(1,1);

WSADATA wsaData;

int nRC;

if (argc!= 2)

{

   fprintf(stderr,

       "\nSyntax: HostInfo ServerNameOrAddress\n");

   return;

}

nRC = WSAStartup(wVersionRequested, &wsaData);

if (nRC)

{

   fprintf(stderr,"\nWSAStartup() error: %d\n", nRC);

   WSACleanup();

   return;

}

if (wVersionRequested!= wsaData.wVersion)

{

   fprintf(stderr,"\nWinSock version 1.1 not supported\n");

   WSACleanup();

   return;

}

nRC = Printhostent(argv[1]);

if (nRC)

   fprintf(stderr,"\nPrinthostent return code: %d\n", nRC);

WSACleanup();

}

int Printhostent(LPCSTR lpServerNameOrAddress)

{

LPHOSTENT lpHostEntry; // Pointer to host entry structure

struct in_addr iaHost; // Internet address structure

struct in_addr *pinAddr; // Pointer to an internet address

LPSTR lpAlias;        // Character pointer for alias names

int iNdx;

iaHost.s_addr = inet_addr(lpServerNameOrAddress);

if (iaHost.s_addr == INADDR_NONE)

{

   lpHostEntry = gethostbyname(lpServerNameOrAddress);

}

else

{

   lpHostEntry = gethostbyaddr((const char *)&iaHost,

                   sizeof(struct in_addr), AF_INET);

}

 

if (lpHostEntry == NULL)

{

   fprintf(stderr,"\nError getting host: %d",

            WSAGetLastError());

   return WSAGetLastError();

}

printf("\n\nHOSTENT");

printf("\n-----------------");

printf("\nHost Name........: %s", lpHostEntry->h_name);

printf("\nHost Aliases.....");

for (iNdx = 0;; iNdx++)

{

   lpAlias = lpHostEntry->h_aliases[iNdx];

   if (lpAlias == NULL)

       break;

   printf(": %s", lpAlias);

   printf("\n            ");

}

printf("\nAddress type.....: %d", lpHostEntry->h_addrtype);

if (lpHostEntry->h_addrtype == AF_INET)

   printf(" (AF_INET)");

else

   printf(" (UnknownType)");

printf("\nAddress length...: %d", lpHostEntry->h_length);

printf("\nIP Addresses.....");

for (iNdx = 0;; iNdx++)

{

   pinAddr = ((LPIN_ADDR)lpHostEntry->h_addr_list[iNdx]);

   if (pinAddr == NULL)

       break;

   printf(": %s", inet_ntoa(*pinAddr));

   printf("\n            ");

}

printf("\n");

return 0;

}

 

Литература

1. Олифер В.Г., Олифер Н.А. Сетевые операционные системы. - СПб: Питер, 2003.

2. Джонс Э., Оланд Дж. Программирование в сетях Microsoft Windows; Пер. с англ. - СПб: Издательско-торговый дом «Русская редакция», 2002.

3. Рихтер Дж., Кларк Дж.Д. Программирование серверных приложений для Microsoft Windows 2000; Пер. с англ. - СПб: Издательско-торговый дом «Русская редакция», 2001.

4. Джонсон М. Хард.  Системное программирование в среде Win 32; Пер. с англ. - М: Издательский дом «Вильямс», 2001.

5. Соломон Д., Руссинович М. Внутреннее устройство Microsoft Windows 2000; Пер. с англ. - СПб: Питер, 2001.

 

 

                                                                          ББК 32.973.202-018.1я73-1

                                                                                  Св. план 2007 г.

                                                                                                      поз.37

 

 

ЧЕРКАСОВА Наталья Ивановна

 


Поделиться с друзьями:

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

Особенности сооружения опор в сложных условиях: Сооружение ВЛ в районах с суровыми климатическими и тяжелыми геологическими условиями...

Биохимия спиртового брожения: Основу технологии получения пива составляет спиртовое брожение, - при котором сахар превращается...

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.239 с.