Модуль 3: Основные понятия объектно-ориентированного
программирования.
Темы лекции: Программирование сокетов в С++. Работа с
графикой.
Практическое задание: Программирование сокетов в С++.
Тренер: Игорь Шкулипа, к.т.н.
C++ Базовый. Занятие 13
http://www.slideshare.net/IgorShkulipa 2
Основы socket API
http://www.slideshare.net/IgorShkulipa 3
Сокет
Сокет (socket) - это конечная точка сетевых коммуникаций. Он
является чем-то вроде «порта», через который можно
отправлять байты во внешний мир.
Приложение просто пишет данные в сокет; их дальнейшая
буферизация, отправка и транспортировка осуществляется
используемым стеком протоколов и сетевой аппаратурой.
Чтение данных из сокета происходит аналогичным образом.
В программе сокет идентифицируется дескриптором - это
просто переменная типа int. Программа получает дескриптор
от операционной системы при создании сокета, а затем
передаёт его сервисам socket API для указания сокета, над
которым необходимо выполнить то или иное действие.
#include <sys/types.h>
#include <sys/socket.h>
http://www.slideshare.net/IgorShkulipa 4
Атрибуты сокета
С каждым сокет связываются три атрибута: домен, тип и
протокол. Эти атрибуты задаются при создании сокета и
остаются неизменными на протяжении всего времени его
существования.
Для создания сокета используется функция socket, имеющая
следующий прототип.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
http://www.slideshare.net/IgorShkulipa 5
Домен сокета
Домен определяет пространство адресов, в котором располагается сокет,
и множество протоколов, которые используются для передачи данных.
Чаще других используются домены Unix и Internet, задаваемые
константами AF_UNIX и AF_INET соответственно (префикс AF означает
"address family" - "семейство адресов").
◦ При задании AF_UNIX для передачи данных используется файловая
система ввода/вывода Unix. В этом случае сокеты используются для
межпроцессного взаимодействия на одном компьютере и не годятся
для работы по сети.
◦ Константа AF_INET соответствует Internet-домену. Сокеты,
размещённые в этом домене, могут использоваться для работы в
любой IP-сети.
◦ Существуют и другие домены (AF_IPX для протоколов Novell,
AF_INET6 для новой модификации протокола IP - IPv6 и т. д.)
http://www.slideshare.net/IgorShkulipa 6
Тип сокета
Тип сокета определяет способ передачи данных по сети:
SOCK_STREAM. Передача потока данных с предварительной установкой
соединения. Обеспечивается надёжный канал передачи данных, при
котором фрагменты отправленного блока не теряются, не
переупорядочиваются и не дублируются.
SOCK_DGRAM. Передача данных в виде отдельных сообщений
(датаграмм). Предварительная установка соединения не требуется.
Обмен данными происходит быстрее, но является ненадёжным:
сообщения могут теряться в пути, дублироваться и
переупорядочиваться. Допускается передача сообщения нескольким
получателям (multicasting) и широковещательная передача
(broadcasting).
SOCK_RAW. Этот тип присваивается низкоуровневым сокетам. Их
отличие от обычных сокетов состоит в том, что с их помощью
программа может взять на себя формирование некоторых заголовков,
добавляемых к сообщению.
http://www.slideshare.net/IgorShkulipa 7
Протокол сокета
Последний атрибут определяет протокол, используемый для
передачи данных.
Часто протокол однозначно определяется по домену и типу
сокета. В этом случае в качестве третьего параметра функции
socket можно передать 0, что соответствует протоколу по
умолчанию.
Но, иногда (например, при работе с низкоуровневыми сокетами)
требуется задать протокол явно.
Числовые идентификаторы протоколов зависят от выбранного
домена; их можно найти в документации.
http://www.slideshare.net/IgorShkulipa 8
Адрес сокета
Прежде чем передавать данные через сокет, его необходимо
связать с адресом в выбранном домене.
Иногда связывание осуществляется неявно (внутри функций
connect и accept), но выполнять его необходимо во всех
случаях.
Для явного связывания сокета с некоторым адресом
используется функция bind.
int bind(int sockfd, struct sockaddr *addr, int addrlen);
http://www.slideshare.net/IgorShkulipa 9
Аргументы bind
В качестве первого параметра передаётся дескриптор сокета,
который мы хотим привязать к заданному адресу.
Второй параметр, addr, содержит указатель на структуру с
адресом, а третий - длину этой структуры.
struct sockaddr
{
unsigned short sa_family;
char sa_data[14];
};
Поле sa_family содержит идентификатор домена, тот же, что и
первый параметр функции socket.
В зависимости от значения этого поля по-разному
интерпретируется содержимое массива sa_data.
http://www.slideshare.net/IgorShkulipa 10
Альтернативные адреса
Вместо sockaddr, можно использовать одну из альтернативных
структур вида sockaddr_XX
(XX - суффикс, обозначающий домен: "un" - Unix, "in" - Internet и
т. д.).
При передаче в функцию bind указатель на эту структуру
приводится к указателю на sockaddr.
struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
http://www.slideshare.net/IgorShkulipa 11
Поля sockaddr_in
sin_family соответствует полю sa_family в sockaddr
в sin_port записывается номер порта
в sin_addr - IP-адрес хоста.
sin_addr само является структурой
sin_zero – дополнение до размера основной структуры
struct in_addr
{
unsigned long s_addr;
};
Раньше in_addr представляла собой объединение (union),
содержащее гораздо большее число полей. Сейчас, когда в
ней осталось всего одно поле, она продолжает использоваться
для обратной совместимости.
http://www.slideshare.net/IgorShkulipa 12
Преобразование IP адреса в in_addr
Для преобразования IP адреса в in_addr можно использовать
функцию inet_aton из заголовочного файла arpa/inet.h
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
http://www.slideshare.net/IgorShkulipa 13
Установка соединения со стороны сервера
Установка соединения на стороне сервера состоит из следующих этапов:
1. Сначала сокет создаётся и привязывается к локальному адресу.
2. На следующем шаге создаётся очередь запросов на соединение. При
этом сокет переводится в режим ожидания запросов со стороны
клиентов. Всё это выполняет функция listen.
int listen(int sockfd, int backlog);
Первый параметр - дескриптор сокета, а второй задаёт размер очереди
запросов.
Каждый раз, когда очередной клиент пытается соединиться с сервером,
его запрос ставится в очередь, так как сервер может быть занят
обработкой других запросов. Если очередь заполнена, все
последующие запросы будут игнорироваться.
http://www.slideshare.net/IgorShkulipa 14
Установка соединения со стороны сервера
Когда сервер готов обслужить очередной запрос, он использует функцию
accept.
int accept(int sockfd, void *addr, int *addrlen);
Функция accept создаёт для общения с клиентом новый сокет и
возвращает его дескриптор.
Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в
слушающем состоянии и может принимать другие соединения.
В структуру, на которую ссылается addr, записывается адрес сокета
клиента, который установил соединение с сервером.
В переменную, адресуемую указателем addrlen, изначально
записывается размер структуры; функция accept записывает туда
длину, которая реально была использована.
Если вас не интересует адрес клиента, вы можете просто передать NULL
в качестве второго и третьего параметров.
Полученный от accept новый сокет связан с тем же самым адресом, что и
слушающий сокет.
http://www.slideshare.net/IgorShkulipa 15
Установка соединения со стороны клиента
На стороне клиента для установления соединения используется функция
connect, которая имеет следующий прототип.
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
Здесь sockfd - сокет, который будет использоваться для обмена данными с
сервером, serv_addr содержит указатель на структуру с адресом сервера,
а addrlen - длину этой структуры.
Обычно сокет не требуется предварительно привязывать к локальному
адресу, так как функция connect сделает это за вас, подобрав
подходящий свободный порт.
http://www.slideshare.net/IgorShkulipa 16
Отправка данных
Функция send используется для отправки данных:
int send(int sockfd, const void *msg, int len, int flags);
sockfd - это дескриптор сокета
msg - указатель на буфер с данными
len - длина буфера в байтах
flags - набор битовых флагов, управляющих работой функции (если
флаги не используются, можно передать функции 0)
Некоторые флаги:
MSG_OOB - Предписывает отправить данные как срочные.
MSG_DONTROUTE - Запрещает маршрутизацию пакетов. Нижележащие
транспортные слои могут проигнорировать этот флаг.
Функция send возвращает число байтов, которое на самом деле было
отправлено (или -1 в случае ошибки). Это число может быть меньше
указанного размера буфера.
http://www.slideshare.net/IgorShkulipa 17
Отправка всего буфера
Для того, чтобы отправить весь буфер целиком, необходимо написать
свою функцию и вызывать в ней send, пока все данные не будут
отправлены.
int sendall(int s, char *buf, int len, int flags)
{
int total = 0;
int n;
while(total < len)
{
n = send(s, buf+total, len-total, flags);
if(n == -1)
{
break;
}
total += n;
}
return (n==-1 ? -1 : total);
}
http://www.slideshare.net/IgorShkulipa 18
Получение данных
Для чтения данных из сокета используется функция recv.
int recv(int sockfd, void *buf, int len, int flags);
Принимает дескриптор сокета, указатель на буфер и набор
флагов.
По аналогии с send функция recv возвращает количество
прочитанных байтов, которое может быть меньше размера
буфера.
Существует один особый случай, при котором recv возвращает 0.
Это означает, что соединение было разорвано.
http://www.slideshare.net/IgorShkulipa 19
Закрытие сокета
Закончив обмен данными, закройте сокет с помощью функции
close. Это приведёт к разрыву соединения.
int close(int sockfd);
Также можно запретить передачу данных в каком-то одном
направлении, используя shutdown.
int shutdown(int sockfd, int how);
Параметр how может принимать одно из следующих значений:
⚫ 0 - запретить чтение из сокета
⚫ 1 - запретить запись в сокет
⚫ 2 - запретить и то и другое
Всё равно потребуется вызвать close, чтобы освободить
связанные с ним системные ресурсы.
http://www.slideshare.net/IgorShkulipa 20
Пример сервера
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <sstream>
using namespace std;
class SocketServer
{
public:
SocketServer(string ip, int port);
int Run();
void Close();
private:
sockaddr_in _fullAddress;
int _listener;
string _answers[100];
char _buffer[100];
};
http://www.slideshare.net/IgorShkulipa 21
Пример сервера (конструктор)
SocketServer::SocketServer(string ip, int port)
{
//Заполнение структуры адреса
_fullAddress.sin_family = AF_INET;
_fullAddress.sin_port = port;
inet_aton(ip.c_str(), &(_fullAddress.sin_addr));
//Создание сокета
_listener = socket(AF_INET, SOCK_STREAM, 0);
//Создание массива ответов
for (int i=0; i<100; i++)
{
stringstream ss;
ss<<"Answer #"<< i <<"n";
_answers[i]=ss.str();
}
}
http://www.slideshare.net/IgorShkulipa 22
Пример сервера (основной метод)
int SocketServer::Run()
{
//Выполняем связывание
int iBindResult=bind(_listener,
(sockaddr*) &_fullAddress,
sizeof(_fullAddress));
if (iBindResult<0)
{
cout<<"Error on bindingn";
}
//Слушаем сокет
listen(_listener, 1);
cout<<"Server startedn";
...
http://www.slideshare.net/IgorShkulipa 23
Пример сервера (основной метод, продолжение)
//Переходим в режим приема-передачи данных
while (1)
{
//Проверка приема
int sock = accept(_listener, NULL, NULL);
if (sock < 0)
{
cout<<"Error on acceptingn";
return 0;
}
http://www.slideshare.net/IgorShkulipa 24
Пример сервера (основной метод, продолжение 2)
//Чтение буфера данных
while (1) {
int iReadCount = recv(sock, _buffer, 100, 0);
if (iReadCount <= 0) break;
cout<<"Buffer received: "<<iReadCount<<" bytesn";
stringstream ss;
for (int i=0; i<iReadCount; i++)
{
ss<<_buffer[i];
}
int iChoice;
ss>>iChoice;
cout<<"Choice= "<<iChoice<<"n";
cout<<"Answer= "<<_answers[iChoice]<<"n";
int iSent=
send(sock, _answers[iChoice].c_str(), sizeof(_answers[iChoice]), 0);
cout<<"Answer sent: "<< iSent<<" bytesn";
}
}
}
http://www.slideshare.net/IgorShkulipa 25
Пример сервера (закрытие и функция main)
void SocketServer::Close()
{
close(_listener);
}
int main()
{
SocketServer* sServer=new SocketServer("127.0.0.1", 1234);
sServer->Run();
}
http://www.slideshare.net/IgorShkulipa 26
Пример клиента
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <sstream>
using namespace std;
class SocketClient
{
public:
SocketClient(string serverIP, int serverPort);
string GetAnswer(int iNumber);
private:
sockaddr_in _serverAddress;
int _socket;
char _buffer[100];
};
http://www.slideshare.net/IgorShkulipa 27
Пример клиента (конструктор)
SocketClient::SocketClient(string serverIP, int serverPort)
{
_serverAddress.sin_family = AF_INET;
_serverAddress.sin_port = serverPort;
inet_aton(serverIP.c_str(), &(_serverAddress.sin_addr));
_socket = socket(AF_INET, SOCK_STREAM, 0);
}
http://www.slideshare.net/IgorShkulipa 28
Пример клиента (основной метод)
string SocketClient::GetAnswer(int iNumber)
{
int iConnect=connect(_socket,
(sockaddr *)&_serverAddress,
sizeof(_serverAddress));
if( iConnect< 0)
{
return "Connection failedn";
}
...
http://www.slideshare.net/IgorShkulipa 29
Пример клиента (основной метод)
else
{
stringstream ss;
ss<<iNumber;
for (int i=0;i<ss.str().length();i++)
{
_buffer[i]=ss.str().c_str()[i];
}
send(_socket, _buffer, 100, 0);
int iReceived=
recv(_socket, _buffer, 100, 0);
cout<<"Received "<<iReceived<<" bytesn";
string strResult(_buffer);
return strResult+"n";
}
}
http://www.slideshare.net/IgorShkulipa 30
Пример клиента (функция main)
int main()
{
SocketClient* sClient=new SocketClient("127.0.0.1", 1234);
int iChoice=0;
while(iChoice>=0)
{
cout << "Input choice -1..99:";
cin >> iChoice;
if (iChoice<0)
{
cout<<"Good bye.n";
}
else
{
cout<<"Server Response is:";
cout << sClient->GetAnswer(iChoice);
}
}
}
http://www.slideshare.net/IgorShkulipa 31
Основы графики в Linux
http://www.slideshare.net/IgorShkulipa 32
OpenGL
OpenGL - это графический стандарт в области компьютерной графики. На
данный момент он является одним из самых популярных графических
стандартов во всём мире.
OpenGL переводится как Открытая Графическая Библиотека (Open
Graphics Library), это означает, что OpenGL - это открытый и
мобильный стандарт. Программы, написанные с помощью OpenGL
можно переносить практически на любые платформы, получая при
этом одинаковый результат.
Ссылки на примеры и статьи:
1. http://ogldev.atspace.co.uk/
2. http://igorbarbosa.com/articles/opengl-glut-with-linux-mint-and-ubuntu-
12-04/
3. http://how2.org.ua/game-devel/создание-двумерного-opengl-
приложения-в-c.html
4. http://www.rusdoc.ru/material/lang/other/glut.shtml
5. http://www.pvsm.ru/opengl/22610
http://www.slideshare.net/IgorShkulipa 33
Как установить OpenGL
Необходимо загрузить файл пакета командой:
# sudo apt-get install freeglut3 freeglut3-dev
Далее, в свойствах проекта, необходимо добавить библиотеку:
/usr/lib/libglut.so
Подключить заголовочный файл:
#include <GL/freeglut.h>
http://www.slideshare.net/IgorShkulipa 34
Пример OpenGL приложения
#include <GL/freeglut.h>
#include <string>
using namespace std;
class FirstOGLApplication
{
public:
FirstOGLApplication(int argc, char * argv[]);
FirstOGLApplication(int width, int height, int argc, char * argv[]);
FirstOGLApplication(int width, int height, int startX, int startY, int
argc, char * argv[]);
void Run();
void Run(string strHeader);
private:
int _width; int _height; int _startX; int _startY;
static void DrawMyself();
static void DrawLine(float x1, float y1, float x2, float y2, float r,
float g, float b, int width);
};
http://www.slideshare.net/IgorShkulipa 35
Пример OpenGL приложения (конструкторы)
FirstOGLApplication::FirstOGLApplication(int argc, char * argv[])
{
_width=100; _height=100; _startX=100; _startY=100;
this->InitGL(argc, argv);
}
FirstOGLApplication::FirstOGLApplication(int width, int height, int
argc, char * argv[])
{
_width=width; _height=height; _startX=100; _startY=100;
this->InitGL(argc, argv);
}
FirstOGLApplication::FirstOGLApplication(int width, int height, int
startX, int startY, int argc, char * argv[])
{
_width=width; _height=height; _startX=startX; _startY=startY;
this->InitGL(argc, argv);
}
http://www.slideshare.net/IgorShkulipa 36
Пример OpenGL приложения (метод InitGL)
void FirstOGLApplication::InitGL(int argc, char* argv[])
{
// Инициализация GLUT
glutInit(&argc, argv);
// Установка режима отображения (RGBA-палитра)
glutInitDisplayMode(GLUT_RGBA);
// Установка размера окна
glutInitWindowSize(_width, _height);
// Установка изначальной позиции окна
glutInitWindowPosition(_startX, _startY);
}
http://www.slideshare.net/IgorShkulipa 37
Пример OpenGL приложения (методы запуска)
void FirstOGLApplication::Run()
{
this->Run("");
}
void FirstOGLApplication::Run(string strHeader)
{
// Создание окна с заголовком
glutCreateWindow(strHeader.c_str());
// Очистка цветом 0х00000000 (черный)
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// Запуск функции DrawMyself
glutDisplayFunc(FirstOGLApplication::DrawMyself);
// Запуск приложения в режим ожидания событий
glutMainLoop();
}
http://www.slideshare.net/IgorShkulipa 38
Пример OpenGL приложения (рисуем себя)
void FirstOGLApplication::DrawMyself()
{
glClear(GL_COLOR_BUFFER_BIT);
// Линия синего цвета толщиной 1
DrawLine(-0.5, -0.5, -0.5, 0.5, 0, 0, 1, 1);
// Линия зеленого цвета толщиной 2
DrawLine(-0.5, 0.5, 0.5, 0.5, 0, 1, 0, 2);
// Линия красного цвета толщиной 3
DrawLine( 0.5, 0.5, 0.5, -0.5, 1, 0, 0, 3);
// Линия белого цвета толщиной 4
DrawLine( 0.5, -0.5, -0.5, -0.5, 1, 1, 1, 4);
glFlush();
glutSwapBuffers();
}
http://www.slideshare.net/IgorShkulipa 39
Пример OpenGL приложения (рисование линии)
void FirstOGLApplication::DrawLine(float x1, float y1, float x2,
float y2, float r, float g, float b, int width)
{
// Устанавливаем цвет
glColor3f(r, g, b);
// Устанавливаем толщину линии
glLineWidth(width);
// Рисуем линию
glBegin(GL_LINES);
// Координаты точек в относительных величинах с центром в центре окна
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
}
http://www.slideshare.net/IgorShkulipa 40
Пример OpenGL приложения (функция main)
int main(int argc, char * argv[])
{
FirstOGLApplication* app=
new FirstOGLApplication(500, 500, 100, 100, argc, argv);
app->Run("Hello, OpenGL!");
return 0;
}
http://www.slideshare.net/IgorShkulipa 41
Пример OpenGL приложения (результат)
http://www.slideshare.net/IgorShkulipa 42
Лабораторная работа №13. Клиент-сервер
Создать консольный калькулятор с архитектурой «клиент-
сервер».
Клиент:
⚫ Отправляет данные на сервер с запросом о выполнении
арифметических операций над числами.
⚫ Выводит меню для управления вычислениями.
⚫ Выводит результаты на экран.
Сервер:
⚫ Обрабатывает запрос клиента и отправляет ему результат

C++ Базовый. Занятие 13.

  • 1.
    Модуль 3: Основныепонятия объектно-ориентированного программирования. Темы лекции: Программирование сокетов в С++. Работа с графикой. Практическое задание: Программирование сокетов в С++. Тренер: Игорь Шкулипа, к.т.н. C++ Базовый. Занятие 13
  • 2.
  • 3.
    http://www.slideshare.net/IgorShkulipa 3 Сокет Сокет (socket)- это конечная точка сетевых коммуникаций. Он является чем-то вроде «порта», через который можно отправлять байты во внешний мир. Приложение просто пишет данные в сокет; их дальнейшая буферизация, отправка и транспортировка осуществляется используемым стеком протоколов и сетевой аппаратурой. Чтение данных из сокета происходит аналогичным образом. В программе сокет идентифицируется дескриптором - это просто переменная типа int. Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для указания сокета, над которым необходимо выполнить то или иное действие. #include <sys/types.h> #include <sys/socket.h>
  • 4.
    http://www.slideshare.net/IgorShkulipa 4 Атрибуты сокета Скаждым сокет связываются три атрибута: домен, тип и протокол. Эти атрибуты задаются при создании сокета и остаются неизменными на протяжении всего времени его существования. Для создания сокета используется функция socket, имеющая следующий прототип. #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
  • 5.
    http://www.slideshare.net/IgorShkulipa 5 Домен сокета Доменопределяет пространство адресов, в котором располагается сокет, и множество протоколов, которые используются для передачи данных. Чаще других используются домены Unix и Internet, задаваемые константами AF_UNIX и AF_INET соответственно (префикс AF означает "address family" - "семейство адресов"). ◦ При задании AF_UNIX для передачи данных используется файловая система ввода/вывода Unix. В этом случае сокеты используются для межпроцессного взаимодействия на одном компьютере и не годятся для работы по сети. ◦ Константа AF_INET соответствует Internet-домену. Сокеты, размещённые в этом домене, могут использоваться для работы в любой IP-сети. ◦ Существуют и другие домены (AF_IPX для протоколов Novell, AF_INET6 для новой модификации протокола IP - IPv6 и т. д.)
  • 6.
    http://www.slideshare.net/IgorShkulipa 6 Тип сокета Типсокета определяет способ передачи данных по сети: SOCK_STREAM. Передача потока данных с предварительной установкой соединения. Обеспечивается надёжный канал передачи данных, при котором фрагменты отправленного блока не теряются, не переупорядочиваются и не дублируются. SOCK_DGRAM. Передача данных в виде отдельных сообщений (датаграмм). Предварительная установка соединения не требуется. Обмен данными происходит быстрее, но является ненадёжным: сообщения могут теряться в пути, дублироваться и переупорядочиваться. Допускается передача сообщения нескольким получателям (multicasting) и широковещательная передача (broadcasting). SOCK_RAW. Этот тип присваивается низкоуровневым сокетам. Их отличие от обычных сокетов состоит в том, что с их помощью программа может взять на себя формирование некоторых заголовков, добавляемых к сообщению.
  • 7.
    http://www.slideshare.net/IgorShkulipa 7 Протокол сокета Последнийатрибут определяет протокол, используемый для передачи данных. Часто протокол однозначно определяется по домену и типу сокета. В этом случае в качестве третьего параметра функции socket можно передать 0, что соответствует протоколу по умолчанию. Но, иногда (например, при работе с низкоуровневыми сокетами) требуется задать протокол явно. Числовые идентификаторы протоколов зависят от выбранного домена; их можно найти в документации.
  • 8.
    http://www.slideshare.net/IgorShkulipa 8 Адрес сокета Преждечем передавать данные через сокет, его необходимо связать с адресом в выбранном домене. Иногда связывание осуществляется неявно (внутри функций connect и accept), но выполнять его необходимо во всех случаях. Для явного связывания сокета с некоторым адресом используется функция bind. int bind(int sockfd, struct sockaddr *addr, int addrlen);
  • 9.
    http://www.slideshare.net/IgorShkulipa 9 Аргументы bind Вкачестве первого параметра передаётся дескриптор сокета, который мы хотим привязать к заданному адресу. Второй параметр, addr, содержит указатель на структуру с адресом, а третий - длину этой структуры. struct sockaddr { unsigned short sa_family; char sa_data[14]; }; Поле sa_family содержит идентификатор домена, тот же, что и первый параметр функции socket. В зависимости от значения этого поля по-разному интерпретируется содержимое массива sa_data.
  • 10.
    http://www.slideshare.net/IgorShkulipa 10 Альтернативные адреса Вместоsockaddr, можно использовать одну из альтернативных структур вида sockaddr_XX (XX - суффикс, обозначающий домен: "un" - Unix, "in" - Internet и т. д.). При передаче в функцию bind указатель на эту структуру приводится к указателю на sockaddr. struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; };
  • 11.
    http://www.slideshare.net/IgorShkulipa 11 Поля sockaddr_in sin_familyсоответствует полю sa_family в sockaddr в sin_port записывается номер порта в sin_addr - IP-адрес хоста. sin_addr само является структурой sin_zero – дополнение до размера основной структуры struct in_addr { unsigned long s_addr; }; Раньше in_addr представляла собой объединение (union), содержащее гораздо большее число полей. Сейчас, когда в ней осталось всего одно поле, она продолжает использоваться для обратной совместимости.
  • 12.
    http://www.slideshare.net/IgorShkulipa 12 Преобразование IPадреса в in_addr Для преобразования IP адреса в in_addr можно использовать функцию inet_aton из заголовочного файла arpa/inet.h #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp);
  • 13.
    http://www.slideshare.net/IgorShkulipa 13 Установка соединениясо стороны сервера Установка соединения на стороне сервера состоит из следующих этапов: 1. Сначала сокет создаётся и привязывается к локальному адресу. 2. На следующем шаге создаётся очередь запросов на соединение. При этом сокет переводится в режим ожидания запросов со стороны клиентов. Всё это выполняет функция listen. int listen(int sockfd, int backlog); Первый параметр - дескриптор сокета, а второй задаёт размер очереди запросов. Каждый раз, когда очередной клиент пытается соединиться с сервером, его запрос ставится в очередь, так как сервер может быть занят обработкой других запросов. Если очередь заполнена, все последующие запросы будут игнорироваться.
  • 14.
    http://www.slideshare.net/IgorShkulipa 14 Установка соединениясо стороны сервера Когда сервер готов обслужить очередной запрос, он использует функцию accept. int accept(int sockfd, void *addr, int *addrlen); Функция accept создаёт для общения с клиентом новый сокет и возвращает его дескриптор. Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в слушающем состоянии и может принимать другие соединения. В структуру, на которую ссылается addr, записывается адрес сокета клиента, который установил соединение с сервером. В переменную, адресуемую указателем addrlen, изначально записывается размер структуры; функция accept записывает туда длину, которая реально была использована. Если вас не интересует адрес клиента, вы можете просто передать NULL в качестве второго и третьего параметров. Полученный от accept новый сокет связан с тем же самым адресом, что и слушающий сокет.
  • 15.
    http://www.slideshare.net/IgorShkulipa 15 Установка соединениясо стороны клиента На стороне клиента для установления соединения используется функция connect, которая имеет следующий прототип. int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); Здесь sockfd - сокет, который будет использоваться для обмена данными с сервером, serv_addr содержит указатель на структуру с адресом сервера, а addrlen - длину этой структуры. Обычно сокет не требуется предварительно привязывать к локальному адресу, так как функция connect сделает это за вас, подобрав подходящий свободный порт.
  • 16.
    http://www.slideshare.net/IgorShkulipa 16 Отправка данных Функцияsend используется для отправки данных: int send(int sockfd, const void *msg, int len, int flags); sockfd - это дескриптор сокета msg - указатель на буфер с данными len - длина буфера в байтах flags - набор битовых флагов, управляющих работой функции (если флаги не используются, можно передать функции 0) Некоторые флаги: MSG_OOB - Предписывает отправить данные как срочные. MSG_DONTROUTE - Запрещает маршрутизацию пакетов. Нижележащие транспортные слои могут проигнорировать этот флаг. Функция send возвращает число байтов, которое на самом деле было отправлено (или -1 в случае ошибки). Это число может быть меньше указанного размера буфера.
  • 17.
    http://www.slideshare.net/IgorShkulipa 17 Отправка всегобуфера Для того, чтобы отправить весь буфер целиком, необходимо написать свою функцию и вызывать в ней send, пока все данные не будут отправлены. int sendall(int s, char *buf, int len, int flags) { int total = 0; int n; while(total < len) { n = send(s, buf+total, len-total, flags); if(n == -1) { break; } total += n; } return (n==-1 ? -1 : total); }
  • 18.
    http://www.slideshare.net/IgorShkulipa 18 Получение данных Длячтения данных из сокета используется функция recv. int recv(int sockfd, void *buf, int len, int flags); Принимает дескриптор сокета, указатель на буфер и набор флагов. По аналогии с send функция recv возвращает количество прочитанных байтов, которое может быть меньше размера буфера. Существует один особый случай, при котором recv возвращает 0. Это означает, что соединение было разорвано.
  • 19.
    http://www.slideshare.net/IgorShkulipa 19 Закрытие сокета Закончивобмен данными, закройте сокет с помощью функции close. Это приведёт к разрыву соединения. int close(int sockfd); Также можно запретить передачу данных в каком-то одном направлении, используя shutdown. int shutdown(int sockfd, int how); Параметр how может принимать одно из следующих значений: ⚫ 0 - запретить чтение из сокета ⚫ 1 - запретить запись в сокет ⚫ 2 - запретить и то и другое Всё равно потребуется вызвать close, чтобы освободить связанные с ним системные ресурсы.
  • 20.
    http://www.slideshare.net/IgorShkulipa 20 Пример сервера #include<sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <iostream> #include <sstream> using namespace std; class SocketServer { public: SocketServer(string ip, int port); int Run(); void Close(); private: sockaddr_in _fullAddress; int _listener; string _answers[100]; char _buffer[100]; };
  • 21.
    http://www.slideshare.net/IgorShkulipa 21 Пример сервера(конструктор) SocketServer::SocketServer(string ip, int port) { //Заполнение структуры адреса _fullAddress.sin_family = AF_INET; _fullAddress.sin_port = port; inet_aton(ip.c_str(), &(_fullAddress.sin_addr)); //Создание сокета _listener = socket(AF_INET, SOCK_STREAM, 0); //Создание массива ответов for (int i=0; i<100; i++) { stringstream ss; ss<<"Answer #"<< i <<"n"; _answers[i]=ss.str(); } }
  • 22.
    http://www.slideshare.net/IgorShkulipa 22 Пример сервера(основной метод) int SocketServer::Run() { //Выполняем связывание int iBindResult=bind(_listener, (sockaddr*) &_fullAddress, sizeof(_fullAddress)); if (iBindResult<0) { cout<<"Error on bindingn"; } //Слушаем сокет listen(_listener, 1); cout<<"Server startedn"; ...
  • 23.
    http://www.slideshare.net/IgorShkulipa 23 Пример сервера(основной метод, продолжение) //Переходим в режим приема-передачи данных while (1) { //Проверка приема int sock = accept(_listener, NULL, NULL); if (sock < 0) { cout<<"Error on acceptingn"; return 0; }
  • 24.
    http://www.slideshare.net/IgorShkulipa 24 Пример сервера(основной метод, продолжение 2) //Чтение буфера данных while (1) { int iReadCount = recv(sock, _buffer, 100, 0); if (iReadCount <= 0) break; cout<<"Buffer received: "<<iReadCount<<" bytesn"; stringstream ss; for (int i=0; i<iReadCount; i++) { ss<<_buffer[i]; } int iChoice; ss>>iChoice; cout<<"Choice= "<<iChoice<<"n"; cout<<"Answer= "<<_answers[iChoice]<<"n"; int iSent= send(sock, _answers[iChoice].c_str(), sizeof(_answers[iChoice]), 0); cout<<"Answer sent: "<< iSent<<" bytesn"; } } }
  • 25.
    http://www.slideshare.net/IgorShkulipa 25 Пример сервера(закрытие и функция main) void SocketServer::Close() { close(_listener); } int main() { SocketServer* sServer=new SocketServer("127.0.0.1", 1234); sServer->Run(); }
  • 26.
    http://www.slideshare.net/IgorShkulipa 26 Пример клиента #include<sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <iostream> #include <sstream> using namespace std; class SocketClient { public: SocketClient(string serverIP, int serverPort); string GetAnswer(int iNumber); private: sockaddr_in _serverAddress; int _socket; char _buffer[100]; };
  • 27.
    http://www.slideshare.net/IgorShkulipa 27 Пример клиента(конструктор) SocketClient::SocketClient(string serverIP, int serverPort) { _serverAddress.sin_family = AF_INET; _serverAddress.sin_port = serverPort; inet_aton(serverIP.c_str(), &(_serverAddress.sin_addr)); _socket = socket(AF_INET, SOCK_STREAM, 0); }
  • 28.
    http://www.slideshare.net/IgorShkulipa 28 Пример клиента(основной метод) string SocketClient::GetAnswer(int iNumber) { int iConnect=connect(_socket, (sockaddr *)&_serverAddress, sizeof(_serverAddress)); if( iConnect< 0) { return "Connection failedn"; } ...
  • 29.
    http://www.slideshare.net/IgorShkulipa 29 Пример клиента(основной метод) else { stringstream ss; ss<<iNumber; for (int i=0;i<ss.str().length();i++) { _buffer[i]=ss.str().c_str()[i]; } send(_socket, _buffer, 100, 0); int iReceived= recv(_socket, _buffer, 100, 0); cout<<"Received "<<iReceived<<" bytesn"; string strResult(_buffer); return strResult+"n"; } }
  • 30.
    http://www.slideshare.net/IgorShkulipa 30 Пример клиента(функция main) int main() { SocketClient* sClient=new SocketClient("127.0.0.1", 1234); int iChoice=0; while(iChoice>=0) { cout << "Input choice -1..99:"; cin >> iChoice; if (iChoice<0) { cout<<"Good bye.n"; } else { cout<<"Server Response is:"; cout << sClient->GetAnswer(iChoice); } } }
  • 31.
  • 32.
    http://www.slideshare.net/IgorShkulipa 32 OpenGL OpenGL -это графический стандарт в области компьютерной графики. На данный момент он является одним из самых популярных графических стандартов во всём мире. OpenGL переводится как Открытая Графическая Библиотека (Open Graphics Library), это означает, что OpenGL - это открытый и мобильный стандарт. Программы, написанные с помощью OpenGL можно переносить практически на любые платформы, получая при этом одинаковый результат. Ссылки на примеры и статьи: 1. http://ogldev.atspace.co.uk/ 2. http://igorbarbosa.com/articles/opengl-glut-with-linux-mint-and-ubuntu- 12-04/ 3. http://how2.org.ua/game-devel/создание-двумерного-opengl- приложения-в-c.html 4. http://www.rusdoc.ru/material/lang/other/glut.shtml 5. http://www.pvsm.ru/opengl/22610
  • 33.
    http://www.slideshare.net/IgorShkulipa 33 Как установитьOpenGL Необходимо загрузить файл пакета командой: # sudo apt-get install freeglut3 freeglut3-dev Далее, в свойствах проекта, необходимо добавить библиотеку: /usr/lib/libglut.so Подключить заголовочный файл: #include <GL/freeglut.h>
  • 34.
    http://www.slideshare.net/IgorShkulipa 34 Пример OpenGLприложения #include <GL/freeglut.h> #include <string> using namespace std; class FirstOGLApplication { public: FirstOGLApplication(int argc, char * argv[]); FirstOGLApplication(int width, int height, int argc, char * argv[]); FirstOGLApplication(int width, int height, int startX, int startY, int argc, char * argv[]); void Run(); void Run(string strHeader); private: int _width; int _height; int _startX; int _startY; static void DrawMyself(); static void DrawLine(float x1, float y1, float x2, float y2, float r, float g, float b, int width); };
  • 35.
    http://www.slideshare.net/IgorShkulipa 35 Пример OpenGLприложения (конструкторы) FirstOGLApplication::FirstOGLApplication(int argc, char * argv[]) { _width=100; _height=100; _startX=100; _startY=100; this->InitGL(argc, argv); } FirstOGLApplication::FirstOGLApplication(int width, int height, int argc, char * argv[]) { _width=width; _height=height; _startX=100; _startY=100; this->InitGL(argc, argv); } FirstOGLApplication::FirstOGLApplication(int width, int height, int startX, int startY, int argc, char * argv[]) { _width=width; _height=height; _startX=startX; _startY=startY; this->InitGL(argc, argv); }
  • 36.
    http://www.slideshare.net/IgorShkulipa 36 Пример OpenGLприложения (метод InitGL) void FirstOGLApplication::InitGL(int argc, char* argv[]) { // Инициализация GLUT glutInit(&argc, argv); // Установка режима отображения (RGBA-палитра) glutInitDisplayMode(GLUT_RGBA); // Установка размера окна glutInitWindowSize(_width, _height); // Установка изначальной позиции окна glutInitWindowPosition(_startX, _startY); }
  • 37.
    http://www.slideshare.net/IgorShkulipa 37 Пример OpenGLприложения (методы запуска) void FirstOGLApplication::Run() { this->Run(""); } void FirstOGLApplication::Run(string strHeader) { // Создание окна с заголовком glutCreateWindow(strHeader.c_str()); // Очистка цветом 0х00000000 (черный) glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Запуск функции DrawMyself glutDisplayFunc(FirstOGLApplication::DrawMyself); // Запуск приложения в режим ожидания событий glutMainLoop(); }
  • 38.
    http://www.slideshare.net/IgorShkulipa 38 Пример OpenGLприложения (рисуем себя) void FirstOGLApplication::DrawMyself() { glClear(GL_COLOR_BUFFER_BIT); // Линия синего цвета толщиной 1 DrawLine(-0.5, -0.5, -0.5, 0.5, 0, 0, 1, 1); // Линия зеленого цвета толщиной 2 DrawLine(-0.5, 0.5, 0.5, 0.5, 0, 1, 0, 2); // Линия красного цвета толщиной 3 DrawLine( 0.5, 0.5, 0.5, -0.5, 1, 0, 0, 3); // Линия белого цвета толщиной 4 DrawLine( 0.5, -0.5, -0.5, -0.5, 1, 1, 1, 4); glFlush(); glutSwapBuffers(); }
  • 39.
    http://www.slideshare.net/IgorShkulipa 39 Пример OpenGLприложения (рисование линии) void FirstOGLApplication::DrawLine(float x1, float y1, float x2, float y2, float r, float g, float b, int width) { // Устанавливаем цвет glColor3f(r, g, b); // Устанавливаем толщину линии glLineWidth(width); // Рисуем линию glBegin(GL_LINES); // Координаты точек в относительных величинах с центром в центре окна glVertex2f(x1, y1); glVertex2f(x2, y2); glEnd(); }
  • 40.
    http://www.slideshare.net/IgorShkulipa 40 Пример OpenGLприложения (функция main) int main(int argc, char * argv[]) { FirstOGLApplication* app= new FirstOGLApplication(500, 500, 100, 100, argc, argv); app->Run("Hello, OpenGL!"); return 0; }
  • 41.
    http://www.slideshare.net/IgorShkulipa 41 Пример OpenGLприложения (результат)
  • 42.
    http://www.slideshare.net/IgorShkulipa 42 Лабораторная работа№13. Клиент-сервер Создать консольный калькулятор с архитектурой «клиент- сервер». Клиент: ⚫ Отправляет данные на сервер с запросом о выполнении арифметических операций над числами. ⚫ Выводит меню для управления вычислениями. ⚫ Выводит результаты на экран. Сервер: ⚫ Обрабатывает запрос клиента и отправляет ему результат