2014-01-04 4 views
0

Здравствуйте, снова замечательное сообщество stackoverflow! В прошлый раз, когда я задал вопрос, он был быстро решен сразу же, и я надеюсь, что так будет. :)Winsock2 loop loop

Итак, я играю с winsock, и я хочу иметь возможность одновременно подключать несколько клиентов к моему серверу. Я чувствую, что это может быть достигнуто с помощью цикла создания потоков, создания сокетов, привязки и прослушивания каждый раз, когда клиент подключается, но мои усилия для этого появились только при «прослушивании с ошибкой». Два разных в зависимости от того, что я пытался. Я искал Google только для того, чтобы найти расширенные образцы MSDN, которые являются самыми легкими и еще слишком жесткими, примеры там. У кого-нибудь есть какие-то простые предложения? (Боковой вопрос: я не могу получить «mrecv()», чтобы вернуть всю переменную «recvbuf». Все, что я получаю, это одна буква. I знаю Это ошибка новичка, которую я делаю, но я просто не могу понять:./Эта проблема не может ждать, пока позже, однако)

(Вот код сервера до сих пор :)

#undef UNICODE 

#define WIN32_LEAN_AND_MEAN 

#include <windows.h> 
#include <winsock2.h> 
#include <ws2tcpip.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <iostream> 

// Need to link with Ws2_32.lib 
#pragma comment (lib, "Ws2_32.lib") 
// #pragma comment (lib, "Mswsock.lib") 

int minitialize(); 
int msend(char msendbuf[512]); 
char mrecv(); 
int mshutdown(); 

#define DEFAULT_BUFLEN 512 
#define DEFAULT_PORT "10150" 
WSADATA wsaData; 
int iResult; 

SOCKET ListenSocket = INVALID_SOCKET; 
SOCKET ClientSocket = INVALID_SOCKET; 

struct addrinfo *result = NULL; 
struct addrinfo hints; 

int iSendResult; 
char recvbuf[DEFAULT_BUFLEN]; 
int recvbuflen = DEFAULT_BUFLEN; 

int main(void) 
{ 
    minitialize(); 
    mrecv(); 

    char mmessage[512]; 
    if (strncmp(mmessage,"shutdown",(strlen(mmessage))) == 0) {mshutdown();} 
    std::cin.getline(mmessage, 512); 
    msend(mmessage); 

    // shutdown the connection since we're done 
    mshutdown(); 
    std::cin.ignore(); 

    return 0; 
} 

int msend(char msendbuf[512]) // Send a message 
{ 
    int iResult3 = send(ClientSocket, msendbuf, 512, 0); 
    if (iResult3 == SOCKET_ERROR) { 
     printf("send failed with error: %d\n", WSAGetLastError()); 
     closesocket(ClientSocket); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Bytes Sent: %ld\n", iResult); 
      std::cout<<"msendbuf: "<<msendbuf<<"\n"; 
      std::cin.ignore(); 
} 

char mrecv() //Recieve a message 
{ 
    int iResult2 = recv(ClientSocket, recvbuf, 512, 0); 
     if (iResult2 > 0) { 
      printf("Bytes received: %d\n", iResult2); 
     std::cout<<"recvbuf: "<<recvbuf<<"\n"; 
     } 
     else if (iResult2 == 0) 
      printf("Connection closing...\n"); 
     else { 
      printf("recv failed with error: %d\n", WSAGetLastError()); 
      closesocket(ClientSocket); 
      WSACleanup(); 
      return 1; 
     } 
    return *recvbuf; 
} 

int minitialize() //initialize the winsock server 
{ 
    // Initialize Winsock 
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData); 
    if (iResult != 0) { 
     printf("WSAStartup failed with error: %d\n", iResult); 
     return 1; 
    } 

    ZeroMemory(&hints, sizeof(hints)); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 
    hints.ai_flags = AI_PASSIVE; 

    // Resolve the server address and port 
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); 
    if (iResult != 0) { 
     printf("getaddrinfo failed with error: %d\n", iResult); 
     WSACleanup(); 
     return 1; 
    } 

    // Create a SOCKET for connecting to server 
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 
    if (ListenSocket == INVALID_SOCKET) { 
     printf("socket failed with error: %ld\n", WSAGetLastError()); 
     freeaddrinfo(result); 
     WSACleanup(); 
     return 1; 
    } 

    // Setup the TCP listening socket 
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); 
    if (iResult == SOCKET_ERROR) { 
     printf("bind failed with error: %d\n", WSAGetLastError()); 
     freeaddrinfo(result); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 

    freeaddrinfo(result); 

    iResult = listen(ListenSocket, SOMAXCONN); 
    if (iResult == SOCKET_ERROR) { 
     printf("listen failed with error: %d\n", WSAGetLastError()); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 

    // Accept a client socket 
    ClientSocket = accept(ListenSocket, NULL, NULL); 
    if (ClientSocket == INVALID_SOCKET) { 
     printf("accept failed with error: %d\n", WSAGetLastError()); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 

    // No longer need server socket 
    closesocket(ListenSocket); 
} 

int mshutdown()  //shutdown the server 
{ 
    iResult = shutdown(ClientSocket, SD_SEND); 
    if (iResult == SOCKET_ERROR) { 
     printf("shutdown failed with error: %d\n", WSAGetLastError()); 
     closesocket(ClientSocket); 
     WSACleanup(); 
     return 1; 
    } 

    // cleanup 
    closesocket(ClientSocket); 
    WSACleanup(); 
    return 0; 
} 

Да, если вам интересно, я планирую. о переносе всех этих глобальных переменных в их соответствующие локальные функции. Я просто нуждаюсь в них там, чтобы очистить немного беспорядка, и это, похоже, не вызывает никаких проблем.

+0

Другим способом обработки нескольких клиентов является использование select(), тогда вам не нужны потоки (по крайней мере, для этого) и не будут запускаться в гоночные проблемы или взаимоблокировки по линии. – dutt

ответ

1

mrecv() определяется как

char mrecv().

В функции mrecv(), она возвращает строку. Потому что mrecv() по определению должен возвращать символ, возвращается первый символ recvbuf. Компилятор не будет жаловаться на return *recvbuf. Это синтаксически корректно, даже если цель состоит в том, чтобы вернуть один символ.

accept() возвращает файловый дескриптор к соединению клиента. По возвращении accept() вы можете создать новый процесс для обработки сообщения с использованием этого дескриптора, пока исходный процесс вернется к принятию соединений. Или вы можете создать новый поток для обработки сообщения из этого дескриптора, в то время как исходный поток возобновляет прием соединений. Вам нужно будет зациклиться на accept().

Вот шаблон скелета, который вы можете использовать для сервера. У вас уже все правильно на minitialize(). В приведенном ниже примере кода mrecv() принимает параметр, файловый дескриптор подключенного клиента, newconn.

socket() 
bind() 
listen(mysock, 10); 
while(keep listening) { 
    newconn = accept(mysock, &peeraddr, &peeraddrlen) 
    if(newconn > 0) { 
     if(CreateThread(<security attribute>, <stack size>, (void *)&mrecv, (void *)&newconn, <creation flag>, <threadid>)) 
      perror("Unable to create thread\n"); 
    } 
} 
closesocket(mysock) 
+0

Боюсь, я не могу взять кредит на minitialize(). Или что-нибудь здесь действительно. Все это пример кода MSDN, слегка измененный и разбитый на функции. : P – Phixle

+0

Кроме того, я здесь на окнах. Поэтому pthread придется заменить CreateThread() ...Но какие аргументы я должен передать? Я не совсем уверен, как сохранить переменные (в частности, ClientSocket) отдельно после создания нового потока. И родительский, и дочерний потоки (если это правильная терминология) будут использовать одни и те же глобальные переменные ... И я не уверен, что вижу способ их передачи, чтобы избежать какой-либо проблемы. :/ – Phixle

+0

Для обсуждения давайте назовем их «родителями» и «дочерними». «Parent» будет использовать «ClientSocket» для ожидания соединений. Как только новое соединение будет 'accept'ed, родитель будет' fork' '' функцией' и передаст ему значение 'ClientSocket', давайте вызовите дочернюю переменную' ClientSocketCopy'. Затем функция обработает сообщение из 'ClientSocketCopy'. – alvits

1

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

No. Все, что вам нужно, чтобы это принять клиентское соединение и запустить поток с этим сокетом. Оставляйте гнездо для прослушивания строго в одиночку. Операция accept() не влияет на нее, и нет необходимости ее перестраивать.

+0

Хотелось бы, чтобы появилось «полезное решение». Тот, который был отмечен как правильный, дал код и проницательность, но ваше было по-прежнему очень четким объяснением заявления. :) – Phixle