Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Использовать WinSock 2 или...? 
:(
    Опции темы
KPEHDEL
Дата 4.2.2011, 12:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



Здравствуйте! Требуется создать архитектуру клиент -> сервер С++.
Подскажите пожалуйста, что нынче используют для этого WinSock или есть какие-то более современнее, продвинутые программы.
Требуется создать две простейшие проги, сервер ставится на один комп, клиент на другой, клиент пингует сервер и получает ответа чёнить типа "Пинг естЬ" или "Пинга нет". 
Заранее спасибо, жду вашего ответа"
PM MAIL WWW ICQ   Вверх
KaraKum
Дата 4.2.2011, 12:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



WinSock немножко виндовая - для компиляции в Линуксе нужно будет 2 строчки заменить, а среди кросс-платформенных есть ENet (UDP), Boost asio.
PM MAIL WWW   Вверх
boostcoder
Дата 4.2.2011, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



asio.
libevent.

PM WWW   Вверх
KPEHDEL
  Дата 6.2.2011, 01:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



Перерыл весь инет вроде получилось собрать клиент и сервер, переписал немного код под себя,всё работает:
Сервер:
Код

/*© CyberGod*/
#include <iostream>//для использования стандарных операторов типо cout
#include <winsock2.h> 
using namespace std;//прямой доступ к операторам типо std::cout<<endl;
int i_error=0;//будет хранить код ошибки
int main ()
{
        puts("Program Works");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) ERROR! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - configured");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET SSock/*создаем прототип*/=socket(AF_INET,/*исп.при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(SSock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//необходимо сокет связать с IP-адресом компьютера.
        struct sockaddr_in addr;//создаем протатип(addr), структуры sockaddr_in
        addr.sin_family=AF_INET;//при создании Интернет-приложений
        addr.sin_port=htons(5001);//порт
        addr.sin_addr.s_addr=htonl(INADDR_ANY);//без конкретного адреса
        /*если мы захотим фактически задать IP-адрес мы должны использовать функцию inet_addr(). inet_addr ("129.42.12.241")*/
        i_error = bind(/*при усп. bind возвращает 0, в прот. случае - "-1"*/SSock,(SOCKADDR*)&addr,/*addr - указ. на структуру sockaddr_in*/sizeof(addr)/*размер структуры*/);
        if(i_error==-1) puts("2) ERROR! socket is not connected with the IP-address of the computer!"); else puts("2) The socket is successfully connected to the IP-address of the computer.");
//3. Инициализация процесса «прослушивания» порта, приложение-сервер будет следить за всеми подключениями на заданный порт
        puts("3) Initialization process of listening port.");
        listen(SSock, 6000);//сколько запросов могут стоять в очереди
//4. подключение клиента - Получение дескриптора клиентского соединения,сервер создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
//Если функция accept выполнилась успешно - возвращает новый сокет для установленного соединения, в противном случае  INVALID_SOCKET
        int size_client_addr=sizeof(addr_klienta);//размер структуры для клиента
        SOCKET ClientSock/*создаем прототип сокета для подкл. клиента*/=accept(SSock,/*сокет*/(sockaddr*)&addr_klienta,/*адрес структуры (!)sockaddr_in куда будет помещена инф. о клиенте*/&size_client_addr/*Длинна адреса*/);
/*Метод Socket.Accept извлекает из очереди первый запрос и возвращает новый объект Socket, который можно использовать для коммуникационного взаимодействия с клиентом.*/
/*accept будет возвращена информация о клиенте. 
Нам будет важна только:
addr.sin_port – порт с которого было подключение
addr.sin_addr – ip клиента в двоичном формате. Для преобразования в строковой формат используется функция inet_ntoa. Т.е. ip:pchar; ip:=inet_ntoa(addr.sin_addr);*/
        if(ClientSock==INVALID_SOCKET) puts("4) ERROR! client connection."); else puts("4) The client is connected.");
//5. Передача-чтение данных
//чтение
//--------
/*Здесь надо написать что-то, для того чтобы можно было присоединить несколько Klientov*/
//--------
        char c_data_ot_k[1024];
        recv(ClientSock,/*сокет*/c_data_ot_k,/*буфер куда поподут данные*/1024,/*размер буфера*/0/*делаем аналогичной функциb read*/);
        cout<<"5) Were the findings: "<<c_data_ot_k;
        char c_data_s_go_k[]="Hello klient";
        //отправ
        send(ClientSock,/*сокет*/c_data_s_go_k,/*данные*/sizeof(c_data_s_go_k),0/*делаем аналогичной функции write*/);
//6. Закрытие сокета
        closesocket (SSock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}


Клиент:
Код

/*© CyberGod*/
#include <iostream>//для использования стандарных операторов типо cout
#pragma comment(lib, "ws2_32")//включаем библиотеку для сокетов
#include <winsock2.h>
using namespace std;//прямой доступ к операторам типо std::cout<<endl;
int i_error=0;//будет хранить код ошибки
int main ()
{
        puts("K L I E N T: Program Works!");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) Error! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - cofigured.");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET my_sock=socket(AF_INET,/*используется при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(my_sock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//При установке соединения -клиент может указать в качестве адреса как DNS-имя, так и IP-адрес сервера.
//INADDR_ANY сервер работает НА машине, с любым ip
//Если мы захотим фактически задать IP-адрес Мы должны использовать функцию inet_addr(). inet_addr ("77.235.103.45")
        struct sockaddr_in  dest_addr;//создаем протатип(dest_addr), структуры sockaddr_in
        dest_addr.sin_family=AF_INET;//при создании Интернет-приложений
        dest_addr.sin_port=htons (5001); //порт
        // Преобразование IP -адреса. servIP - адрес сервера
        char *servIP = "127.0.0.1";//задаем ip
        dest_addr.sin_addr.s_addr = inet_addr(servIP);
//4. Установка связи
        connect(my_sock,/*сокет*/(sockaddr*)&dest_addr,/*dest_addr -указатель на структуру*/sizeof(dest_addr)/*размер*/);
//5. Передача-чтение данных
        char c_data_k_go_s[]="V2 kliento";
//отправка
        send(my_sock,/*сокет*/c_data_k_go_s,/*данные*/sizeof(c_data_k_go_s),0/*делаем аналогичной как функция write*/);
//чтение
        char c_data_ot_s[1024];
        recv(my_sock,/*сокет*/c_data_ot_s,/*буфер куда поподут данные*/1024,/*буфера-сколько считать*/0/*делаем аналогичной функции read*/);
        cout<<"5) Were received from the server data:"<<c_data_ot_s;
//6. Закрытие сокета
        closesocket (my_sock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}


Но получается что к серверу может подцепится только один клиент, если попробывать запустить второй клиент, приложение ответа от сервера не получит.
Не сложно догадаться, что требуется дописать только сервер. 
С этим у меня и проблема не совсем пойму какие действия должны выполняться, чтобы сервер позволял приконнектится нескольким клиентам (winSock ток начал изучать).
В коде сервера выделил для этого место, есть такой комментарий, туда надо дописать:
//--------
/*Здесь надо написать что-то, для того чтобы можно было присоединить несколько Klientov*/
//--------
Помогите понять какие действия требуется дописать, если будут примеры, буду благодарен. Заранее спасибо.
PM MAIL WWW ICQ   Вверх
KaraKum
Дата 6.2.2011, 01:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



В приведённом примере сервер ждёт только одно подключение, после чего получает только одно сообщение от клиента и завершается, а нужно чтобы процесс получения сообщений длился вечно:
Код

//вечный цикл:
while(true)
{
     //получения сообщения от какого-либо подключённого клиента:
     recv();
     //здесь какая-либо обработка полученного сообщения; какие-то действия; отправка сообщения клиенту, от которого получено текущее сообщение, или другим клиентам...
}

PM MAIL WWW   Вверх
KPEHDEL
Дата 6.2.2011, 07:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



Дописал сервер, клиент не изменял, всё работает. Вот изменёный код сервера:
Код

#include <iostream>//для использования стандарных операторов типо cout
#include <winsock2.h> 
using namespace std;//прямой доступ к операторам типо std::cout<<endl;
int i_error=0;//будет хранить код ошибки
int main ()
{
        puts("S E R V E R: Program Works");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) ERROR! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - configured");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET SSock/*создаем прототип*/=socket(AF_INET,/*исп.при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(SSock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//необходимо сокет связать с IP-адресом компьютера.
        struct sockaddr_in addr;//создаем протатип(addr), структуры sockaddr_in
        addr.sin_family=AF_INET;//при создании Интернет-приложений
        addr.sin_port=htons(5001);//порт
        addr.sin_addr.s_addr=htonl(INADDR_ANY);//без конкретного адреса
        /*если мы захотим фактически задать IP-адрес мы должны использовать функцию inet_addr(). inet_addr ("129.42.12.241")*/
        i_error = bind(/*при усп. bind возвращает 0, в прот. случае - "-1"*/SSock,(SOCKADDR*)&addr,/*addr - указ. на структуру sockaddr_in*/sizeof(addr)/*размер структуры*/);
        if(i_error==-1) puts("2) ERROR! socket is not connected with the IP-address of the computer!"); else puts("2) The socket is successfully connected to the IP-address of the computer.");
//3. Инициализация процесса «прослушивания» порта, приложение-сервер будет следить за всеми подключениями на заданный порт
        puts("3) Initialization process of listening port.");
        listen(SSock, 3);//сколько запросов могут стоять в очереди
//4. подключение клиента - Получение дескриптора клиентского соединения,сервер создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
        while(true)
        {
//Если функция accept выполнилась успешно - возвращает новый сокет для установленного соединения, в противном случае  INVALID_SOCKET
        int size_client_addr=sizeof(addr_klienta);//размер структуры для клиента
        SOCKET ClientSock/*создаем прототип сокета для подкл. клиента*/=accept(SSock,/*сокет*/(sockaddr*)&addr_klienta,/*адрес структуры (!)sockaddr_in куда будет помещена инф. о клиенте*/&size_client_addr/*Длинна адреса*/);
        
/*Метод Socket.Accept извлекает из очереди первый запрос и возвращает новый объект Socket, который можно использовать для коммуникационного взаимодействия с клиентом.*/
/*Нам будет важна только:
addr.sin_port – порт с которого было подключение
addr.sin_addr – ip клиента в двоичном формате. Для преобразования в строковой формат используется функция inet_ntoa. Т.е. ip:pchar; ip:=inet_ntoa(addr.sin_addr);*/
        if(ClientSock==INVALID_SOCKET) puts("4) ERROR! client connection."); else puts("4) The client is connected.");
//5. Передача-чтение данных
//чтение
        char c_data_ot_k[1024];
        recv(ClientSock,/*сокет*/c_data_ot_k,/*буфер куда поподут данные*/1024,/*размер буфера*/0/*делаем аналогичной функциb read*/);
        cout<<"5) Were the findings: "<<c_data_ot_k;
        char c_data_s_go_k[]="Hello klient";
        //отправ
        send(ClientSock,/*сокет*/c_data_s_go_k,/*данные*/sizeof(c_data_s_go_k),0/*делаем аналогичной функции write*/);
        }
//6. Закрытие сокета
        closesocket (SSock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}

Посмотрите, всё ли записанно граматно, или может можно записать в чём то более правильно. Укажите на ошибки) Хочется приблизить написание к более правильному)
И ещё вопрос, как правильнее указать ограничение на количество присоединённых клиентов?
PM MAIL WWW ICQ   Вверх
KaraKum
Дата 6.2.2011, 14:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



В примере всё верно, однако сервер отправляет клиенту лишь одно сообщение и то сразу после подключения оного, но не удастся реализовать "произвольное общение", например как это происходит в играх, базах данных и прочее. Для этого, поддержку соединения с каждым клиентом нужно отправлять в отдельный поток, а в текущем потоке продолжать "слушать" попытки создания входящих соединений.
Код

/** Данные для передачи в соединение.*/
class OpaqueData
{
     SOCKET socket;
     /** Любые данные о клиенте, известные в момент соединения. Если программируется игра, то это объект, связывающие соединение с игроком в игровом мире и прочее...*/
     void* someData;
};
/** Здесь поддерживается одно-единственное соединение. По такой функции на каждое входящее соединение.*/
void* ThreadFunction(void* opaqueData)
{
     OpaqueData* castData = (OpaqueData*)opaqueData;
     while(true)
     {
           //здесь происходит постоянное чтение данных именно от этого клиента:
           recv(opaqueData->socket, buf, ...);
           printf("received ... from ...\n", buf, opaqueData->someData);
     }
}

int main()
{
     while(true)
     {
          SOCKET connection = accept();
          OpaqueData* opaqueData = new OpaqueData(connection, new SomeData);
          //отправляем поддержку соединения в отдельный поток
          CreateThread(..., ..., ThreadFunction, opaqueData, ...);
          //продолжаем получать соединения...
     }
}

PM MAIL WWW   Вверх
KaraKum
Дата 6.2.2011, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



А вот при использовании ENet отдельных потоков для каждого клиента создавать не нужно  smile 
PM MAIL WWW   Вверх
boostcoder
Дата 6.2.2011, 18:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(KaraKum @  6.2.2011,  14:49 Найти цитируемый пост)
А вот при использовании ENet отдельных потоков для каждого клиента создавать не нужно

для чего привлекать такую экзотику?
libevent и asio умеют так же ;)
PM WWW   Вверх
KPEHDEL
Дата 6.2.2011, 20:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



Примерно понял, но не совсем... немного пожанглировал, получилось чё-то типа этого(прекрасно понимаю, что не правильно):
Код

void* someData; 
}; 
/** Здесь поддерживается одно-единственное соединение. По такой функции на каждое входящее соединение.*/ 
void* ThreadFunction(void* opaqueData) 
{ 
OpaqueData* castData = (OpaqueData*)opaqueData; 
while(true) 
{ 
        //здесь происходит постоянное чтение данных именно от этого клиента: 
        recv(opaqueData->socket, buf, 1024,0); 
        printf("received ... from ...\n", buf, opaqueData->someData); 
} 
} 
int i_error=0;//будет хранить код ошибки
int main ()
{
        puts("S E R V E R: Program Works");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) ERROR! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - configured");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET SSock/*создаем прототип*/=socket(AF_INET,/*исп.при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(SSock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//необходимо сокет связать с IP-адресом компьютера.
        struct sockaddr_in addr;//создаем протатип(addr), структуры sockaddr_in
        addr.sin_family=AF_INET;//при создании Интернет-приложений
        addr.sin_port=htons(5001);//порт
        addr.sin_addr.s_addr=htonl(INADDR_ANY);//без конкретного адреса
        /*если мы захотим фактически задать IP-адрес мы должны использовать функцию inet_addr(). inet_addr ("129.42.12.241")*/
        i_error = bind(/*при усп. bind возвращает 0, в прот. случае - "-1"*/SSock,(SOCKADDR*)&addr,/*addr - указ. на структуру sockaddr_in*/sizeof(addr)/*размер структуры*/);
        if(i_error==-1) puts("2) ERROR! socket is not connected with the IP-address of the computer!"); else puts("2) The socket is successfully connected to the IP-address of the computer.");
//3. Инициализация процесса «прослушивания» порта, приложение-сервер будет следить за всеми подключениями на заданный порт
        puts("3) Initialization process of listening port.");
        listen(SSock, 3);//сколько запросов могут стоять в очереди
//4. подключение клиента - Получение дескриптора клиентского соединения,сервер создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
        while(true)
        {
        SOCKET connection = accept(); 
        OpaqueData* opaqueData = new OpaqueData(connection, new SomeData); 
        //отправляем поддержку соединения в отдельный поток
        char c_data_s_go_k[]="Hello klient"; 
        send(ClientSock,/*сокет*/c_data_s_go_k,/*данные*/sizeof(c_data_s_go_k),0/*делаем аналогичной функции write*/); 
        CreateThread(..., ..., ThreadFunction, opaqueData, ...); 
        //продолжаем получать соединения... 
        char c_data_ot_k[1024]; 
        recv(ClientSock,/*сокет*/c_data_ot_k,/*буфер куда поподут данные*/1024,/*размер буфера*/0/*делаем аналогичной функциb read*/); 
        }
//6. Закрытие сокета
        closesocket (SSock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}

Помогите добиться правильного результата))
ENet, libevent и asio  обязательно посмотрю, после того как с этим разбирусь  smile 

Это сообщение отредактировал(а) KPEHDEL - 6.2.2011, 20:07
PM MAIL WWW ICQ   Вверх
KaraKum
Дата 6.2.2011, 20:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



Цитата(boostcoder @ 6.2.2011,  19:18)
Цитата(KaraKum @  6.2.2011,  14:49 Найти цитируемый пост)
А вот при использовании ENet отдельных потоков для каждого клиента создавать не нужно

для чего привлекать такую экзотику?
libevent и asio умеют так же ;)

ENet не экзотичнее чем какие-либо другие.
PM MAIL WWW   Вверх
KaraKum
Дата 6.2.2011, 21:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



Функции recv() достаточно только в ThreadFunction() - больше нигде её вызывать не нужно. В остальном всё верно.
А что нужно реализовать? Какие действия должны выполняться? В чём цель написания этого клиент-сервера? Если что-то из этого знать, то можно посоветовать что-то по-конкретнее.
А вообще архитектура клиент-сервера такая (для TCP):
   - сервер:
       - поднятие сервера
       - ожидание подключений
       - поддержка каждого созданного подключения (обычно в отдельном потоке)
       - возможность узнать от какого конкретно клиента приходят данные и возможность отправить данные любому из текуще подключённых клиентов
   - клиент:
       - подключение к серверу и поддержка одного-единственного соединения
       - отправка/получение данных
В последнем варианте, в принципе, всё это реализовано.
PM MAIL WWW   Вверх
KPEHDEL
Дата 6.2.2011, 23:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



Изначально цель была просто изучение, возникла эта цель вспонтанно, когда проснулся утром в общаге, не знал чем заняться.
Теперь вот думаю, что цель можно будет поставить как клиент-сервер для игры. 

Оставил recv() только в ThreadFunction(), удалил из вайла в main().
Всё ещё остались следующие вопросы:
Не пойму что надо вбить в CreateThread(..., ..., ThreadFunction, opaqueData, ...); за место троеточий.
И например вот здесь: recv(opaqueData->socket, buf, 1024,0); за место buf тоже надо использовать размер буфера например 1024?
SOCKET connection = accept(); - функция accept ведь не может быть пустой.
Правильно ли сделал, что в нижепредставленном коде вынес SOCKET socket,SSock;//как глобальную переменную, так как используется не только в функции?
Где лучше объявить addr_klienta?
Изменённый код сервера:
Код

#include <iostream>//для использования стандарных операторов типо cout
#include <winsock2.h> 
using namespace std;//прямой доступ к операторам типо std::cout<<endl;
int i_error=0;//будет хранить код ошибки
SOCKET socket,SSock;//как глобальную переменную, так как используется не только в функции
/** Данные для передачи в соединение.*/ 
class OpaqueData
{ /** Любые данные о клиенте, известные в момент соединения. Если программируется игра, то это объект, связывающие соединение с игроком в игровом мире и прочее...*/
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
        int size_client_addr=sizeof(addr_klienta);//размер структуры для клиента
        socket=accept(SSock,(sockaddr*)&addr_klienta,&size_client_addr);
        if (socket==INVALID_SOCKET) puts("4) ERROR! client connection."); else puts("4) The client is connected.\n");
void* someData; 
}; 
/* Здесь поддерживается одно-единственное соединение. По такой функции на каждое входящее соединение.*/ 
void* ThreadFunction(void* opaqueData) 
{ 
OpaqueData* castData = (OpaqueData*)opaqueData; 
while(true) 
{ 
        //здесь происходит постоянное чтение данных именно от этого клиента: 
        recv(opaqueData->socket, buf, 1024,0); 
        printf("received ... from ...\n", buf, opaqueData->someData); 
} 
} 
int main ()
{
        puts("S E R V E R: Program Works");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) ERROR! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - configured");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET SSock/*создаем прототип*/=socket(AF_INET,/*исп.при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(SSock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//необходимо сокет связать с IP-адресом компьютера.
        struct sockaddr_in addr;//создаем протатип(addr), структуры sockaddr_in
        addr.sin_family=AF_INET;//при создании Интернет-приложений
        addr.sin_port=htons(5001);//порт
        addr.sin_addr.s_addr=htonl(INADDR_ANY);//без конкретного адреса
        /*если мы захотим фактически задать IP-адрес мы должны использовать функцию inet_addr(). inet_addr ("129.42.12.241")*/
        i_error = bind(/*при усп. bind возвращает 0, в прот. случае - "-1"*/SSock,(SOCKADDR*)&addr,/*addr - указ. на структуру sockaddr_in*/sizeof(addr)/*размер структуры*/);
        if(i_error==-1) puts("2) ERROR! socket is not connected with the IP-address of the computer!"); else puts("2) The socket is successfully connected to the IP-address of the computer.");
//3. Инициализация процесса «прослушивания» порта, приложение-сервер будет следить за всеми подключениями на заданный порт
        puts("3) Initialization process of listening port.");
        listen(SSock, 3);//сколько запросов могут стоять в очереди
//4. подключение клиента - Получение дескриптора клиентского соединения,сервер создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
        while(true)
        {
        SOCKET connection = accept(); 
        OpaqueData* opaqueData = new OpaqueData(connection, new SomeData); 
        //отправляем поддержку соединения в отдельный поток
        char c_data_s_go_k[]="Hello klient"; 
        send(socket,/*сокет*/c_data_s_go_k,/*данные*/sizeof(c_data_s_go_k),0/*делаем аналогичной функции write*/); 
        CreateThread(..., ..., ThreadFunction, opaqueData, ...); 
        }
//6. Закрытие сокета
        closesocket (SSock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}

Так как C++ изучаю относитльно недавно, бывают сложности с синтаксом.
Синтаксические ошибки на которые ругается Visual Studio 2010:
[/quote]1>Server_V0.01.cpp(10): error C2327: OpaqueData::addr_klienta: не является именем типа, статическим членом или перечислителем
1>Server_V0.01.cpp(10): error C2065: addr_klienta: необъявленный идентификатор
1>Server_V0.01.cpp(10): error C2070: 'unknown-type': недопустимый оператор sizeof
1>Server_V0.01.cpp(10): error C2864: OpaqueData::size_client_addr: в пределах класса могут быть инициализированы только статические константные данные-члены целого типа
1>Server_V0.01.cpp(11): error C2327: OpaqueData::addr_klienta: не является именем типа, статическим членом или перечислителем
1>Server_V0.01.cpp(11): error C2065: addr_klienta: необъявленный идентификатор
1>Server_V0.01.cpp(11): error C2327: OpaqueData::size_client_addr: не является именем типа, статическим членом или перечислителем
1>Server_V0.01.cpp(11): error C2065: size_client_addr: необъявленный идентификатор
1>Server_V0.01.cpp(11): error C4430: отсутствует спецификатор типа - предполагается int. Примечание. C++ не поддерживает int по умолчанию
1>Server_V0.01.cpp(11): error C2864: OpaqueData::socket: в пределах класса могут быть инициализированы только статические константные данные-члены целого типа
1>Server_V0.01.cpp(12): error C2059: синтаксическая ошибка: if
1>Server_V0.01.cpp(12): error C2238: непредвиденные лексемы перед ";"
1>Server_V0.01.cpp(12): error C2059: синтаксическая ошибка: else
1>Server_V0.01.cpp(12): error C2238: непредвиденные лексемы перед ";"
1>Server_V0.01.cpp(22): error C2227: выражение слева от "->socket" должно указывать на тип класса, структуры или объединения либо на универсальный тип
1>          тип: void *
1>Server_V0.01.cpp(22): error C2065: buf: необъявленный идентификатор
1>Server_V0.01.cpp(23): error C2065: buf: необъявленный идентификатор
1>Server_V0.01.cpp(23): error C2227: выражение слева от "->someData" должно указывать на тип класса, структуры или объединения либо на универсальный тип
1>          тип: void *
1>Server_V0.01.cpp(54): error C2660: accept: функция не принимает 0 аргументов
1>Server_V0.01.cpp(55): error C2061: синтаксическая ошибка: идентификатор "SomeData"
1>Server_V0.01.cpp(55): error C2661: OpaqueData::OpaqueData: нет перегруженной функции, принимающей 2 аргументов
1>Server_V0.01.cpp(58): error C2664: send: невозможно преобразовать параметр 1 из "SOCKET (__stdcall *)(int,int,int)" в "SOCKET"
1>          Не существует контекста, в котором такое преобразование возможно
1>Server_V0.01.cpp(59): error C2143: синтаксическая ошибка: отсутствие ")" перед "..."
1>Server_V0.01.cpp(59): error C2660: CreateThread: функция не принимает 0 аргументов
1>Server_V0.01.cpp(59): error C2059: синтаксическая ошибка: )

Вот это всё надо мне отладить, чтобы успокоится)))) С вашей помощью получится точно smile 
PM MAIL WWW ICQ   Вверх
KaraKum
Дата 7.2.2011, 00:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

Репутация: 1
Всего: 1



Ну, приведённый мною код не подразумевался компилироваться  smile  - это был, как бы, общий пример.
В некомпилируемости много причин, можно начать "отлаживать" со следующего:
    - в OpaqueData нужно передавать только ту информацию, которую хочется - в данном случае нужно передавать только SOCKET чтобы можно было вызвать функцию recv() в ThreadFunction()
    - SOCKET socket переменная не может быть глобальной - она конкретна для каждого отдельного соединения, поэтому её и нужно передавать в функцию ThreadFunction()
    - в объявлении класса (в данном случае OpaqueData) не должны вызываться функции
    - CreateThread() вызывать так: CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunction, opaqueData, 0, NULL);

Это сообщение отредактировал(а) KaraKum - 7.2.2011, 01:11
PM MAIL WWW   Вверх
KPEHDEL
Дата 7.2.2011, 09:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 138
Регистрация: 31.3.2010

Репутация: нет
Всего: нет



В OpaqueData чтобы передовался только сокет стираем две верхних строчки:
class OpaqueData
{ 
        socket=accept(SSock,(sockaddr*)&addr_klienta,&size_client_addr);
        if (socket==INVALID_SOCKET) puts("4) ERROR! client connection."); else puts("4) The client is connected.\n");
void* someData; 
}; 
Так? Подправил CreateThread. Убрал обозначение сокет переменных как глобальных. В исправлении дальнейших ошибок по прежнему путаюсь.
Код:
Код

#include <iostream>//для использования стандарных операторов типо cout
#include <winsock2.h> 
using namespace std;//прямой доступ к операторам типо std::cout<<endl;
int i_error=0;//будет хранить код ошибки
/** Данные для передачи в соединение.*/ 
class OpaqueData
{ /** Любые данные о клиенте, известные в момент соединения. Если программируется игра, то это объект, связывающие соединение с игроком в игровом мире и прочее...*/
        int socket=accept(SSock,(sockaddr*)&addr_klienta,&size_client_addr);
        if (socket==INVALID_SOCKET) puts("4) ERROR! client connection."); else puts("4) The client is connected.\n");
void* someData; 
}; 
/* Здесь поддерживается одно-единственное соединение. По такой функции на каждое входящее соединение.*/ 
void* ThreadFunction(void* opaqueData) 
{ 
OpaqueData* castData = (OpaqueData*)opaqueData;
while(true) 
{ 
        //здесь происходит постоянное чтение данных именно от этого клиента: 
        recv(opaqueData->socket, buf, 1024,0); 
        printf("received ... from ...\n", buf, opaqueData->someData); 
} 
} 
int main ()
{
        puts("S E R V E R: Program Works");
//0. настройка библиотеки Ws2_32.dll
//В случае успеха WSAStartup возвращает 0; иначе - код ошибки. 
        WSADATA wsaData;//определяем переменную
        i_error = WSAStartup(MAKEWORD(2,2),&wsaData);//настраиваем
        if (i_error) cout<<"0) ERROR! "<<WSAGetLastError()<<"; 2 method: "<<i_error; else puts("0) Library Ws2_32.dll - configured");
//1. создание серверного сокета
//В случае успеха socket возвращает номер сокет-дескриптора. иначе -1
        SOCKET SSock/*создаем прототип*/=socket(AF_INET,/*исп.при создании Интернет-приложений*/SOCK_STREAM,/*потоковый*/IPPROTO_TCP/*ТСР - для потоковых*/);
        if(SSock==-1) puts("1) ERROR! - the socket is not created!"); else puts("1) The socket is successfully created.");
//2. Привязка к локальным именам
//необходимо сокет связать с IP-адресом компьютера.
        struct sockaddr_in addr;//создаем протатип(addr), структуры sockaddr_in
        addr.sin_family=AF_INET;//при создании Интернет-приложений
        addr.sin_port=htons(5001);//порт
        addr.sin_addr.s_addr=htonl(INADDR_ANY);//без конкретного адреса
        /*если мы захотим фактически задать IP-адрес мы должны использовать функцию inet_addr(). inet_addr ("129.42.12.241")*/
        i_error = bind(/*при усп. bind возвращает 0, в прот. случае - "-1"*/SSock,(SOCKADDR*)&addr,/*addr - указ. на структуру sockaddr_in*/sizeof(addr)/*размер структуры*/);
        if(i_error==-1) puts("2) ERROR! socket is not connected with the IP-address of the computer!"); else puts("2) The socket is successfully connected to the IP-address of the computer.");
//3. Инициализация процесса «прослушивания» порта, приложение-сервер будет следить за всеми подключениями на заданный порт
        puts("3) Initialization process of listening port.");
        listen(SSock, 3);//сколько запросов могут стоять в очереди
//4. подключение клиента - Получение дескриптора клиентского соединения,сервер создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'
        struct sockaddr_in addr_klienta;//создаем прототип структуры для подключающегося клиента
        while(true)
        {
        SOCKET connection = accept(); 
        OpaqueData* opaqueData = new OpaqueData(connection, new SomeData); 
        //отправляем поддержку соединения в отдельный поток
        char c_data_s_go_k[]="Hello klient"; 
        send(socket,/*сокет*/c_data_s_go_k,/*данные*/sizeof(c_data_s_go_k),0/*делаем аналогичной функции write*/); 
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunction, opaqueData, 0, NULL);
        }
//6. Закрытие сокета
        closesocket (SSock);//закрытие
        WSACleanup();// выгружаем WinSock
        puts("\n\nTo complete the program, press Enter.");
        getchar();
        return 0;
}

PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0621 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.