Страница 1 из 1
C++ Неблокирующие сокеты + select
Добавлено: 16 янв 2015, 22:52
Dima2387
Читаю статью о сокетах на 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'
Самое интересное что не компилится он на моем домашнем компе. На работе все компилируется без проблем.
Re: C++ Неблокирующие сокеты + select
Добавлено: 16 янв 2015, 23:31
Olej
Dima2387 писал(а):Читаю статью о сокетах на rsdn и решил попробовать скомпилить пример. Но вот проблема - он не компилится =(
Прежде всего, ... никогда не читайте, а уж тем более не компилируйте с RSDN - они там родились в Windows, понимают только в Windows ... и умрут они в Windows.
Re: C++ Неблокирующие сокеты + select
Добавлено: 16 янв 2015, 23:38
Dima2387
#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 в местах где он конкретно нужен
Re: C++ Неблокирующие сокеты + select
Добавлено: 16 янв 2015, 23:47
Olej
Ну, вот эти места:
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.
Re: C++ Неблокирующие сокеты + select
Добавлено: 16 янв 2015, 23:49
Olej
Dima2387 писал(а):Самое интересное что не компилится он на моем домашнем компе. На работе все компилируется без проблем.
Так вы бы написали, какие: дистрибутивы Linux + их версии + версии компилятора GCC стоят на одном и на другом компьютере.
... а ещё и разрядность: 32 или 64 ... что-то мне подсказывает, что напоролись вы именно на разрядность, и именно с итератором.
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.
Это свободно распространяемое программное обеспечение. Условия копирования
приведены в исходных текстах. Без гарантии каких-либо качеств, включая
коммерческую ценность и применимость для каких-либо целей.
Re: C++ Неблокирующие сокеты + select
Добавлено: 18 янв 2015, 00:12
Olej
Гость писал(а):На работе
xubuntu x64
Больше на память инфы не помню
Дома
[dima@acer6292 system]$ cat /etc/release
ROSA release 2012.0 (LTS) for i586
Я же говорил, что наиболее вероятно дело в разрядности 32/64.
Re: C++ Неблокирующие сокеты + select
Добавлено: 19 янв 2015, 22:14
Dima2387
Проблема с расчетом величины переменной
Код: Выделить всё
#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
-----
Помогите пожалуйста правильно расчитать размер переменной.
Re: C++ Неблокирующие сокеты + select
Добавлено: 20 янв 2015, 00:47
Olej
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?
strcat() - тупо дописывает байты в
уже зарезервированную область и ничего больше.
2. ... а вот когда вы сделаете current_time достаточного размера
.... скажем так: char current_time[
120 ]; - то вот тогда смотрим на длины....
только каждый из ваших 5-ти sizeof() возвратит 4 на 32-битной системе и 8 на 64-битной системе, потому что все эти char* - это просто
указатели и ничего более.
3. ... но это всё и не нужно, потому что требуемая вам длина - это всего то: strlen( current_time ) + 1 (+1 - это если вы собираетесь и завершающий '\0' передавать, что я советовал бы делать).
Re: C++ Неблокирующие сокеты + select
Добавлено: 20 янв 2015, 00:59
Olej
4. У вас в коде идёт смешение C POSIX API и C++ типов и механизмов.
(это при сетевом-сокетном программировании им будет неизбежно)
Только идёт оно ... "с точностью до наоборот"
Я уже писал, что за такие вещи - я бы ноги из задницы выдёргивал:
Код: Выделить всё
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() !