28 мая 2009 г.

Сокеты на C/C++

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

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

Сокеты, независимо от вида, деляться на три типа: потоковые, сырые(windows их не "держит") и дейтаграммные. Потоковые сокеты работают с установкой соединения, обеспечивая надежную идентификацию обеих сторон и гарантируют целостность и успешность доставки данных,опираються на протокол TCP. Дейтаграммные сокеты работают без установки соединения и не обеспечивают ни идентификации отправителя, ни контроля успешности доставки данных, зато они быстрее потоковых, опираються на протокол UDP. Сырые сокеты, они предоставляют возможность ручного формирования TCP\IP пакетов.


Програмирование сокетов идет поэтапно:

1.Создание сокета
2.Привязка сокета к локальным именам
3.Установка связи
4.Передача данных
5.Закрытие сокета

Ну, кто еще не знает, как работать с сокетами я опишу на создании простенького TCP-клиента. Это программа для тех, кто уже хоть немного знаком с языком С++. Надеюсь все помнят, что символ // обозначает коментарии и никак не влияет на действие программы.


#include
#include
// Для работы с библиотекой Winsock2 в исходный текст нужно включить директиву #include ,
//а в командной строке компоновщика указать ws2_32.lib. Библиотека Winsock2 должна быть указана раньше,
// чем библиотека Windows.
#include
#include

#define PORT 31337 // Порт, через который идет подключение к серверу....21,80,25,110 и т.п.
#define SERVERADDR "127.0.0.1" // Адрес сервера

int main ( int argc, char* argv [ ] )
{
char buff [ 1024 ] ;
printf ( "TCP Client \n" ); // ну, тут и так понятно
//Перед началом использования библиотеки ее необходимо подготовить к работе с помощью вызова функции
// WSAStartup(Ver, lpWSAData), передав ей в старшем байте номер требуемой версии, а в младшем подверсии.
// Аргумент lpWSAData должен указывать на структуру WSADATA, в которую при успешной инициализации
//будет занесена информация о производителе библиотеки. Если инициализация не удалась, то функция //возвращает не нулевое значение.
if ( WSAStartup ( 0x202, ( WSADATA * ) &buff [ 0 ] ) )
{
printf ( " WSAStart error %d \n ", WSAGetLastError ( ) );
return -1;
}
// Создаем сокет socket ( int af, int type, int protocol ). Первый слева аргумент указывает на семейство использу-
//емых протоколов, AF_INET - используеться при создании интернет приложений. SOCK_STREAM - тип //создаваемого сокета, SOCK_STREAM (потоковый) или SOCK_DGRAM (дейтаграммный) или SOCK_RAW //(сырой).Последний аргумент задает тип создаваемого сокета, нулевое значение соответствует выбору по //умолчанию, ТСР - для потоковых, и UDP - дейтограммных.
//Если функция завершилась успешно то она возвращает дескриптор сокета.
SOCKET my_sock ;
my_sock=socket ( AF_INET, SOCK_STREAM, 0 );
if ( my_sock < sin_family="AF_INET;" sin_port="htons" sin_addr="inet_addr" hst="gethostbyname">h_addr_list содержит массив указателей на адреса, НО не массив адресов
( ( unsigned long ** ) hst->h_addr_list ) [ 0 ] [ 0 ] ;
//Если ip-адрес не получен, то работа программы завершаеться
else
{
printf ( " invalid address %s \n ", SERVERADDR ) ;
closesocket ( my_sock ) ;
WSACleanup ( ) ;
return -1;
}
//После получения адреса сервера, попытаемся установит соединение.Для этого мы вызовем функцию connect
// (SOCKET s, sockaddr * name, len). Первый элемент -SOCKET- это дескриптор сокета, второй -
//указатель на структуру sockaddr, содержащую в себе адрес (ip) и порт, последний аргумент сообщает о размере
// sockaddr.
// Если по каким-то причинам установить соединение не удаеться, то функция возвращает не нулевое значение.
if (connect ( my_sock, ( sockaddr * ) &dest_addr, sizeof ( dest_addr ) ) )
{
printf (" Connect error %d \n ", WSAGetLastError ( ) );
return -1;
}

printf (" Соединение с %s успешно установлено \n\ Type quit for quit \n\n" , SERVERADDR );
//Далее начинаеться чтение и передача сообщений.
int nsize;
while ( ( nsize = recv ( my_sock, &buff[0], sizeof ( buff ) -1,0 ) ) !=SOCKET_ERROR )
{
//Ставим завершающий ноль в конце строки
buff[ nsize ] =0;
//Выводим на экран
printf ( " S=>C: %s ", buff ) ;
//Читаем пользовательский ввод с клавиатуры
printf ( "S<=C: " ) ; fgets ( &buff [ 0 ] , sizeof ( buff ) -1, stdin ); //После того, как ввели слово quit, выходим..... if ( !strcmp ( &buff [ 0 ], "quit \n " ) ) { printf ( " Exit " ); closesocket ( my_sock ) ; //выход правильный WSACleanup ( ) ; return 0; } //Передача строки клиента серверу send ( my_sock, &buff [ 0 ], nsize, 0 ) ; } printf ( " Recv error %d \n", WSAGetLastError ( ) ); closesocket ( my_sock ) ; WSACleanup ( ) ; return -1; }