Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Win Api
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 14.09.2009, 10:47   #1
AndreySt
 
Регистрация: 13.09.2009
Сообщений: 3
По умолчанию Сокеты: блокируемые или асинхронные?

Доброго времени суток!
Столкнулся с затруднением, решая простую задачу получения UDP-пакетов.

Есть приложение (плагин - multithreaded dll), чья задача - прослушивать определенный порт, получая UDP-дейтаграммы. Когда родительский процесс завершается, он уведомляет предварительно мой плагин. Тот должен как можно быстрее вернуть управление - необязательно даже дожидаться завершения текущей операции чтения.

Текущая реализация: блокируемый recv() в цикле, цикл крутится в отдельном потоке. По команде QuitListen() выполняю shutdown(), в потоке прослушки recv() возвращает -1, ошибка - Interrupted function call(10004), там же взводится событие SetEvent(). Получив это событие, метод QuitListen() возвращает управление основному потоку.

Вариант, конечно, неудобоваримый - как минимум, не устраивает интервал 6 секунд между shutdown() и возвратом из recv().

В поисках решения много гуглил, но в результате цельной картины так и не сложилось. Читал о трех решениях: 1) неблокируемые сокеты и постоянный опрос - не рекомендовано 2) блокируемые? сокеты и select() - так и не нашел подходящего примера кода 3) неблокируемые сокеты и...?

Буду признателен за любую помощь в вопросе, желательно - с отсылкой к примеру.
Спасибо заранее.

Последний раз редактировалось AndreySt; 14.09.2009 в 10:51.
AndreySt вне форума Ответить с цитированием
Старый 14.09.2009, 13:26   #2
AndreySt
 
Регистрация: 13.09.2009
Сообщений: 3
По умолчанию

Поменял shutdown на closesocket - закрывает сразу, получаю ту же ошибку 10004 и выхожу из цикла. Все ж - некрасиво.
AndreySt вне форума Ответить с цитированием
Старый 14.09.2009, 16:24   #3
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию

Итак, два варианта:
1. У вас Виста, тогда вам подойдут асинхронные сокеты и функция WSAPoll. WSAPoll на MSDN
2. У вас Не Виста. Вам подойдет - асинхронные сокеты и select.
Пример:
Заголовочный файл
Код:
#ifndef SOCKET_H
#define SOCKET_H

#include <WinSock2.h>

#pragma comment(lib, "ws2_32")

#include <string>

enum TypeSocket 
{
	BlockingSocket, 
	NonBlockingSocket
};

class Socket 
{
public:
	virtual ~Socket();
	Socket(const Socket &);
	Socket& operator=(Socket &);

	std::string ReceiveLine();
	std::string ReceiveBytes();

	void   Close();

	void   SendLine (std::string);
	void   SendBytes(const std::string &);

protected:
	friend class SocketServer;
	friend class SocketSelect;

	Socket(SOCKET s);
	Socket();

	SOCKET s_;
	int* refCounter_;

private:
	static void Start();
	static void End();
	static int  nofSockets_;
};

class SocketSelect //вот то, что вам нужно
{
public:
	SocketSelect(Socket const * const s1, Socket const * const s2 = NULL, TypeSocket type = BlockingSocket);

	bool Readable(Socket const * const s);

private:
	fd_set fds_;
}; 
#endif
Собственно реализация
Код:
#include "Socket.h"
#include <iostream>

using namespace std;

int Socket::nofSockets_ = 0;

void Socket::Start() 
{
	if (!nofSockets_) 
	{
		WSADATA info;
		if (WSAStartup(MAKEWORD(2,0), &info)) 
		{
			throw "Could not start WSA";
		}
	}
	++nofSockets_;
}

void Socket::End() 
{
	WSACleanup();
}

Socket::Socket() 
: s_(0) 
{
	Start();
	s_ = socket(AF_INET,SOCK_STREAM,0);

	if (s_ == INVALID_SOCKET) 
	{
		throw "INVALID_SOCKET";
	}

	refCounter_ = new int(1);
}

Socket::Socket(SOCKET s) 
: s_(s) 
{
	Start();
	refCounter_ = new int(1);
};

Socket::~Socket() 
{
	if (! --(*refCounter_)) 
	{
		Close();
		delete refCounter_;
	}
	--nofSockets_;
	if (!nofSockets_) 
		End();
}

Socket::Socket(const Socket& o) 
{
	refCounter_ = o.refCounter_;
	(*refCounter_)++;
	s_ = o.s_;

	nofSockets_++;
}

Socket& Socket::operator=(Socket& o) 
{
	(*o.refCounter_)++;

	refCounter_ = o.refCounter_;
	s_ = o.s_;

	nofSockets_++;

	return *this;
}

void Socket::Close() 
{
	closesocket(s_);
}

std::string Socket::ReceiveBytes() 
{
	std::string ret;
	char buf[1024];

	while (1) 
	{
		u_long arg = 0;
		if (ioctlsocket(s_, FIONREAD, &arg) != 0)
			break;

		if (arg == 0)
			break;

		if (arg > 1024) 
			arg = 1024;

		int rv = recv (s_, buf, arg, 0);
		if (rv <= 0) 
			break;

		std::string t;

		t.assign (buf, rv);
		ret += t;
	}
	return ret;
}

std::string Socket::ReceiveLine() 
{
	std::string ret;
	while (1) 
	{
		char r;
		switch(recv(s_, &r, 1, 0)) 
		{
		case 0: // not connected anymore;
		  // ... but last line sent
		  // might not end in \n,
		  // so return ret anyway.
			return ret;
		case -1:
			return "";
		  //      if (errno == EAGAIN) {
		  //        return ret;
		  //      } else {
		  //      // not connected anymore
		  //      return "";
		  //      }
		}

		ret += r;
		if (r == '\n')  
			return ret;
	}
}

void Socket::SendLine(std::string s) 
{
	s += '\n';
	send(s_,s.c_str(),s.length(),0);
}

void Socket::SendBytes(const std::string& s) 
{
	send(s_,s.c_str(),s.length(),0);
}
SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) 
{
	FD_ZERO(&fds_);
	FD_SET(const_cast<Socket*>(s1)->s_, &fds_);
	if(s2) 
		FD_SET(const_cast<Socket*>(s2)->s_,&fds_);

	TIMEVAL tval;
	tval.tv_sec  = 0;
	tval.tv_usec = 1;

	TIMEVAL *ptval;
	if(type == NonBlockingSocket) 
		ptval = &tval;
	else 
		ptval = 0;

	if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR) 
		throw "Error in select";
}

bool SocketSelect::Readable(Socket const* const s) 
{
	if (FD_ISSET(s->s_, &fds_)) 
		return true;
	return false;
}
Собственно, чтобы переделать под UDP протокол - в инициализации сокета поставьте SOCK_DGRAM вместо SOCK_STREAM
MaTBeu вне форума Ответить с цитированием
Старый 14.09.2009, 17:00   #4
AndreySt
 
Регистрация: 13.09.2009
Сообщений: 3
По умолчанию

Цитата:
Сообщение от MaTBeu Посмотреть сообщение
2. У вас Не Виста. Вам подойдет - асинхронные сокеты и select.

MaTBeu, спасибо. Буду пробовать асинхронные сокеты + select(). Здесь вы имеете в виду опрос состояния сокета select-ом и получение данных recv, когда в нем что-то есть, или есть еще варианты?

P.S. Давно не кодил на C++ - трудно сообразить, как воспользоваться классом SocketSelect из вашего примера.

Последний раз редактировалось AndreySt; 14.09.2009 в 17:03.
AndreySt вне форума Ответить с цитированием
Старый 14.09.2009, 18:43   #5
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию

Исходники взяты отсюда
MaTBeu вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Сокеты SL1CK Общие вопросы C/C++ 8 05.10.2009 13:20
Сокеты IICuX123 Общие вопросы .NET 6 27.07.2009 01:27
Сокеты Raz0r Помощь студентам 2 29.04.2009 14:47
Открытые сокеты или перманентные запросы Квэнди Свободное общение 4 30.01.2008 21:18
сокеты cross Работа с сетью в Delphi 2 21.12.2006 18:12