C++ Неблокирующие сокеты + select

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Dima2387
Интересующийся
Сообщения: 9
Зарегистрирован: 21 ноя 2014, 17:35
Контактная информация:

C++ Неблокирующие сокеты + select

Непрочитанное сообщение Dima2387 » 16 янв 2015, 22:52

Читаю статью о сокетах на rsdn и решил попробовать скомпилить пример. Но вот проблема - он не компилится =(

Код: Выделить всё

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <algorithm>
#include <set>
using namespace std;

int main()
{
    int listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        perror("socket");
        exit(1);
    }
    
    fcntl(listener, F_SETFL, O_NONBLOCK);
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 2);
    
    set<int> clients;
    clients.clear();

    while(1)
    {
        // Заполняем множество сокетов
        fd_set readset;
        FD_ZERO(&readset);
        FD_SET(listener, &readset);

        for(set<int>::iterator it = clients.begin(); it != clients.end(); it++)
            FD_SET(*it, &readset);

        // Задаём таймаут
        timeval timeout;
        timeout.tv_sec = 15;
        timeout.tv_usec = 0;

        // Ждём события в одном из сокетов
        int mx = max(listener, *max_element(clients.begin(), clients.end()));
        if(select(mx+1, &readset, NULL, NULL, &timeout) <= 0)
        {
            perror("select");
            exit(3);
        }
        
        // Определяем тип события и выполняем соответствующие действия
        if(FD_ISSET(listener, &readset))
        {
            // Поступил новый запрос на соединение, используем accept
            int sock = accept(listener, NULL, NULL);
            if(sock < 0)
            {
                perror("accept");
                exit(3);
            }
            
            fcntl(sock, F_SETFL, O_NONBLOCK);

            clients.insert(sock);
        }

        for(set<int>::iterator it = clients.begin(); it != clients.end(); it++)
        {
            if(FD_ISSET(*it, &readset))
            {
                // Поступили данные от клиента, читаем их
                bytes_read = recv(*it, buf, 1024, 0);

                if(bytes_read <= 0)
                {
                    // Соединение разорвано, удаляем сокет из множества
                    close(*it);
                    clients.erase(*it);
                    continue;
                }

                // Отправляем данные обратно клиенту
                send(*it, buf, bytes_read, 0);
            }
        }
    }
    
    return 0;
}
ОШИБКА
/home/dima/Documents/untitled26/main.cpp:31: ошибка: no match for 'operator<' in 'std::bind(_Functor&&, _ArgTypes&& ...) [with _Functor = int&, _ArgTypes = {sockaddr*, unsigned int}, typename std::_Bind_helper<_Functor, _ArgTypes>::type = std::_Bind<int(sockaddr*, unsigned int)>]((* &((sockaddr*)(& addr))), (* &16u)) < 0'


Самое интересное что не компилится он на моем домашнем компе. На работе все компилируется без проблем.
Программирование в Linux с нуля http://linuxdevelop.net/

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 16 янв 2015, 23:31

Dima2387 писал(а):Читаю статью о сокетах на rsdn и решил попробовать скомпилить пример. Но вот проблема - он не компилится =(
Прежде всего, ... никогда не читайте, а уж тем более не компилируйте с RSDN - они там родились в Windows, понимают только в Windows ... и умрут они в Windows. :lol:

Dima2387
Интересующийся
Сообщения: 9
Зарегистрирован: 21 ноя 2014, 17:35
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Dima2387 » 16 янв 2015, 23:38

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <algorithm>
#include <set>

//using namespace std;

int main()
{
int listener;
struct sockaddr_in addr;
char buf[1024];
int bytes_read;

listener = socket(AF_INET, SOCK_STREAM, 0);
if(listener < 0)
{
perror("socket");
exit(1);
}

fcntl(listener, F_SETFL, O_NONBLOCK);

addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
exit(2);
}

listen(listener, 2);

std::set<int> clients;
clients.clear();

while(1)
{
// Заполняем множество сокетов
fd_set readset;
FD_ZERO(&readset);
FD_SET(listener, &readset);

for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
FD_SET(*it, &readset);

// Задаём таймаут
timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;

// Ждём события в одном из сокетов
int mx = std::max(listener, *max_element(clients.begin(), clients.end()));
if(select(mx+1, &readset, NULL, NULL, &timeout) <= 0)
{
perror("select");
exit(3);
}

// Определяем тип события и выполняем соответствующие действия
if(FD_ISSET(listener, &readset))
{
// Поступил новый запрос на соединение, используем accept
int sock = accept(listener, NULL, NULL);
if(sock < 0)
{
perror("accept");
exit(3);
}

fcntl(sock, F_SETFL, O_NONBLOCK);

clients.insert(sock);
}

for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
{
if(FD_ISSET(*it, &readset))
{
// Поступили данные от клиента, читаем их
bytes_read = recv(*it, buf, 1024, 0);

if(bytes_read <= 0)
{
// Соединение разорвано, удаляем сокет из множества
close(*it);
clients.erase(*it);
continue;
}

// Отправляем данные обратно клиенту
send(*it, buf, bytes_read, 0);
}
}
}

return 0;
}


помогло комментирование
//using namespace std;
и вызов std в местах где он конкретно нужен
Программирование в Linux с нуля http://linuxdevelop.net/

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 16 янв 2015, 23:47

Ну, вот эти места:
Dima2387 писал(а):

Код: Выделить всё

for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
            FD_SET(*it, &readset);

Код: Выделить всё

        for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
        {
            if(FD_ISSET(*it, &readset))
            {
                // Поступили данные от клиента, читаем их
                bytes_read = recv(*it, buf, 1024, 0);
- это, в моём понимании просто ужас! - желание обязательно свалить в кучу познания в C++ со стандартными вызовами POSIX C многолетними?
Ну, до поры до времени std::set<int>::iterator it - будет совпадать с целочисленным типом, которого ожидают все операции с дескрипторами ... а если в какой реализации не будет совпадать? ведь этого никто и никогда не гарантировал - чему эквивалентны итераторы STL.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 16 янв 2015, 23:49

Dima2387 писал(а):Самое интересное что не компилится он на моем домашнем компе. На работе все компилируется без проблем.
Так вы бы написали, какие: дистрибутивы Linux + их версии + версии компилятора GCC стоят на одном и на другом компьютере.

... а ещё и разрядность: 32 или 64 ... что-то мне подсказывает, что напоролись вы именно на разрядность, и именно с итератором. :-o

Гость

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Гость » 17 янв 2015, 10:36

На работе

xubuntu x64
Больше на память инфы не помню
Дома

[dima@acer6292 system]$ cat /etc/release
ROSA release 2012.0 (LTS) for i586
[dima@acer6292 system]$ gcc --version
gcc (GCC) 4.6.1 20110627 (Mandriva)
Copyright (C) 2011 Free Software Foundation, Inc.
Это свободно распространяемое программное обеспечение. Условия копирования
приведены в исходных текстах. Без гарантии каких-либо качеств, включая
коммерческую ценность и применимость для каких-либо целей.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 18 янв 2015, 00:12

Гость писал(а):На работе

xubuntu x64
Больше на память инфы не помню
Дома

[dima@acer6292 system]$ cat /etc/release
ROSA release 2012.0 (LTS) for i586
Я же говорил, что наиболее вероятно дело в разрядности 32/64.

Dima2387
Интересующийся
Сообщения: 9
Зарегистрирован: 21 ноя 2014, 17:35
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Dima2387 » 19 янв 2015, 22:14

Проблема с расчетом величины переменной

Код: Выделить всё

#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <algorithm>
#include <set>
#include <iostream>
#include <time.h>
//using namespace std;

int main()

{



    int listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;
    char version[]="server ver.0.0.4\n";
    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        perror("socket");
        exit(1);
    }

    fcntl(listener, F_SETFL, O_NONBLOCK);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 2);

    std::set<int> clients;
    clients.clear();

    while(1)
    {
        // Заполняем множество сокетов
        fd_set readset;
        FD_ZERO(&readset);
        FD_SET(listener, &readset);

        for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
            FD_SET(*it, &readset);

        // Задаём таймаут
        timeval timeout;
        timeout.tv_sec = 9995;
        timeout.tv_usec = 0;

        // Ждём события в одном из сокетов
        int mx = std::max(listener, *max_element(clients.begin(), clients.end()));
        if(select(mx+1, &readset, NULL, NULL, &timeout) <= 0)
        {
            perror("select");
            exit(3);
        }

        // Определяем тип события и выполняем соответствующие действия
        if(FD_ISSET(listener, &readset))
        {
            // Поступил новый запрос на соединение, используем accept
            int sock = accept(listener, NULL, NULL);
            if(sock < 0)
            {
                perror("accept");
                exit(3);
            }

            fcntl(sock, F_SETFL, O_NONBLOCK);

            clients.insert(sock);
        }

        for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
        {
            if(FD_ISSET(*it, &readset))
            {
                // Поступили данные от клиента, читаем их
                bytes_read = recv(*it, buf, 1024, 0);

                if(bytes_read <= 0)
                {
                    // Соединение разорвано, удаляем сокет из множества
                    close(*it);
                    clients.erase(*it);
                    continue;
                }

               // Отправляем данные обратно клиенту
//вычисление текущего времени
                 time_t rawtime;
                 struct tm * timeinfo;
                 char current_time[9];
                 time ( &rawtime );
                 timeinfo = localtime ( &rawtime );
                 strftime (current_time,9,"%H:%M:%S",timeinfo);
//конец вычисления времени
                 // Cклеиваем все переменный вместе чтобы сделать красивый формат
                 char space[] = " ";
                 char prefix[] = "Your message is: ";
                 strcat (current_time, space);
                 strcat (current_time, prefix);
                 strcat (current_time, buf);
                 strcat (current_time, version);
                //Вычисляем размер необходимый для отображения данных
                int current_time_size=sizeof(current_time) + sizeof(space) + sizeof(prefix) + sizeof(bytes_read) + sizeof(version);
                // Высылаем склеенную строку клиенту
                send(*it, current_time, current_time_size, 0);
                //Отображаем эти же данные в консоли сервера
                puts(current_time);



            }
        }
    }

    return 0;
}
По факту обрезает сообщения

$ telnet localhost 3425
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
dima
21:05:03 Your message is: dima
erver ver.0.
21:05:14 Your message is:
ma
erver ver.0.


1. Я присоеденился к серверу командой telnet localhost 3425
2. Я набрал слово dima и нажал энтер. В ответ я увидел

21:05:03 Your message is: dima
erver ver.0.

Вместо ожидаемого

21:05:03 Your message is: dima
server ver.0.0.4

3. Я ничего не вводил и просто нажал энтер и увидел

21:05:14 Your message is:
ma
erver ver.0.

Вместо ожидаемого

21:05:14 Your message is:

Server ver.0.0.4

-----

Помогите пожалуйста правильно расчитать размер переменной.
Программирование в Linux с нуля http://linuxdevelop.net/

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 20 янв 2015, 00:47

Dima2387 писал(а):Проблема с расчетом величины переменной
...
Помогите пожалуйста правильно расчитать размер переменной.
Проблемы в другом ... не с величиной переменной...

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

Код: Выделить всё

                 char current_time[9];
                 time ( &rawtime );
                 timeinfo = localtime ( &rawtime );
                 strftime (current_time,9,"%H:%M:%S",timeinfo);
//конец вычисления времени
                 // Cклеиваем все переменный вместе чтобы сделать красивый формат
                 char space[] = " ";
                 char prefix[] = "Your message is: ";
                 strcat (current_time, space);
                 strcat (current_time, prefix);
                 strcat (current_time, buf);
                 strcat (current_time, version);
                //Вычисляем размер необходимый для отображения данных
                int current_time_size=sizeof(current_time) + sizeof(space) + sizeof(prefix) + sizeof(bytes_read) + sizeof(version);
Ваш буфер current_time имеет длину всего 9 байт, а вы туда последовательными 4-мя strcat() хотите напихать ... байт 30? :-o
strcat() - тупо дописывает байты в уже зарезервированную область и ничего больше.

2. ... а вот когда вы сделаете current_time достаточного размера :lol: .... скажем так: char current_time[ 120 ]; - то вот тогда смотрим на длины....
только каждый из ваших 5-ти sizeof() возвратит 4 на 32-битной системе и 8 на 64-битной системе, потому что все эти char* - это просто указатели и ничего более.

3. ... но это всё и не нужно, потому что требуемая вам длина - это всего то: strlen( current_time ) + 1 (+1 - это если вы собираетесь и завершающий '\0' передавать, что я советовал бы делать).

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++ Неблокирующие сокеты + select

Непрочитанное сообщение Olej » 20 янв 2015, 00:59

4. У вас в коде идёт смешение C POSIX API и C++ типов и механизмов.
(это при сетевом-сокетном программировании им будет неизбежно)
Только идёт оно ... "с точностью до наоборот" :lol:

Я уже писал, что за такие вещи - я бы ноги из задницы выдёргивал:

Код: Выделить всё

    std::set<int> clients;
    clients.clear();
...
        for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
            FD_SET(*it, &readset);
...
        int mx = std::max(listener, *max_element(clients.begin(), clients.end()));
...
        for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
        {
            if(FD_ISSET(*it, &readset))
            {
                // Поступили данные от клиента, читаем их
                bytes_read = recv(*it, buf, 1024, 0);
...
А вот когда идёт манипуляция со строками, конкатенации ... , а ещё далее и более сложные редактирования:

Код: Выделить всё

                 char current_time[9];
...
                 // Cклеиваем все переменный вместе чтобы сделать красивый формат
                 char space[] = " ";
                 char prefix[] = "Your message is: ";
                 strcat (current_time, space);
                 strcat (current_time, prefix);
                 strcat (current_time, buf);
                 strcat (current_time, version);
...
А тут как-раз не совсем уместны C-строки ASCIZ char*, и совершенно уместен был бы тип string C++ (или, если точнее, из STL), над котороым выполняйте легко свои конкатенации и любые редактуры ... и только последней фазой при передаче извлечь из string её C-содержимое методом current_time.c_str() !

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 5 гостей