简体   繁体   English

带有阻塞功能的UDP winsock服务器c ++

[英]UDP winsock server c++ with blocking

I am trying to program a udp client and server that will return the offset between the ntp time and boxtime. 我正在尝试编写一个udp客户端和服务器,它将返回ntp时间和boxtime之间的偏移量。 I cannot get my server to correctly receive data. 我无法让我的服务器正确接收数据。 I am testing it with Microsoft Unit tests, and when I try and test the server and client the test actually fails. 我正在使用Microsoft Unit测试进行测试,当我尝试测试服务器和客户端时,测试实际上失败了。 If I run the test I just get the error message: 如果我运行测试,我只是收到错误消息:

"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 " “活动的测试运行已中止,因为执行过程意外退出。要进一步调查,请在计算机级别或进程vstest.executionengine.x86.exe中启用本地故障转储。请转到更多详细信息: http//go.microsoft .com / fwlink /?linkid = 232477

If I debug I find that recvfrom function in the server returns 0, so it just exits. 如果我调试我发现服务器中的recvfrom函数返回0,所以它只是退出。

Here is my code for the server: 这是我的服务器代码:

#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();

}



}

Edit: I changed the way I did the timeout with a select statement, and recvfrom "if" statements. 编辑:我改变了使用select语句执行超时的方式,并从“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);
}

Should be: 应该:

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;
}

This aborts the receive loop if a call to recvfrom() fails, but simply ignores invalid packets (those less than the minimum length). 如果对recvfrom()的调用失败,则会中止接收循环,但只是忽略无效数据包(小于最小长度的数据包)。

Another issue: 另一个问题:

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);
}

You're sending the entire sendBuffer ; 你正在发送整个sendBuffer ; you should probably send only the size of the NTP packet. 你应该只发送NTP数据包的大小。 (Hopefully pack_ntp returns the packet size and you can use that). (希望pack_ntp返回数据包大小,你可以使用它)。 Also, you're comparing the sent size with sizeof(current_time) which makes zero sense. 此外,您将发送的大小与sizeof(current_time)进行比较,这是毫无意义的。 You should compare against the size of the buffer sent. 您应该与发送的缓冲区的大小进行比较。

There are other minor issues, but these are the big ones that jump out. 还有其他一些小问题,但这些都是跳出来的大问题。

You have this line of code: 你有这行代码:

setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));

If the 10 second timeout elapses because no data was received, recvfrom() will return -1 and WSAGetLastError() will return 10060. Your code is exiting in that situation: 如果因为没有收到数据而超过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
}

Even if select() times out, you are exiting as well: 即使select()超时,您也会退出:

n = select(sd, &fds, NULL, NULL, &tv);
if (n == 0)
{
    exit(0); // <-- here
}

Make sure there is another application actually sending data to your UDP app. 确保有另一个应用程序实际向UDP应用程序发送数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM