2016-10-26 2 views
1

Я пытаюсь связаться с несколькими службами SQL Server Browser, используя следующий код c + object-c.UDP-код получает только первый ответ на широковещательную рассылку

+ (void) doBrowseForServers 
{ 
    // http://mbielanczuk.com/2013/07/browsing-network-for-sql-server-instances-in-c/ 
    // used as a reference. 
    SOCKET sock; 
    int broadcast = 1; 

    struct timeval timeout; 
    timeout.tv_sec = 5; // 10 second timeout 
    timeout.tv_usec = 0; 

    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 
    { 
     NSLog(@"MSSQLBrowserServiceClient failed to initialize socket."); 
     return; 
    } 

    // Setting socket broadcast option. 
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)) == -1) 
    { 
     NSLog(@"MSSQLBrowserServiceClient failed to set broadcast option."); 
     return; 
    } 

    // Set a timeout so that we do not wait forever. 
    if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) == -1) 
    { 
     NSLog(@"MSSQLBrowserServiceClient failed to set send timeout.", errno); 
     return; 
    } 

    // Set a timeout so that we do not wait forever. 
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1) 
    { 
     NSLog(@"MSSQLBrowserServiceClient failed to set receive timeout.", errno); 
     return; 
    } 

    if(sendBroadcast(sock, MSSQL_BROWSER_SERVICE_PORT)) 
    { 
     NSString * response = [self reciveBroadcastRespondFromSocket: sock 
                  usingPort: MSSQL_BROWSER_SERVICE_PORT]; 

     NSDate * lastSeen = [NSDate date]; 

     if(0 != response.length) 
     { 
      // Processing code omitted to focus on send/receive 
     } // End of we had responses 
    } 
    else 
    { 
     NSLog(@"MSSQLBrowserServiceClient failed to browse for servers."); 
    } 
} // End of beginBrowseForServers 

bool sendBroadcast(SOCKET sock, int port) 
{ 
    struct sockaddr_in bcastAddr; 
    memset(&bcastAddr, 0, sizeof(bcastAddr)); 
    bcastAddr.sin_family = AF_INET; 
    bcastAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); 
    bcastAddr.sin_port = htons(port); 
    char query = 0x02; 
    if (sendto(sock, &query, sizeof(query), 0, (struct sockaddr *)&bcastAddr, sizeof(bcastAddr)) == -1) 
    { 
     return 0; 
    } 

    return 1; 
} 

+ (NSString*) reciveBroadcastRespondFromSocket: (SOCKET) sock 
            usingPort: (int) port 
{ 
    ssize_t recvBytes = 0; 
    char buf[512] = {0x00}; 
    memset(buf,'\0', 512); 

    NSMutableData * mutableData = [NSMutableData data]; 

    struct sockaddr_in recvAddr; 
    memset(&recvAddr, 0, sizeof(recvAddr)); 
    recvAddr.sin_family = AF_INET; 
    recvAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    recvAddr.sin_port = htons(port); 
    socklen_t recvAddrLen = sizeof(recvAddr); 

    while ((recvBytes = recvfrom(sock, buf, sizeof(buf)-1, 0, (struct sockaddr*)&recvAddr, &recvAddrLen)) != -1) 
    { 
     [mutableData appendBytes: buf 
          length: recvBytes]; 
    } 

    if(mutableData.length <= 3) 
    { 
     return nil; 
    } 

    char * bytes = (char*)[mutableData bytes]; 

    // Process code removed to focus on networking bits. 
    // .... 

    return @""; 
} 

Проблема в том, что я, кажется, обрабатываю только один ответ сервера за раз. Если я буду запускать этот код несколько раз, я могу получать результаты с разных серверов каждый раз (один сервер на трансляцию), но я понимаю, что я должен получать dudagrams udp со всех серверов через одну трансляцию. Я попытался увеличить мой тайм-аут, но я все еще получаю результаты только от одного сервера на трансляцию.

Может ли кто-нибудь указать, что я делаю неправильно?

ответ

1

Я считаю, что виновник находится в цикле while ((recvBytes = recvfrom. Я думаю, что он заканчивается ошибкой.

Одна типичная «ошибка» для широковещательных передач UDP - WSAECONNRESET. Я точно не знаю, что это значит, что-то о порте назначения недоступно. Тем не менее, это ошибка, которая обычно возникает в широковещательной передаче UDP. Поскольку эта ошибка в значительной степени ожидается и связана с одной из целей широковещательной передачи вместо вашего кода, решение будет состоять в том, чтобы игнорировать эту конкретную ошибку и продолжить цикл до следующего recvfrom (вы на самом деле «делаете» это, когда вы запускаете код несколько раз). Некоторые ОС позволяют настроить сокет для автоматического игнорирования WSAECONNRESET. Например, в Windows это будет WSAIoctl(SIO_UDP_CONNRESET)

Если это не поможет вам, пожалуйста, предоставьте дополнительную информацию о том, как работает ваш код: сколько циклов повторяется, не дожидается ли до истечения тайм-аута, в какой именно кончике ошибок заканчивается цикл.

1

Вы должны проверить errno после возврата возвращенного -1. Это укажет, не были ли доступны данные и вызван ли вызов из-за таймаута (EAGAIN) или если произошла некоторая ошибка (например, во втором вызове, когда вы ожидаете данные с другого сервера). Скорее всего, ваши данные recvAddr и/или recvAddrLen были изменены после первого (успешного) вызова и становятся недействительными при втором вызове. Вы должны (повторно) инициализировать эти переменные при каждом вызове.

Обратите внимание, что вы инициализируете recvAddr значением INADDR_ANY, которое будет перезаписано IP-адресом сервера после первого вызова. Если recvfrom будет использовать этот адрес в качестве индикатора того, что он должен получать только от этого IP-адреса, он объяснит поведение, которое вы видите. Однако стандартная реализация recvfrom не использует это значение адреса.

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