c++ - UDP winsock 服务器 c++ 与阻塞

标签 c++ network-programming udp server winsock

我正在尝试编写一个 udp 客户端和服务器,它将返回 ntp 时间和 boxtime 之间的偏移量。我无法让我的服务器正确接收数据。我正在使用 Microsoft 单元测试对其进行测试,当我尝试测试服务器和客户端时,测试实际上失败了。如果我运行测试,我只会收到错误消息:

"The active Test Run was aborted because the execution process exited unexpectedly. To investigate further, enable local crash dumps either at the machine level or for process vstest.executionengine.x86.exe. Go to more details: 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, '\0', 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 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();

}



}

编辑:我使用 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) 进行比较,这没有意义。您应该与发送的缓冲区大小进行比较。

还有其他一些小问题,但这些是跳出来的大问题。

关于c++ - UDP winsock 服务器 c++ 与阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34021838/

相关文章:

c++ - 为什么这个程序没有收到预期的 UDP 数据包?

sockets - 是否可以通过 UDP 发送非常大的数据?

客户端有关recvfrom函数的问题

c++ - 为什么在文件 I/O 中读取数据 block 比逐字节读取更快

c# - UnityWebRequest 和/或 HttpWebRequest 在 Android 上使用 PUT 给出 403

MATLAB 无法在两个 MATLAB session 之间创建连接

c - 基于网络事件的编程真的更好吗……?

c++ - 错误 LNK2001 : unresolved external symbol "__declspec(dllimport) public: class QString & __thiscall QString::operator=(class QString &&)"

php - 绕过我的需要明文大小的 XOR 加密的方法

c++ - 使用反向循环反转字符串时空字符会发生什么