2010-11-13 3 views
0

У меня есть сервер и клиентские классы, но проблема в том, что: когда я делаю бесконечный цикл для приема входящего соединения, я не могу получить все данные, полученные от клиента, принимая при этом соединения, потому что принимают блоки до тех пор, пока соединение не будет принято, код:Сокеты вопрос

for (;;) 
    { 
     boost::thread thread(boost::bind(&Irc::Server::startAccept, &s)); 
     thread.join(); 
     for (ClientsMap::const_iterator it = s.begin(); it != s.end(); ++it) 
     { 
      std::string msg = getData(it->second->recv()); 
      std::clog << "Msg: " << msg << std::endl; 
     } 
    } 
+1

Какой смысл начинать нить и присоединяться к ней сразу? –

+0

Я думал, что соединение ждет завершения нити? – 2010-11-13 19:28:42

+3

Это значит, что за один раз работает только один поток, полностью побеждая цель. –

ответ

3

Вам нужно либо несколько потоков или вызов select/poll, чтобы выяснить, какие соединения имеют необработанные данные. IBM has a nice example here, который будет работать над любым вкусом Unix, Linux, BSD и т. Д. (Возможно, вам понадобятся разные файлы заголовков в зависимости от ОС).

Прямо сейчас вы начинаете поток, а затем немедленно его ожидаете, что приводит к последовательному выполнению и полностью нарушает цель потоков.

+0

, как я должен это делать, можете ли вы привести пример? – 2010-11-13 19:33:59

+0

Вот пример использования select, я все еще ищу хороший опрос, который является более новым вариантом: http://www.gnu.org/s/libc/manual/html_node/Server-Example.html# Пример сервера –

+0

Вот пример с 'poll', обработка ошибок, все работы: http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/rzab6/example.htm –

0

Хорошим подходом было бы создать один поток, который принимает только новые соединения. Вот где у вас есть сокет-слушатель. Затем для каждого принятого соединения у вас есть новый подключенный сокет, поэтому вы можете создать другой поток, предоставив ему подключенный сокет в качестве параметра. Таким образом, ваш поток, который принимает соединения, не блокируется и может очень быстро подключаться ко многим клиентам. Потоки обработки связаны с клиентами, а затем они выходят.

Я даже не знаю, зачем им их ждать, но если вы это сделаете, вы можете иметь дело с ним каким-либо другим способом, в зависимости от используемой ОС и/или библиотек (сообщения, сигналы и т. Д. используемый).

Если вы не хотите создавать новый поток для каждого подключенного клиента, то, как предложил Бен Фойгт, вы можете использовать select. Это еще один хороший подход, если вы хотите сделать его однопоточным. В принципе, все ваши сокеты будут в массиве дескрипторов сокетов и с помощью select вы узнаете, что произошло (кто-то подключен, сокет готов для чтения/записи, сокет отключен и т. Д.) И действовать соответственно.

Вот один пример Частичный, но он работает. вы просто принимаете соединения в acceptConnections(), которые затем генерируют отдельный поток для каждого клиента. Здесь вы общаетесь с клиентами. Это из окна кода, который я лежал, но его очень легко переопределить для любой платформы.

typedef struct SOCKET_DATA_ { 
    SOCKET sd; 
    /* other parameters that you may want to pass to the clientProc */ 
} SOCKET_DATA; 

/* In this function you communicate with the clients */ 
DWORD WINAPI clientProc(void * param) 
{ 
    SOCKET_DATA * pSocketData = (SOCKET_DATA *)param; 

    /* Communicate with the new client, and at the end deallocate the memory for 
     SOCKET_DATA and return. 
    */ 

    delete pSocketData; 
    return 0; 
} 

int acceptConnections(const char * pcAddress, int nPort) 
{ 
    sockaddr_in sinRemote; 
    int nAddrSize; 
    SOCKET sd_client; 
    SOCKET sd_listener; 
    sockaddr_in sinInterface; 
    SOCKET_DATA * pSocketData; 
    HANDLE hThread; 

    sd_listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

    if (INVALID_SOCKET == sd_listener) { 
     fprintf(stderr, "Could not get a listener socket!\n"); 
     return 1; 
    } 

    sinInterface.sin_family = AF_INET; 
    sinInterface.sin_port = nPort; 
    sinInterface.sin_addr.S_un.S_addr = INADDR_ANY; 

    if (SOCKET_ERROR != bind(sd_listener, (sockaddr*)&sinInterface, sizeof(sockaddr_in))) { 
     listen(sd_listener, SOMAXCONN); 
    } else { 
     fprintf(stderr, "Could not bind the listening socket!\n"); 
     return 1; 
    } 

    while (1) 
    { 
     nAddrSize = sizeof(sinRemote); 
     sd_client = accept(sd_listener, (sockaddr*)&sinRemote, &nAddrSize); 

     if (INVALID_SOCKET == sd_client) { 
      fprintf(stdout, "Accept failed!\n"); 
      closesocket(sd_listener); 
      return 1; 
     } 

     fprintf(stdout, "Accepted connection from %s:%u.\n", inet_ntoa(sinRemote.sin_addr), ntohs(sinRemote.sin_port)); 
     pSocketData = (SOCKET_DATA *)malloc(sizeof(SOCKET_DATA)); 

     if (!pSocketData) { 
      fprintf(stderr, "Could not allocate memory for SOCKET_DATA!\n"); 
      return 1; 
     } 

     pSocketData->sd = sd_client; 
     hThread = CreateThread(0, 0, clientProc, pSocketData, 0, &nThreadID); 

     if (hThread == INVALID_HANDLE_VALUE) { 
      fprintf(stderr, "An error occured while trying to create a thread!\n"); 
      delete pSocketData; 
      return 1; 
     } 
    } 

    closesocket(sd_listener); 
    return 0; 
} 
+0

не могли бы вы привести мне пример? Я не понимаю тебя. – 2010-11-13 20:09:13

+0

Я действительно не думаю, что кто-то предлагал использовать поток для каждого подключенного клиента ... –

+0

Хорошая вещь об одном потоке для каждого клиента заключается в том, что легко отслеживать независимое состояние для каждого клиента. Тем не менее, планирование всех потоков не так эффективно, как «опрос», и если у вас есть данные, разделяемые между клиентами, очень легко ввести условия гонки (а добавление мьютексов или другая синхронизация для исправления условий гонки ухудшит производительность). –

0

Посмотрите здесь: http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio/examples.html

особенно HTTP Server 3 пример, вот именно то, что вы ищете, все, что вам нужно сделать, это изменить, что код немного для ваших нужд:) и ваш результат

+0

так async_accept не блокирует? – 2010-11-13 20:48:25

+0

Да, конечно, каждый конец каждой асинхронной операции является неблокирующей операцией. В большинстве случаев, когда вы используете операции async (функции), вам даже не нужно использовать потоки вообще, это может упростить ваш дизайн + удаляет дополнительные накладные расходы из-за некоторого переключения контекста, вызванного добавленными потоками –

+0

HTTP-сервер, где соединения обслуживают запросы почти полностью независимо друг от друга, не все это отличная основа для использования в чат-сервере. Но на странице 'boost :: asio' также есть пример чата. –

Смежные вопросы