UDP传输C++实现

/*加载库文件*/
#pragma comment( lib, "ws2_32.lib" )
/*加载头文件*/
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

/* 【自学去】网站收集 http://www.zixue7.com */

/*定义多播常量*/
#define MCASTADDR     "224.3.5.8"
#define MCASTPORT     25000
#define BUFSIZE       1024
#define MCOUNT        10

/*定义广播常量*/
#define BCASTPORT     5050
#define BCOUNT        10

/*定义广播全局变量*/
SOCKET             socketBro;
SOCKET             socketRec;
struct sockaddr_in addrBro;
struct sockaddr_in addrRec;
BOOL               broadSendFlag;
BOOL               broadFlag;

DWORD              bCount;
DWORD              bcastAddr;
short              bPort;

/*定义多播全局变量*/
SOCKET             socketMul;
SOCKET             sockJoin;
struct sockaddr_in addrLocal;
struct sockaddr_in addrMul;

BOOL               multiSendFlag;
BOOL               bLoopBack;    
BOOL               multiFlag;

DWORD              dwInterface;  
DWORD              dwMulticastGroup;
DWORD              mCount;         
short              mPort;           

/*自定义函数*/
void initial();
void GetArgments(int argc, char **argv);

void userHelpAll();
void userHelpBro();
void userHelpMul();

void broadcastSend();
void broadcastRec();

void mulControl();
void multicastSend();
void multicastRec();

/*初始化全局变量函数*/
void initial()
{
    /*初始化广播全局变量*/
    bPort = BCASTPORT;
    bCount = BCOUNT;
    bcastAddr = INADDR_BROADCAST;
    broadSendFlag = FALSE;
    broadFlag = FALSE;
    multiFlag = FALSE;

    /*初始化多播全局变量*/
    dwInterface = INADDR_ANY;
    dwMulticastGroup = inet_addr(MCASTADDR);
    mPort = MCASTPORT;
    mCount = MCOUNT;
    multiSendFlag = FALSE;
    bLoopBack = FALSE;
}

/*参数获取函数*/
void GetArgments(int argc, char **argv)
{
    int i;
    /*如果参数个数小于2个*/
    if(argc<=1)
    {
        userHelpAll();
        return ;
    }
    /*获取广播选项*/
    if(argv[1][0]=='-'&&argv[1][1]=='b')
    {
        /*广播标志设置为真*/
        broadFlag = TRUE;
        for(i=2; i < argc ;i++)
        {
            if (argv[i][0] == '-')
            {
                switch (tolower(argv[i][1]))
                {
                    /*如果是发送者*/
                    case 's': 
                        broadSendFlag = TRUE;
                        break;
                    /*广播的地址*/
                    case 'h':
                        if (strlen(argv[i]) > 3)
                            bcastAddr = inet_addr(&argv[i][3]);
                        break;
                    /*广播的端口号*/
                    case 'p':
                        if (strlen(argv[i]) > 3)
                            bPort = atoi(&argv[i][3]);
                        break;
                    /*广播(接收或者发送)的数量*/
                    case 'n': 
                        bCount = atoi(&argv[i][3]);
                        break;
                    /*其他情况显示用户帮助,终止程序*/
                    default:
                        {
                            userHelpBro();
                            ExitProcess(-1);
                        }
                        break;
                }
            }
        }
        return ;
    }

    /*获取多播选项*/
    if(argv[1][0]=='-'&&argv[1][1]=='m')
    {
        /*多播标志设置为真*/
        multiFlag = TRUE;
        for(i=2; i < argc ;i++)
        {
            if (argv[i][0] == '-')
            {
                switch (tolower(argv[i][1]))
                {
                    /*如果是发送者*/
                    case 's': 
                        multiSendFlag = TRUE;
                        break;
                    /*多播地址*/
                    case 'h': 
                        if (strlen(argv[i]) > 3)
                            dwMulticastGroup = inet_addr(&argv[i][3]);
                        break;
                    /*本地接口地址*/
                    case 'i': 
                        if (strlen(argv[i]) > 3)
                            dwInterface = inet_addr(&argv[i][3]);
                        break;
                    /*多播端口号*/
                    case 'p': 
                        if (strlen(argv[i]) > 3)
                            mPort = atoi(&argv[i][3]);
                        break;
                    /*环回标志设置为真*/
                    case 'l': 
                        bLoopBack = TRUE;
                        break;
                    /*发送(接收)的数量*/
                    case 'n':
                        mCount = atoi(&argv[i][3]);
                        break;
                    /*其他情况,显示用户帮助,终止程序*/
                    default:
                        userHelpMul();
                        break;
                }
            }
        }
    
    }
    return;
}

/*全局用户帮助函数*/
void userHelpAll()
{
    printf("Please choose broadcast[-b] or multicast[-m] !\n"); 
    printf("userHelpAll: -b [-s][p][-h][-n] | -m[-s][-h][-p][-i][-l][-n]\n");
    userHelpBro();
    userHelpMul();
}

/*广播用户帮助函数*/
void userHelpBro()
{
    printf("Broadcast: -b -s:str -p:int -h:str -n:int\n");
    printf("           -b     Start the broadcast program.\n");
    printf("           -s     Act as server (send data); otherwise\n");
    printf("                  receive data. Default is receiver.\n");
    printf("           -p:int Port number to use\n ");
    printf("                  The default port is 5050.\n");
    printf("           -h:str The decimal broadcast IP address.\n");
    printf("           -n:int The Number of messages to send/receive.\n");
    printf("                  The default number is 10.\n");
}

/*多播用户帮助函数*/
void userHelpMul()
{
    printf("Multicast: -m -s -h:str -p:int -i:str -l -n:int\n");
    printf("           -m     Start the multicast program.\n");
    printf("           -s      Act as server (send data); otherwise\n");
    printf("                   receive data. Default is receiver.\n");
    printf("           -h:str  The decimal multicast IP address to join\n");
    printf("                   The default group is: %s\n", MCASTADDR);
    printf("           -p:int  Port number to use\n");
    printf("                   The default port is: %d\n", MCASTPORT);
    printf("           -i:str  Local interface to bind to; by default \n");
    printf("                   use INADDRY_ANY\n");
    printf("           -l      Disable loopback\n");
    printf("           -n:int  Number of messages to send/receive\n");
    ExitProcess(-1);
}

/*广播消息发送函数*/
void broadcastSend()
{
    /*设置广播的消息*/
    char *smsg="The message received is from sender!";
    BOOL opt=TRUE;
    int nlen=sizeof(addrBro);
    int ret;
    DWORD i=0;
    
    /*创建UDP套接字*/
    socketBro=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
    /*如果创建失败*/
    if(socketBro==INVALID_SOCKET)
    {
        printf("Create socket failed:%d\n",WSAGetLastError());
        WSACleanup();
        return;
    }
    
    /*设置广播地址各个选项*/
    addrBro.sin_family=AF_INET;
    addrBro.sin_addr.s_addr=bcastAddr;
    addrBro.sin_port=htons(bPort);
    
    /*设置该套接字为广播类型*/
    if (setsockopt(socketBro,SOL_SOCKET,SO_BROADCAST,(char FAR *)&opt,
                                                sizeof(opt))==SOCKET_ERROR)
    /*如果设置失败*/
    {
        printf("setsockopt failed:%d",WSAGetLastError());
        closesocket(socketBro);
        WSACleanup();
        return;
    }
    /*循环发送消息*/
    while(i<bCount)
    {
        /*延迟1秒*/
        Sleep(1000);
        /*从广播地址发送消息*/
        ret=sendto(socketBro,smsg,256,0,(struct sockaddr*)&addrBro,nlen);
        /*如果发送失败*/
        if(ret==SOCKET_ERROR)
            printf("Send failed:%d",WSAGetLastError());
        /*如果发送成功*/
        else
        {        
            printf("Send message %d!\n",i);    
        }
        i++;
    }
    /*发送完毕后关闭套接字、释放占用资源*/
    closesocket(socketBro);
    WSACleanup();
}

/*广播消息接收函数*/
void broadcastRec()
{   
    BOOL optval = TRUE;
    int addrBroLen;
    char buf[256];
    DWORD i=0;
    /*该地址用来绑定套接字*/
    addrRec.sin_family=AF_INET;
    addrRec.sin_addr.s_addr=0;
    addrRec.sin_port=htons(bPort);

    /*该地址用来接收网路上广播的消息*/
    addrBro.sin_family=AF_INET;
    addrBro.sin_addr.s_addr=bcastAddr;
    addrBro.sin_port=htons(bPort);
    
    addrBroLen=sizeof(addrBro);
    //创建UDP套接字
    socketRec=socket(AF_INET,SOCK_DGRAM,0);
    /*如果创建失败*/
    if(socketRec==INVALID_SOCKET)
    {
        printf("Create socket error:%d",WSAGetLastError());
        WSACleanup();
        return;
    }

    /*设置该套接字为可重用类型*/
    if(setsockopt(socketRec,SOL_SOCKET,SO_REUSEADDR,(char FAR *)&optval,
                                                            sizeof(optval))==SOCKET_ERROR)
    /*如果设置失败*/
    {
        printf("setsockopt failed:%d",WSAGetLastError());
        closesocket(socketRec);
        WSACleanup();
        return;
    }
    /*绑定套接字和地址*/
    if(bind(socketRec,(struct sockaddr *)&addrRec,
                                sizeof(struct sockaddr_in))==SOCKET_ERROR)
    /*如果绑定失败*/
    {
        printf("bind failed with: %d\n", WSAGetLastError());
        closesocket(socketRec);
        WSACleanup();
        return ;
    }
    /*从广播地址接收消息*/
    while(i<bCount)
    {
        recvfrom(socketRec,buf,256,0,(struct sockaddr FAR *)&addrBro,(int FAR *)&addrBroLen);
        /*延迟2秒钟*/
        Sleep(2000);
        /*输出接收到缓冲区的消息*/
        printf("%s\n",buf);
        /*情况缓冲区*/
        ZeroMemory(buf,256);
        i++;
    }
    /*接收完毕后关闭套接字、释放占用资源*/
    closesocket(socketRec);
    WSACleanup();
}

/*多播控制函数*/
void mulControl()
{
    int optval; 
    /*创建UDP套接字,用于多播*/
    if ((socketMul = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
                  WSA_FLAG_MULTIPOINT_C_LEAF 
                  | WSA_FLAG_MULTIPOINT_D_LEAF 
                  | WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
    {
        printf("socket failed with: %d\n", WSAGetLastError());
        WSACleanup();
        return ;
    }
    
    /*设置本地接口地址*/
    addrLocal.sin_family = AF_INET;
    addrLocal.sin_port = htons(mPort);
    addrLocal.sin_addr.s_addr = dwInterface;
    
    /*将UDP套接字绑定到本地地址上*/
    if (bind(socketMul, (struct sockaddr *)&addrLocal, 
                                    sizeof(addrLocal)) == SOCKET_ERROR)
    /*如果绑定失败*/
    {
        printf("bind failed with: %d\n", WSAGetLastError());
        closesocket(socketMul);
        WSACleanup();
        return ;
    }

    /*设置多播地址各个选项*/
    addrMul.sin_family      = AF_INET;
    addrMul.sin_port        = htons(mPort);
    addrMul.sin_addr.s_addr = dwMulticastGroup;

    /*重新设置TTL值*/
    optval = 8;
    /*设置多播数据的TTL(存在时间)值。默认情况下,TTL值是1*/
    if (setsockopt(socketMul, IPPROTO_IP, IP_MULTICAST_TTL, 
        (char *)&optval, sizeof(int)) == SOCKET_ERROR)
    /*如果设置失败*/
    {
        printf("setsockopt(IP_MULTICAST_TTL) failed: %d\n",WSAGetLastError());
        closesocket(socketMul);
        WSACleanup();
        return ;
    }

    /*如果指定了返还选项*/
    if (bLoopBack)
    {
        /*设置返还选项为假,禁止将发送的数据返还给本地接口*/
        optval = 0;
        if (setsockopt(socketMul, IPPROTO_IP, IP_MULTICAST_LOOP,
            (char *)&optval, sizeof(optval)) == SOCKET_ERROR)
        /*如果设置失败*/
        {
            printf("setsockopt(IP_MULTICAST_LOOP) failed: %d\n",
                WSAGetLastError());
            closesocket(socketMul);
            WSACleanup();
            return ;
        }
    }
    
    /*加入多播组*/
    if ((sockJoin = WSAJoinLeaf(socketMul, (SOCKADDR *)&addrMul, 
                             sizeof(addrMul), NULL, NULL, NULL, NULL, 
                             JL_BOTH)) == INVALID_SOCKET)
    /*如果加入不成功*/
    {
        printf("WSAJoinLeaf() failed: %d\n", WSAGetLastError());
        closesocket(socketMul);
        WSACleanup();
        return ;
    }
}

/*多播消息发送函数*/
void multicastSend()
{
    
    TCHAR  sendbuf[BUFSIZE];
    DWORD i;
    int ret;

    mulControl();
    /*发送mCount条消息*/
    for(i = 0; i < mCount; i++)
    {
        /*将待发送的消息写入发送缓冲区*/
        sprintf(sendbuf, "server 1: This is a test: %d", i);
        ret=sendto(socketMul, (char *)sendbuf, strlen(sendbuf), 0,
                (struct sockaddr *)&addrMul, sizeof(addrMul));
        /*如果发送失败*/
        if(ret==SOCKET_ERROR)
        {
            printf("sendto failed with: %d\n",WSAGetLastError());
            closesocket(sockJoin);
            closesocket(socketMul);
            WSACleanup();
            return ;
        }
        /*如果发送成功*/
        else
            printf("Send message %d\n",i);
         Sleep(500);
     }
    /*关闭套接字、释放占用资源*/
    closesocket(socketMul);
    WSACleanup();
}

/*多播消息接收函数*/
void multicastRec()
{
    DWORD i;
    struct sockaddr_in  from;
    TCHAR recvbuf[BUFSIZE];
    int ret;
    int len = sizeof(struct sockaddr_in);

    mulControl();
    /*接收mCount条消息*/
    for(i = 0; i < mCount; i++)
    {
        /*将接收的消息写入接收缓冲区*/
        if ((ret = recvfrom(socketMul, recvbuf, BUFSIZE, 0,
                (struct sockaddr *)&from, &len)) == SOCKET_ERROR)
        /*如果接收不成功*/
        {
            printf("recvfrom failed with: %d\n",WSAGetLastError());
            closesocket(sockJoin);
            closesocket(socketMul);
            WSACleanup();
             return ;
        }
        /*接收成功,输出接收的消息*/
        recvbuf[ret] = 0;
        printf("RECV: '%s' from <%s>\n", recvbuf,inet_ntoa(from.sin_addr));
     }
    /*关闭套接字、释放占用资源*/
    closesocket(socketMul);
    WSACleanup();
}

/*主函数*/
int main(int argc, char **argv)
{
    WSADATA wsd;

    initial();
    GetArgments(argc, argv);

    /*初始化Winsock*/
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        printf("WSAStartup() failed\n");
        return -1;
    }

    /*如果是执行广播程序*/
    if(broadFlag)
    {
        /*以发送者身份发送消息*/
        if(broadSendFlag)
        {
            broadcastSend();
            return 0;
        }
        /*以接收者身份接收消息*/
        else
        {
            broadcastRec();
            return 0;
        }
    }

    /*如果是执行多播程序*/
    if(multiFlag)
    {
        /*以发送者身份发送消息*/
        if(multiSendFlag) 
        {
            multicastSend();
            return 0;
        }
        /*以接收者身份接收消息*/
        else    
        {
            multicastRec();
            return 0;
        }
    }
    return 0;
}


 

UDP是一种面向无连接的传输协议,它在传输数据时不提供可靠性,容易出现丢包、错包的情况。为了实现UDP的可靠传输,可以采用以下策略: 1. 检验和:发送方在发送数据前,对数据进行检验和计算,并将计算结果附加在数据中。接收方在接收到数据后,重新计算检验和,并将计算结果与附加的检验和进行比对,如果不一致则认为数据损坏,需要重新发送。 2. 序列号和确认:发送方在发送每个数据包时都给数据包分配一个唯一的序列号。接收方在接收到数据包后,发送一个确认消息给发送方,告知发送方已成功接收到序列号为n的数据包。发送方在收到确认消息后,认为序列号为n的数据包已经可靠传输,可以发送下一个数据包。 3. 超时重传:发送方在发送数据包后设定一个定时器,如果在规定时间内没有收到接收方的确认消息,发送方就会认为数据包丢失,需要重新发送。 4. 滑动窗口:发送方可以同时发送多个数据包,接收方可以接收多个数据包。发送方和接收方分别维护一个窗口,窗口大小表示可以同时传输的数据包的数量。发送方根据接收方发送的确认消息来调整发送窗口的大小,以提高传输效率。 通过以上策略的组合应用,可以在UDP协议下实现可靠传输。当然,这些策略也会增加网络传输的开销和延时,需要根据具体业务需求进行权衡和选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

!chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值