LINUX.ORG.RU

C++[RAII], c network, recv неоправданный интервал


0

0

Привет. Произвел замеры времени потерь для разных участков кода, заметил плохую тенденцию: на recv вызовах теряется ОЧЕНЬ много времени(чуть менее 1 минуты). Подскажите, что конкретно я не так делаю(простая RAII обертка):

#include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netdb.h> #include <cerrno>

class socket : public sph::types::noncopyable { public: socket(addrinfo* const addr) throw(sph::exceptions::network_connection_error); ~socket() throw(); ssize_t send(const char *buf, size_t len) throw(); void send_all(const char *buf, size_t len = 0) throw(sph::exceptions::network_error); ssize_t recv(char *buf, size_t len) throw(); void set_recv_timeout(unsigned int milliseconds) throw(); protected: addrinfo* _addr; int _socket; private: bool _self_made_addrinfo; bool _socket_created; };

socket::socket(addrinfo* const addr) throw(X) : _addr(addr), _self_made_addrinfo(false), _socket_created(false) { // For each resolved ip for (addrinfo* addr_iterator = _addr; addr_iterator != NULL; addr_iterator = addr_iterator->ai_next) { // Try to create socket _socket = ::socket(addr_iterator->ai_family, addr_iterator->ai_socktype, addr_iterator->ai_protocol); // Error create socket if (_socket == -1) throw(X); // Move next if can't connect if (::connect(_socket, addr_iterator->ai_addr, addr_iterator->ai_addrlen) != 0) continue; _socket_created = true; return; } throw(X); } socket::~socket() throw() { // Free addrinfo only if this class creates it if (_self_made_addrinfo) freeaddrinfo(_addr); // Close socket only if we ever create it if (_socket_created) { shutdown(_socket, SHUT_RDWR); close(_socket); } } void socket::send_all(const char *buf, size_t len) throw(sph::exceptions::network_error) { if (len == 0) len = sph::c::strlen(buf); int send_bytes_count = 0; while(send_bytes_count != len) { int ret = send(buf+send_bytes_count, len - send_bytes_count); // If error occured if (ret == -1) throw(sph::exceptions::network_error(errno)); send_bytes_count += ret; } }

inline ssize_t socket::send(const char *buf, size_t len) throw() { return ::send(_socket, const_cast<char*>(buf), len, 0); } inline ssize_t socket::recv(char *buf, size_t len) throw() { return ::recv(_socket, const_cast<char*>(buf), len, 0); } inline void socket::set_recv_timeout(unsigned int milliseconds) throw() { const struct timeval tv = { milliseconds/1000, milliseconds%1000 }; setsockopt (_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); }

addrinfo получаю следующим образом: const struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; int resolve_code = getaddrinfo("server", "80", &hints, &_addr);

anonymous

Прошу прощения, форматированный код:
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <cerrno>

class socket : public sph::types::noncopyable
{
	public:
		socket(addrinfo* const addr) throw(sph::exceptions::network_connection_error);
		~socket() throw();
	
		ssize_t send(const char *buf, size_t len) throw();
		void send_all(const char *buf, size_t len = 0) throw(sph::exceptions::network_error);
				
		ssize_t recv(char *buf, size_t len) throw();
				
		void set_recv_timeout(unsigned int milliseconds) throw();
		
	protected:
		addrinfo* _addr;
		int _socket;
				
	private:
		bool _self_made_addrinfo;
		bool _socket_created;
};

socket::socket(addrinfo* const addr) throw(X) : _addr(addr), _self_made_addrinfo(false), _socket_created(false)
{
	// For each resolved ip
	for (addrinfo* addr_iterator = _addr; addr_iterator != NULL; addr_iterator = addr_iterator->ai_next)
	{
		// Try to create socket
		_socket = ::socket(addr_iterator->ai_family, addr_iterator->ai_socktype, addr_iterator->ai_protocol);
			
		// Error create socket
		if (_socket == -1)
			throw(X);
			
		// Move next if can't connect
		if (::connect(_socket, addr_iterator->ai_addr, addr_iterator->ai_addrlen) != 0)
			continue;
	
		_socket_created = true;
		return;
	}
			
	throw(X);
}
		
socket::~socket() throw()
{
	// Free addrinfo only if this class creates it
	if (_self_made_addrinfo)
		freeaddrinfo(_addr);
			
	// Close socket only if we ever create it
	if (_socket_created)
	{
		shutdown(_socket, SHUT_RDWR);
		close(_socket);
	}
}
		
void socket::send_all(const char *buf, size_t len) throw(sph::exceptions::network_error)
{
	if (len == 0)
		len = sph::c::strlen(buf);
			
	int send_bytes_count = 0;
	while(send_bytes_count != len)
	{
		int ret = send(buf+send_bytes_count, len - send_bytes_count);
				
		// If error occured
		if (ret == -1)
			throw(sph::exceptions::network_error(errno));
					
		send_bytes_count += ret;
	}
}

inline ssize_t socket::send(const char *buf, size_t len) throw() { return ::send(_socket, const_cast<char*>(buf), len, 0); }
			
inline ssize_t socket::recv(char *buf, size_t len) throw() { return ::recv(_socket, const_cast<char*>(buf), len, 0); }
		
inline void socket::set_recv_timeout(unsigned int milliseconds) throw()
{
	const struct timeval tv = { milliseconds/1000, milliseconds%1000 };
	setsockopt (_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}


addrinfo получаю следующим образом:
const struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
int resolve_code = getaddrinfo("server", "80", &hints, &_addr);

anonymous
()

> что конкретно я не так делаю

Пишешь свой класс-обертку для сокетов. Нефиг изобретать велосипед, ходить по граблям и тратить время впустую. Используй готовые и отлаженные.

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

> Также, приложение ее использующее, критично к нагрузкам, поэтому нужен по сути только recv

recv - это syscall. Затраты на сисколл, даже с учётом использования vdso, гораздо выше, чем есть в каком-нибудь очень приятно выглядящем boost::asio. Как только переход на уровень ядра не будет отъедать столько времени, тогда и можно будет говорить об оверхеде в юзерспейсной плюсатой библиотеке.

mv ★★★★★
()

что значит "теряется время"? Типа прога висит в функции recv и ничего в это время не делает? :)))

anonymous
()

Может recv просто ждёт пока прийдут данные? Сокет то блокирующийся.

Deleted
()

делай ioctlsocket(listen_socket,FIONBIO,&async_mode) для перевода 
сокета в асинхронный режим
используй select для ожидания всех сокетов сразу
как вариант - можно использовать блокирующие сокеты, как у тебя,
и ждать каждый сокет в отдельной нити

alex4
()
Ответ на: комментарий от alex4

лучше не select, а poll/epoll, а еще лучше - не париться с изобретением велосипедов, а взять boost.asio, который имеет высокоуровневый интерфейс, использующий для каждой из конкретных платформ ту стратегию, которая наилучшим образом подходит - epoll/poll/kqueue и т.п.

http://xtalk.msk.su/~ott/ru/cpp/BoostAsioNotes.html & http://xtalk.msk.su/~ott/ru/cpp/BoostAsioProxy.html в качестве примеров можно поглядеть

ott ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.