UDP winsock服务器c++带有阻塞
UDP winsock server c++ with blocking
我正在尝试编程一个udp客户端和服务器,它将返回ntp时间和boxtime之间的偏移量。我无法让我的服务器正确接收数据。我正在用Microsoft单元测试来测试它,当我尝试测试服务器和客户端时,测试实际上失败了。如果我运行测试,我只会收到错误消息:
"由于执行进程意外退出,活动的测试运行被中止。要进一步调查,请在计算机级别或为进程vstest.executionengine.x86.exe启用本地崩溃转储。转到更多详细信息:http://go.microsoft.com/fwlink/?linkid=232477"
如果我调试,我发现服务器中的recvfrom函数返回0,所以它就退出了。
这是我的服务器代码:
#pragma once
#include <iostream>
#include "NtpServer.h"
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <winsock.h>
#include <errno.h>
using std::chrono::system_clock;
namespace ntp
{
struct sockaddr_in server;
struct sockaddr_storage client;
//constructor to create ntp server
NtpServer::NtpServer(u_short portnum, const std::chrono::nanoseconds desiredOffset) : portnum(0), client_length(0), bytes_received(0), current_time(0), desiredOffset(0)
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
std::cerr << "Could not open Windows connection." << std::endl;
exit(0);
}
memset((void *)&server, ' ', sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(portnum);
server.sin_addr.s_addr = htonl(INADDR_ANY);
sd = WSASocket(AF_INET, SOCK_DGRAM, 17, NULL, 0, NULL);
if (sd == INVALID_SOCKET)
{
std::cerr << "Could not create socket." << std::endl;
WSACleanup();
exit(0);
}
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);
}
NtpServer::~NtpServer()
{
closesocket(sd);
WSACleanup();
}
void NtpServer::getResult(const std::chrono::nanoseconds desiredOffset)
{
ntp_data ntpData = ntp_data();
//set up timeout with blocking
fd_set fds;
int n;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(sd, &fds);
tv.tv_sec = 10; // 10 Secs Timeout
tv.tv_usec = 0;
n = select(sd, &fds, NULL, NULL, &tv);
if (n == 0)
{
exit(0);
}
while (1)
{
//client_length = sizeof(client);
int len = (int)sizeof(struct sockaddr_in);
/* Receive bytes from client */
bytes_received = recvfrom(sd, sendBuffer, NTP_PACKET_MAX, 0, (struct sockaddr *)&client, &len);
if (bytes_received == SOCKET_ERROR)
{
std::cerr << "Could not receive datagram." << std::endl;
closesocket(sd);
WSACleanup();
exit(0);
}
if (bytes_received < NTP_PACKET_MIN)
{
continue;
}
/* Check for time request */
if (strcmp(readBuffer, "GET TIMErn") == 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();
}
}
编辑:我用select语句和recvfrom"if"语句更改了超时的方式。
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);
}
应为:
bytes_received = recvfrom(sd, sendBuffer, NTP_PACKET_MAX, 0, (struct sockaddr *)&client, &client_length);
if (bytes_received == SOCKET_ERROR)
{
int err = WSAGetLastError();
// Handle WSAETIMEDOUT here if necessary
std::cerr << "Could not receive datagram, error: " << err << std::endl;
closesocket(sd);
WSACleanup();
exit(0);
}
if (bytes_received < NTP_PACKET_MIN)
{
// print/log a warning here
continue;
}
如果对recvfrom()
的调用失败,这将中止接收循环,但只是忽略无效数据包(小于最小长度的数据包)。
另一个问题:
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);
}
您正在发送整个sendBuffer
;您可能应该只发送NTP数据包的大小。(希望pack_ntp
返回数据包大小,您可以使用它)。此外,您正在将发送的大小与sizeof(current_time)
进行比较,这毫无意义。您应该与发送的缓冲区的大小进行比较。
还有其他一些小问题,但这些都是突出的大问题。
您有这行代码:
setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
如果由于未接收到数据而经过10秒超时,则recvfrom()
将返回-1,WSAGetLastError()
将返回10060。您的代码在这种情况下退出:
bytes_received = recvfrom(sd, sendBuffer, NTP_PACKET_MAX, 0, (struct sockaddr *)&client, &len);
if (bytes_received == SOCKET_ERROR)
{
std::cerr << "Could not receive datagram." << std::endl;
closesocket(sd);
WSACleanup();
exit(0); // <-- here
}
即使select()
超时,您也在退出:
n = select(sd, &fds, NULL, NULL, &tv);
if (n == 0)
{
exit(0); // <-- here
}
确保有另一个应用程序实际向您的UDP应用程序发送数据。
相关文章:
- (Winsock) UDP 接收工作正常,但同一套接字的发送失败
- WinSock Non-Blocking I/O
- 使用 winsock 接收 http 请求
- 如何查找 winsock.h 在 Visual Studio 中的包含位置
- 在Linux上交叉编译Windows应用程序时如何链接到Winsock?
- C++低吞吐量 winsock TCP 测试应用程序
- Winsock 数据不是从 IP 检索的,而是从普通 URL 检索的
- 错误:10035 Winsock发送
- 如何使用c++在winsock中从客户端向服务器发送大字符串
- 如何使用winsock在c++应用程序中实现安全套接字通信
- 如何为winsock的send()函数构造constchar*缓冲区
- C++WinSock选择本地接口进行连接
- 使用全局 IP 地址时,C++ winsock 2 应用程序中的代码是否必须更改?
- 使用 WinSock 处理多个用户
- Winsock本地客户服务器基准测试
- WinSock c++ inet_ntop始终显示 204.204.204.204(并且 accept() 没有失败)
- Winsock错误10022在听
- 致命错误:Winsock:没有这样的文件或目录
- 如何通过TCP/IP Winsock发送双型数据
- Winsock 400 错误请求,开放性