//в этом буфере будет запрос серверу
char bufferSend[128] = "";
/*Сформируем запрос. Большинство серверов воспринимают пустой запрос GET / как запрос на страничку index.htm. В конце запроса ОБЯЗАТЕЛЬНО должна идти пустая строка, иначе сервер не прекратит нас слушать.*/
strcpy(bufferSend, "GET / HTTP/1.0\r\n");
strcat(bufferSend, "\r\n");
//спрашиваем
nret = send(theSocket, bufferSend, strlen(bufferSend), 0);
//проверяем на ошибки
if (nret == SOCKET_ERROR)
{
hr = HRESULT_FROM_WIN32(WSAGetLastError());
ReportError(hr, "send()");
WSACleanup();
return;
}
Мы задали вопрос и должны ждать и принимать ответ. Сначала создадим на диалоговом окне два поля EditBox - одно побольше, другое поменьше. Добавим большому полю переменную m_strSimpleDisplay – здесь будет выдаваться полученная информация. А маленькому полю добавим переменную m_strReturned – тут будем выдавать количество полученных байт.
m_strSimpleDisplay = "";
m_strReturned = "";
UpdateData(FALSE);
Начнём принимать информацию
while (1)
{
nret = recv(theSocket, bufferRecv, sizeof(bufferRecv), 0);
//заполняем поле m_strReturned
char charRet[128] = ””;
itoa(nret, charRet, 10);
strcat(charRet, " bytes\r\n");
m_strReturned += ret;
//заполняем поле m_strSimpleDisplay
m_strSimpleDisplay += bufferRecv;
UpdateData(FALSE);
if (nret == 0 || nret == -1)
break;
}
Компилируем, запускаем и нажимаем кнопку SendRequest. Смотрим, каким образом присылаются данные.
Пример простого TCP-эхо-сервера
 #include <stdio.h>
 #include <winsock2.h> // Wincosk2.h должен быть раньше windows!
 #include <windows.h>
 #define MY_PORT 666 // Порт, который слушает сервер 666
 // макрос для печати количества активных пользователей
 #define PRINTNUSERS if (nclients) printf("%d user on-line\n", nclients); \
 else printf("No User on line\n");
 // прототип функции, обслуживающий подключившихся пользователей
DWORD WINAPI SexToClient(LPVOID client_socket);
 // глобальная переменная - количество активных пользователей
int nclients = 0;
int main(int argc, char* argv[])
 {
 char buff[1024]; // Буфер для различных нужд
 printf("TCP SERVER DEMO\n");
 // Шаг 1 - Инициализация Библиотеки Сокетов
 // т.к. возвращенная функцией информация не используется
 // ей передается указатель на рабочий буфер, преобразуемый к указателю
 // на структуру WSADATA.
 // Такой прием позволяет сэкономить одну переменную, однако, буфер
 // должен быть не менее полкилобайта размером (структура WSADATA // занимает 400 байт)
 if (WSAStartup(0x0202, (WSADATA *)&buff[0]))
 {
 // Ошибка!
 printf("Error WSAStartup %d\n", WSAGetLastError());
 return -1;
 }
 // Шаг 2 - создание сокета
 SOCKET mysocket;
 // AF_INET - сокет Интернета
 // SOCK_STREAM - потоковый сокет (с установкой соединения)
 // 0 - по умолчанию выбирается TCP протокол
 if ((mysocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 {
 // Ошибка!
 printf("Error socket %d\n", WSAGetLastError());
 WSACleanup(); // Деиницилизация библиотеки Winsock
 return -1;
 }
 // Шаг 3 - связывание сокета с локальным адресом
 sockaddr_in local_addr;
 local_addr.sin_family = AF_INET;
 local_addr.sin_port = htons(MY_PORT); // не забываем о сетевом порядке!!!
 local_addr.sin_addr.s_addr = 0; // сервер принимает подключения
 // на все свои IP-адреса
 // вызываем bind для связывания
 if (bind(mysocket, (sockaddr *)&local_addr, sizeof(local_addr)))
 {
 // Ошибка
 printf("Error bind %d\n", WSAGetLastError());
 closesocket(mysocket); // закрываем сокет!
 WSACleanup();
 return -1;
 }
 // Шаг 4 - ожидание подключений
 // размер очереди - 0x100
 if (listen(mysocket, 0x100))
 {
 // Ошибка
 printf("Error listen %d\n", WSAGetLastError());
 closesocket(mysocket);
 WSACleanup();
 return -1;
 }
 printf("Ожидание подключений...\n");
 // Шаг 5 - извлекаем сообщение из очереди
 SOCKET client_socket; // сокет для клиента
 sockaddr_in client_addr; // адрес клиента (заполняется системой)
 // функции accept необходимо передать размер структуры
 int client_addr_size = sizeof(client_addr);
 // цикл извлечения запросов на подключение из очереди
 while ((client_socket = accept(mysocket, (sockaddr *)&client_addr, \
 &client_addr_size)))
 {
 nclients++; // увеличиваем счетчик подключившихся клиентов
 // пытаемся получить имя хоста
 HOSTENT *hst;
 hst = gethostbyaddr((char *)&client_addr. sin_addr.s_addr, 4, AF_INET);
 // вывод сведений о клиенте
 printf("+%s [%s] new connect!\n",
 (hst) ? hst->h_name : "", inet_ntoa(client_addr.sin_addr));
 PRINTNUSERS
 // Вызов нового потока для обслужвания клиента
 // Да, для этого рекомендуется использовать _beginthreadex
 // но, поскольку никаких вызовов функций стандартной Си библиотеки
 // поток не делает, можно обойтись и CreateThread
 DWORD thID;
 CreateThread(NULL, NULL, SexToClient, &client_socket, NULL, &thID);
 }
 return 0;
 }
 // Эта функция создается в отдельном потоке
 // и обсуживает очередного подключившегося клиента независимо от остальных
 DWORD WINAPI SexToClient(LPVOID client_socket)
 {
 SOCKET my_sock;
 my_sock = ((SOCKET *)client_socket)[0];
 char buff[20 * 1024];
 #define sHELLO "Hello, Sailor\r\n"
 // отправляем клиенту приветствие
 send(my_sock, sHELLO, sizeof(sHELLO), 0);
 // цикл эхо-сервера: прием строки от клиента и возвращение ее клиенту
 int bytes_recv;
 while ((bytes_recv = recv(my_sock, &buff[0], sizeof(buff), 0)) &&
 bytes_recv != SOCKET_ERROR)
 send(my_sock, &buff[0], bytes_recv, 0);
 // если мы здесь, то произошел выход из цикла по причине
 // возращения функцией recv ошибки - соединение с клиентом разорвано
 nclients--; // уменьшаем счетчик активных клиентов
 printf("-disconnect\n"); PRINTNUSERS
 // закрываем сокет
 closesocket(my_sock);
 return 0;
 } 
// Пример простого TCP-клиента
 #include <stdio.h>
 #include <string.h>
 #include <winsock2.h>
 #include <windows.h>
 #define PORT 666
 #define SERVERADDR "127.0.0.1"
 int main(int argc, char* argv[])
 {
 char buff[1024];
 printf("TCP DEMO CLIENT\n");
 // Шаг 1 - инициализация библиотеки Winsock
 if (WSAStartup(0x202, (WSADATA *)&buff[0]))
 {
 printf("WSAStart error %d\n", WSAGetLastError());
 return -1;
 }
 // Шаг 2 - создание сокета
 SOCKET my_sock;
 my_sock = socket(AF_INET, SOCK_STREAM, 0);
 if (my_sock < 0)
 {
 printf("Socket() error %d\n", WSAGetLastError());
 return -1;
 }
 // Шаг 3 - установка соединения
 // заполнение структуры sockaddr_in - указание адреса и порта сервера
 sockaddr_in dest_addr;
 dest_addr.sin_family = AF_INET;
 dest_addr.sin_port = htons(PORT);
 HOSTENT *hst;
 // преобразование IP адреса из символьного в сетевой формат
 if (inet_addr(SERVERADDR) != INADDR_NONE)
 dest_addr.sin_addr.s_addr = inet_addr(SERVERADDR);
 else
 {
 // попытка получить IP адрес по доменному имени сервера
 if (hst = gethostbyname(SERVERADDR))
 // hst->h_addr_list содержит не массив адресов,
 // а массив указателей на адреса
 ((unsigned long *)&dest_addr.sin_addr)[0] =
 ((unsigned long **)hst->h_addr_list)[0][0];
 else
 {
 printf("Invalid address %s\n", SERVERADDR);
 closesocket(my_sock);
 WSACleanup();
 return -1;
 }
 }
 // адрес сервера получен - пытаемся установить соединение
 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);
 // Шаг 4 - чтение и передача сообщений
 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], strlen(&buff[0]), 0);
 }
 printf("Recv error %d\n", WSAGetLastError());
 closesocket(my_sock);
 WSACleanup();
 return -1;
 }
Доработать программу задания темы №1.
Клиент должен сформировать пакет данных для расчётов на сервере и передать их по каналу связи. Формат сообщений разработать самостоятельно.
Научиться создавать простые многопоточные приложения на базе операционной системы Linux.
1. Рассмотреть представленный пример, и разработать приложение на его основе.
2. Разработать и реализовать алгоритм решения второго задания, с учетом разделения вычислений между несколькими потоками. Избегать ситуаций изменения одних и тех же общих данных несколькими потоками.
1. W. R. Stevens, S. A. Rago, Advanced Programming in the UNIX® Environment: Second Edition, Addison Wesley Professional, 2005
2. D. P. Bovet, M. Cesati, Understanding the Linux Kernel, 3rd Edition, O'Reilly, 2005
3. А. Боровский. «Потоки». http://www.citforum.ru/programming/unix/threads/
Прежде чем приступать к программированию потоков, следует ответить на вопрос, а нужны ли они вам. С помощью управления процессами в Linux можно решить многие задачи, которые в других ОС решаются только с помощью потоков. Потоки часто становятся источниками программных ошибок особого рода. Эти ошибки возникают при использовании потоками разделяемых ресурсов системы (например, общего адресного пространства) и являются частным случаем более широкого класса ошибок – ошибок синхронизации. Если задача разделена между независимыми процессами, то доступом к их общим ресурсам управляет операционная система, и вероятность ошибок из-за конфликтов доступа снижается. Впрочем, разделение задачи между несколькими независимыми процессами само по себе не защитит вас от других разновидностей ошибок синхронизации. В пользу потоков можно указать то, что накладные расходы на создание нового потока в многопоточном приложении ниже, чем накладные расходы на создание нового самостоятельного процесса. Уровень контроля над потоками в многопоточном приложении выше, чем уровень контроля приложения над дочерними процессами. Кроме того, многопоточные программы не склонны оставлять за собой вереницы зомби или «осиротевших» независимых процессов.
Первая подсистема потоков в Linux появилась около 1996 года и называлась, без лишних затей, – LinuxThreads. Рудимент этой подсистемы, который вы найдете в любой современной системе Linux, – файл /usr/include/pthread.h, указывает год релиза – 1996 и имя разработчика – Ксавье Лерой (Xavier Leroy). Библиотека LinuxThreads была попыткой организовать поддержку потоков в Linux в то время, когда ядро системы еще не предоставляло никаких специальных механизмов для работы с потоками. Позднее разработку потоков для Linux вели сразу две конкурирующие группы – NGPT и NPTL. В 2002 году группа NGPT фактически присоединилась к NPTL и теперь реализация потоков NPTL является стандартом Linux. Подсистема потоков Linux стремится соответствовать требованиям стандартов POSIX, так что новые многопоточные приложения Linux должны без проблем компилироваться на новых POSIX-совместимых системах.