草庐IT

win系统C++的udp通信(接收并发送)详细教程、win下inet_pton和inet_ntop无法使用解决方法

我宿孤栈 2023-04-11 原文

对UDP编程0基础的可以参考这篇记录博文。

我做的是同一个程序中接收指定IP地址和端口号的信息作为输入,通过程序的算法进行处理,处理后的信息再通过另一个指定IP地址和端口号进行发送。也就是需要做两个udp一个接收数据,另一个发送数据。

网上的教程都是两个或多个.CPP文件的代码,对于UDP零基础的不太友好,代码结构包括运行步骤也不清晰。

一、源码编写

源码的话网上有很多资源,大多大同小异。

修改后源码:

//
// Created by JinxBIGBIG on 2022/8/3.
//
#include <sstream>
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include "positionInfo.h"
#include "OdrManager.hh"
#include "ShareMessage.h"
//链接静态库
#pragma comment (lib,"ws2_32.lib")

using namespace std;
using namespace OpenDrive;

//传参分别为:待发送信息;发送端口号;发送ip地址
int sendTo(char *sendBuf, short sendHostShort, const char *sendIP)
{
    WSADATA wdata;
    WORD wVersion;
    wVersion = MAKEWORD(2, 2);
    WSAStartup(wVersion, &wdata);
    if (HIBYTE(wdata.wVersion) != 2 || LOBYTE(wdata.wVersion) != 2)
    {
        return -1;
    }
    sockaddr_in sClient;
    sClient.sin_family = AF_INET;
    sClient.sin_port = htons(sendHostShort);

    //此处或出现bug,解决办法详见下文
    //inet_pton(AF_INET, "127.0.0.1", &sClient.sin_addr);
    sClient.sin_addr.S_un.S_addr = inet_addr(sendIP);

    SOCKET psock = socket(AF_INET, SOCK_DGRAM, 0);
    int len = sizeof(sClient);
    //char sendBuf[128];
    while (1)
    {

        //memset(sendBuf, 0, sizeof(sendBuf));
        //cout << "pelase input word:";
        //cin.getline(sendBuf, 64);
        sendto(psock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&sClient, len);
        //cout << "Send over;" << endl;
    }
    return 0;
}

int main()
{
   //以下为对接收数据进行处理的相关算法
    OpenDrive::OdrManager manager;
    Point point;
    PointLaneInfo pointLaneInfo;
    string xodrPath = "..\\map2.xodr";
    //string xodrPath = "..\\data\\map.xodr";
    bool xodrLoad = manager.loadFile(xodrPath);
    OpenDrive::Position* pos = manager.createPosition();
    manager.activatePosition(pos);
    
    //接收IP和发送IP、接收端口号和发送端口号设置
    char const *receiveIP = "127.0.0.1";
    char const *sendIP = "127.0.0.1";
    short receiveHostShort;
    short sendHostShort;
    cout << "Please input receiveHostPort(like:9999) and sendHostPort(like:9999)" << endl;
    cin >> receiveHostShort >> sendHostShort;
  
    //接收UDP编写
    WSADATA  wsData;
    int nret = WSAStartup(MAKEWORD(2, 2), &wsData);
    if(nret!=0)
        return nret;
    sockaddr_in sa,recSa;
    int len = sizeof(sa);
    sa.sin_addr.S_un.S_addr = inet_addr(receiveIP);
    sa.sin_family = AF_INET;
    sa.sin_port = htons(receiveHostShort);
    SOCKET  sock = socket(AF_INET, SOCK_DGRAM, 0);

    if (sock==INVALID_SOCKET)
        return WSAGetLastError();
    bind(sock, (sockaddr*)&sa, len);
    while (true)
    {
        char buf[1024];
        memset(buf, 0, 1024);
        int nlen = recvfrom(sock, buf, 1024, 0, (sockaddr*)&recSa, &len);
        if (nlen>0)
        {
            //char sIP[20];

            //此处或出现BUG,详见下文
            //inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
            inet_ntoa(recSa.sin_addr);
            cout << "The information :" << buf << endl;
            istringstream str(buf);
            double out;
            int i = 0;
            //接收的信息设置是一个三维坐标,类型为double,(x, y , z)
            double p[3];
            //将接收的char类型的数据以空格为分隔符分别读取xyz值
            while (str >> out) {
                //cout << out << endl;
                p[i] = out;
                i++;
            }
            point = {p[0], p[1], p[2]};
            //对接收的数据进行处理
            manager.setInertialPos(point.x, point.y, point.z);
            bool result = manager.inertial2lane();
            cout << "Position initialing result :" << result << endl;
            //处理后的输出信息为roadID
            int roadID = manager.getRoadHeader()->mId;
            //转换为char *类型后进行发送
            string str1 = "";
            str1 += to_string(roadID);
            char* sendBuf = const_cast<char *>(str1.data());
            cout << "sendBuf: "<< *sendBuf << endl;
            //此处对发送方法进行了封装直接调用sendTo即可,sendTo见main函数上方
            sendTo(sendBuf, sendHostShort, sendIP);
        }
    }
    /*int count = 1;
    string str = "";
    str += to_string(count);
    char* roadID = const_cast<char *>(str.data());
    sendTo(roadID, sendHostShort, sendIP);*/
}

详解

2.1、代码运行可能BUG

对于上述代码有两个函数的调用可能会出现BUG:

receive:

  //inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
  inet_ntoa(recSa.sin_addr);

这两个函数实现的都是将网络字节序列IP地址转化为字符串IP地址。都依赖于头文件#include <WS2tcpip.h>
并且需要添加链接库#pragma comment (lib,"ws2_32.lib")

可能BUG:添加头文件和链接库调用仍然出错的情况,可在cmakelist.txt中添加target_link_libraries(get_udp ws2_32link_libraries(ws2_32)指定链接库。

inet_ntop()在win下运行可能会无法调用,linux下可正常调用。
解决办法有3个:
其一:按上述代码所示通过函数inet_ntoa()替代,但其传参和返回值有所区别。
都无需返回值;
inet_ntop()传参分别为:第一个参数可以是AF_INETAF_INET6:第二个参数是一个指向网络字节序的二进制值的指针;第三个参数是一个指向转换后的点分十进制串的指针;第四个参数是目标的大小,以免函数溢出其调用者的缓冲区。
inet_ntoa()则只需要指向网络字节序的二进制值的指针。

其二:通过WSAAddressToStringA()方法实现并封装为inet_ntop()方法:

 
PCSTR WSAAPI inet_ntop(INT Family,const VOID *pAddr,PSTR pStringBuf, size_t  StringBufSize)
{
    if(pStringBuf ==NULL || StringBufSize == 0)
    {
       WSASetLastError(ERROR_INVALID_PARAMETER);
       return NULL;
    }
    if(Family == AF_INET6)
    {
        int ret=0;
        ret=WSAAddressToStringA((PSOCKADDR)pAddr,sizeof(PSOCKADDR),NULL,pStringBuf,(LPDWORD)&StringBufSize);
        if(ret!=0)
        {
            return NULL;
        }
    }
    else if(Family == AF_INET)
    {
        struct in_addr a;
        memcpy(&a,pAddr,sizeof(struct in_addr));
        pStringBuf = inet_ntoa(a);
    }
    else
    {
        WSASetLastError(WSAEAFNOSUPPORT);
        return NULL;
    }
    return pStringBuf;
}

封装后再调用inet_ntop()即可。

其三:直接使用WSAAddressToStringA(),代码见下方send中其三

send:

   //inet_pton(AF_INET, sendIP, &sClient.sin_addr);
   sClient.sin_addr.S_un.S_addr = inet_addr(sendIP);

这两个函数实现的都是将字符串IP地址转化为网络字节序列IP地址。都依赖于头文件#include <WS2tcpip.h>
并且需要添加链接库#pragma comment (lib,"ws2_32.lib")。但inet_pton()其在win下运行可能会无法调用,linux下可正常调用。

解决办法有两个:
其一:按上述代码所示通过函数inet_addr()替代,但其传参和返回值有所区别。inet_pton()无需返回值,转化后的IP地址直接存储在sClient.sin_addr中;inet_addr()需要返回sClient.sin_addr的子变量sClient.sin_addr.S_un.S_addr。传参就比较好理解,前者第一个参数为固定值,其余为字符串形式的发送IP地址和转化后IP地址;后者只需要传输字符串形式的发送IP地址。

其二:同理改写封装WSAStringToAddress

其三

/*
 ipv6 address to string or string to ipv6 address;
Edited by Mr Zhu,email:40222865@qq.com or weixin:40222865
*/
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
using namespace std;
int main()
{
   WSADATA wsa_data;
       WORD sockversion = MAKEWORD(2,2);
       if(WSAStartup(sockversion, &wsa_data) != 0)
       {
           return 0;
       }
    struct sockaddr_in6 ser_addr;
    int addr_size=sizeof(struct sockaddr_in6);
    char ip_addr[100]="";
    DWORD string_leng=100;
    int i;

        WSAStringToAddress( (LPSTR)"ff::1:ff:1",    
                             AF_INET6,
                             NULL,
                            (LPSOCKADDR) &ser_addr,
                            &addr_size );

printf("16进制ip地址是:");
for(i=0;i<15;i=i+2)
{
    printf("%x%x:",ser_addr.sin6_addr.u.Byte[i],ser_addr.sin6_addr.u.Byte[i+1]);
}

ser_addr.sin6_port=htons(5240);
    WSAAddressToStringA(
        (LPSOCKADDR)&ser_addr,        // sockaddr类型指针
                    addr_size,        //地址长度
                    NULL,             //地址协议指针
        (LPSTR)     ip_addr,          //转换后字符串地址
                &string_leng          //函数返回的字符串长度
    );

    printf("\nipv6 address is\"%s\"\n",ip_addr);

memset(ip_addr,0,100);
ser_addr.sin6_port=htons(0);
    WSAAddressToStringA(
        (LPSOCKADDR)&ser_addr,        // sockaddr类型指针
                    addr_size,        //地址长度
                    NULL,             //地址协议指针
        (LPSTR)     ip_addr,          //转换后字符串地址
                &string_leng          //函数返回的字符串长度
    );
    printf("\n端口为0后显示 address is\"%s\"\n",ip_addr);
    return 1;
}

2.2、输入输出设置相关

开篇说过两个udp只需要两对IP地址和端口号即可,那么只需要在代码:

    char const *receiveIP = "127.0.0.1";
    char const *sendIP = "127.0.0.1";
    short receiveHostShort;
    short sendHostShort;

此处进行修改。上述代码接收和发送IP地址均为本机(测试用),而输入和输出端口由控制台输入,若需要指定在此处直接赋值即可。

2.3、 接收信息后的处理和发送相关

接收后的信息处理和发送均在while循环中。
receive

while (true)
    {
        char buf[1024];
        memset(buf, 0, 1024);
        int nlen = recvfrom(sock, buf, 1024, 0, (sockaddr*)&recSa, &len);
        if (nlen>0)
        {
            //char sIP[20];
            //inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
            inet_ntoa(recSa.sin_addr);
            cout << "The information :" << buf << endl;
            istringstream str(buf);
            double out;
            int i = 0;
            double p[3];
            while (str >> out) {
                //cout << out << endl;
                p[i] = out;
                i++;
            }
            point = {p[0], p[1], p[2]};
            manager.setInertialPos(point.x, point.y, point.z);
            bool result = manager.inertial2lane();
            cout << "Position initialing result :" << result << endl;
            int roadID = manager.getRoadHeader()->mId;
            //转化为char *类型
            string str1 = "";
            str1 += to_string(roadID);
            char* sendBuf = const_cast<char *>(str1.data());
            cout << "sendBuf: "<< *sendBuf << endl;
            sendTo(sendBuf, sendHostShort, sendIP);
        }
    }

上述代码中变量buf即为接收的信息,此处博主设置的接收信息的格式为三个double类型的数据,形如1 1 1,按空格划分;接收后为char类型的buf,首先将其读取为Point类型数据point,Point结构定义如下:

struct Point{
    double x{0.};
    double y{0.};
    double z{0.};
    Point() = default;
    Point(double _x, double _y, double _z)
            : x(_x)
            , y(_y)
            , z(_z)
    {}
};

再将其作为算法输入进行处理,最后输出为一个int型的数据roadID;发送之前需要将其转换char *类型;最后再调用封装好的sendTo()方法。
send

这块代码改动不大,写好入参皆可。

三、代码运行

上述代码是本程序里边的源码,完成接收、处理和发送三个操作。直接将程序编译好运行即可。但是测试的时候还需要编写一个发送端的代码,负责发送本程序待接收的信息;另外,如果要测试是否能够正常接收,还可以再单独写一个接收端的代码。

3.1、发送端代码

send.cpp:

//
// Created by JinxBIGBIG on 2022/8/3.
//

#include <iostream>
#include <WS2tcpip.h>

using namespace std;

#pragma comment(lib,"ws2_32.lib")

int main()
{

    WSADATA wdata;
    
    WORD wVersion;

    wVersion = MAKEWORD(2, 2);

    WSAStartup(wVersion, &wdata);

    if (HIBYTE(wdata.wVersion) != 2 || LOBYTE(wdata.wVersion) != 2)
    {
        return -1;
    }

    sockaddr_in sClient;

    sClient.sin_family = AF_INET;
    sClient.sin_port = htons(9999);

    //inet_pton(AF_INET, "127.0.0.1", &sClient.sin_addr);
    sClient.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    SOCKET psock = socket(AF_INET, SOCK_DGRAM, 0);

    int len = sizeof(sClient);

    char sendBuf[128];
    while (1)
    {

        memset(sendBuf, 0, sizeof(sendBuf));

        cout << "Please input the point(x, y, z):";

        cin.getline(sendBuf, 64);
        sendto(psock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&sClient, len);

    }
    return 0;
}

将此代码单独编译运行生成get_send.exe文件后,点击运行:

此处附上接收端代码:

receive.cpp:

//
// Created by JinxBIGBIG on 2022/8/3.
//

#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment (lib,"ws2_32.lib")
using namespace  std;

int main()
{
    WSADATA  wsData;

    int nret=WSAStartup(MAKEWORD(2, 2), &wsData);
    if(nret!=0)
    {
        return nret;
    }

    sockaddr_in sa,recSa;

    int len = sizeof(sa);

    sa.sin_addr.S_un.S_addr = INADDR_ANY;

    sa.sin_family = AF_INET;
    sa.sin_port = htons(9999);

    SOCKET  sock = socket(AF_INET, SOCK_DGRAM, 0);

    if (sock==INVALID_SOCKET)
    {
        return WSAGetLastError();
    }

    bind(sock, (sockaddr*)&sa, len);

    while (true)
    {
        char buf[1024];

        memset(buf, 0, 1024);

        int nlen = recvfrom(sock, buf, 1024, 0, (sockaddr*)&recSa, &len);

        if (nlen>0)
        {
            //char sIP[20];
            //inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
            inet_ntoa(recSa.sin_addr);
            cout << buf << endl;
        }
    }
}

同理,将此代码单独编译运行生成get_receive.exe文件后,如有需要点击运行。

3.2、调试助手

上述1解决测试时的发送端问题,但本程序在接收发送端发送的数据并处理后还需要进行发送,那么如何查看发送的消息是否有正确接收呢?
可以下载网络调试助手

点集立即下载:

我选的是箭头所指这个就自动跳到迅雷下载了。下载后是一个单独的.exe文件,

双击即可运行:

协议选择udp协议,ip地址下拉选项可设置为本机地址,主机端口也可设置,此处博主设置的8080。即本程序中的处理后数据的发送目标是char const *sendIP = "127.0.0.1; sendHostShort = 8080;,而负责接收数据的目标地址是char const *receiveIP = "127.0.0.1; receiveHostShort = 9999;也就是本机。

3.3、运行测试

3.3.1 运行本程序:可在编辑器里边运行也可以通过.exe文件运行,

本程序IP地址都直接给的本机,端口号则是手动输入9999 8080

    char const *receiveIP = "127.0.0.1";
    char const *sendIP = "127.0.0.1";
    short receiveHostShort;
    short sendHostShort;
    cout << "Please input receiveHostPort(like:9999) and sendHostPort(like:9999)" << endl;
    cin >> receiveHostShort >> sendHostShort;

3.3.2 运行发送端程序get_send.exe给本程序发送坐标((5915.00, -2937.76, 0)):输入坐标后回车:

可在本程序的终端界面看到:

图中的:

The information :5915.00 -2937.76 0
Position initialing result :1
sendBuf: 4

第1行为接收的坐标信息,2、3行则是对接收的数据处理后的自定义显示结果。
3.3.3 通过调试助手接收本程序发送的数据:打开网络调试助手


点击打开:

即可看到已成功接收传递的数据40,3.2中的sendBuf: 4表示的是传递的char类型数据第一个元素的信息,也就是40的4。
3.3.4 另外可在图中箭头所示设置接收数据显示的类型,HEX为16进制。

如果传输的数据是1,则其HEX为:
也就是31,数字“1”被当做字符存储时,用的ASCII码,值是49(10进制),转化为16进制就是31(316^1 + 116 ^ 0)。

如果我们要想接收端接收的是16进制的字符,那么我们就需要在发送之前进行字符串转16进制操作。

至此结束。

四、补充

如果是单独运行发送端和接收端的代码,则直接将三中的发送端代码send.cpp和接收端代码receive.cpp分别运行后执行生成的两个.exe文件再分别运行c测试即可。

有关win系统C++的udp通信(接收并发送)详细教程、win下inet_pton和inet_ntop无法使用解决方法的更多相关文章

  1. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  2. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  3. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  6. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  7. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  8. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  9. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  10. ruby - 如何更快地解决 project euler #21? - 2

    原始问题Letd(n)bedefinedasthesumofproperdivisorsofn(numberslessthannwhichdivideevenlyinton).Ifd(a)=bandd(b)=a,whereab,thenaandbareanamicablepairandeachofaandbarecalledamicablenumbers.Forexample,theproperdivisorsof220are1,2,4,5,10,11,20,22,44,55and110;therefored(220)=284.Theproperdivisorsof284are1,2,

随机推荐