Reputation: 1302
I'm doing some socket programming with a UDP connection. I have my server written (which also takes an offset for a NTP server), but when I unit test it, it fails at the recvfrom function. I'm new to winsock programming, and network programming in general, so I'm not sure exactly where to go from here. Here is my code:
NtpServer::NtpServer(u_short portnum, const std::chrono::nanoseconds desiredOffset) : portnum(0), client_length(0), bytes_received(0), current_time(0), desiredOffset(0)
{
WORD wVersionRequested;
wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (WSAStartup(wVersionRequested, &wsaData) != 0)
{
std::cerr << "Could not open Windows connection" << std::endl;
exit(0);
}
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd == INVALID_SOCKET)
{
std::cerr << "Could not create socket." << std::endl;
WSACleanup();
exit(0);
}
//blocking enabled
u_long iMode = 0;
iResult = ioctlsocket(sd, FIONBIO, &iMode);
if (iResult != NO_ERROR)
std::cerr << "ioctlsocket failed with error: " << iResult << std::endl;
memset((void *)&server, '\0', sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(portnum);
if (bind(sd, reinterpret_cast<SOCKADDR *>(&server),
sizeof(server)) == -1)
{
std::cerr << "Could not bind name to socket" << std::endl;
closesocket(sd);
WSACleanup();
exit(0);
}
getResult(desiredOffset);
}
and the getResult function:
void NtpServer::getResult(const std::chrono::nanoseconds desiredOffset)
{
ntp_data ntpData = ntp_data();
//struct for timeout
struct timeval tv;
tv.tv_sec = 10; // 10 Secs Timeout
setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
while (1)
{
client_length = (int)sizeof(struct sockaddr_in);
/* Receive bytes from client */
bytes_received = recvfrom(sd, sendBuffer, NTP_PACKET_MAX, 0, (struct sockaddr *)&client, &client_length);
if (bytes_received < NTP_PACKET_MIN)
{
std::cerr << "Could not receive datagram." << std::endl;
closesocket(sd);
WSACleanup();
exit(0);
}
/* Check for time request */
if (strcmp(readBuffer, "GET TIME\r\n") == 0)
{
/* Get current time */
system_clock::time_point now = std::chrono::system_clock::now();
auto timepointoffset = (now + desiredOffset).time_since_epoch();
double current_value = std::chrono::duration_cast<std::chrono::duration<double>>(timepointoffset).count();
unpack_ntp(&ntpData, (unsigned char*)readBuffer, bytes_received);
make_packet(&ntpData, NTP_CLIENT, current_value);
pack_ntp((unsigned char *)sendBuffer, NTP_PACKET_MIN, &ntpData);
/* Send data back */
if (sendto(sd, sendBuffer,
(int)sizeof(sendBuffer), 0,
(struct sockaddr *)&client, client_length) !=
(int)sizeof(current_time))
{
std::cerr << "Error sending datagram." << std::endl;
closesocket(sd);
WSACleanup();
exit(0);
}
}
}
closesocket(sd);
WSACleanup();
}
Upvotes: 0
Views: 331
Reputation: 4943
You must test for errors from recvfrom()
-- you're setting SO_RCVTIMEO, so you can expect the receives to timeout.
Do you really want to enable read timeouts? If so, when you set SO_RCVTIMEO, set tv.tv_usec = 0
. You're leaving it uninitialized. If tv_usec happens to be an invalid value (less than 0 or greater than 999,999) then the setsockopt()
call should fail.
Upvotes: 1