我正在尝试使用以下c + objective-c代码与多个SQL Server Browser服务进行通信。
+ (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 @"";
}
问题是我似乎一次只处理一个服务器响应。如果我多次运行此代码,我每次最终都会收到来自不同服务器的结果(每个广播一台服务器),但我的理解是我应该通过单个广播从所有服务器接收udp数据报。我已经尝试增加超时但我仍然只收到每个广播一台服务器的结果。
有谁可以指出我做错了什么?
答案 0 :(得分:2)
我认为罪魁祸首是while ((recvBytes = recvfrom
循环。我认为它以错误终止。
UDP广播的一个典型“错误”是WSAECONNRESET
。我不知道这意味着什么,关于目标端口的东西是无法访问的。但是,这是UDP广播中经常发生的错误。由于这个错误是非常期待的,并且它与一个广播目标而不是你的代码相关,解决方案是忽略这个特定错误并继续循环到下一个recvfrom
(你实际上“做”这个当你多次运行代码)。某些操作系统允许配置套接字以自动忽略WSAECONNRESET
。例如,在Windows上,它将是WSAIoctl(SIO_UDP_CONNRESET)
如果这对您没有帮助,请提供有关代码如何运行的更多信息:循环迭代次数,等待超时,确切错误结束循环。
答案 1 :(得分:2)
你应该在recvfrom返回-1后检查errno。这将指示是否没有可用的数据,并且由于超时(EAGAIN)或发生了某些错误而返回了呼叫(例如,当您期望来自另一个服务器的数据时,在第二次呼叫时)。很可能您的recvAddr和/或recvAddrLen数据在第一次(成功)调用后更改,并在第二次调用时变为无效。每次调用时都必须(重新)初始化这些变量。
请注意,您使用INADDR_ANY值初始化recvAddr,第一次调用后将使用服务器IP地址覆盖该值。如果recvfrom将使用此地址作为其仅应从此IP地址接收的指示,则它将解释您看到的行为。但是,标准的recvfrom实现不使用此地址值。