Windows下基于UDP的可靠传输协议实现

前言:在某互联网公司实习了好几个月,有一个月都是在做基于UDP协议的应用层软件开发,目的是要用在流媒体服务器上,传输高清视频图像帧。整个开发过程,从0到最后完成了几十 G 以上的大文件可靠传输,但效率方面还需要进一步提升。UDP网络传输协议部分编程,由于存在丢包问题,确实有点复杂,现在分享一下自己的开发经验。




#ifndef UDPNONBLOCKINGOUTPUT
#define UDPNONBLOCKINGOUTPUT
#include "winsock.h"   
#include <iostream>  
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <time.h>
#include <vector>
#pragma comment(lib,"wsock32.lib")   

#define SERVER_PORT 2000   
#define CLIENT_PORT 3000  
#define BEGIN_NUM 19900711
#define DATA_NUM  20160113
#define CHECK_NUM 20161817
#define END_CHECK_NUM 20160114
#define END_NUM   11700991
#define END_ALL   20160115
#define BLOCK_DATA_SIZE (10 * 1024)
#define FILE_HEAD  4
#define BLOCK_HEAD 4 
#define DESTADDRESS "192.168.27.170"
#define FILENAME "D:\\48M.mp4"
//UDT包头
typedef struct _UDP_HEAD_
{
	UINT     m_PacketType;			 //包类型 1数据包 2确认包
	UINT     m_PacketLen;			 //本次数据帧长度(不包括包头大小,仅仅数据部分大小)
	UINT     m_PacketId;             //包序列号
	UINT     m_Check;                //效验和 以上字段的相加
}UDP_HEAD;

class CUdpNonBlockingOutput
{
public:
	CUdpNonBlockingOutput()
	{
		m_flagNonBlocking = 0;
		InitSocket();
	}
	~CUdpNonBlockingOutput()
	{
		closesocket(m_socketClient);
		WSACleanup();
	}

//	static DWORD WINAPI ThreadFun(LPVOID pM);

	void InitSocket();

    void SendKeyPackage(char *eachBuf, UINT size);

    void RecvKeyPackage(char *eachBuf, UINT size);

	void SendFile();

	void SendMsg();

//	void RecvMsg(int size);

private:

	char m_RecvBuff[1024];
	char m_SendBuff[1024];

    sockaddr_in Client;
	sockaddr_in ClientAddr;
    SOCKET m_socketServer;
	SOCKET m_socketClient;

	int m_len;
	int m_flagNonBlocking;
	DWORD  dwFileSize;
	BYTE * fileData;

    HANDLE m_Mutex;

	static std::vector<UINT> m_lackPackageNum;
};
#endif

#include "UdpNonBlockingOutput.h"

void CUdpNonBlockingOutput::InitSocket()
{
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		printf("sock init failed!\n");
		return;
	}
    m_socketServer = socket(AF_INET, SOCK_DGRAM, 0);//创建socket
	m_socketClient = socket(AF_INET, SOCK_DGRAM, 0);//创建socket   
	if (m_socketClient == SOCKET_ERROR)
	{
		printf("sock create failed\n");
		WSACleanup();
		return;
	}

	ClientAddr.sin_family = AF_INET;
	ClientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	ClientAddr.sin_port = htons(CLIENT_PORT);
    if (bind(m_socketServer, (struct sockaddr*)&ClientAddr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)//绑定   
	{
		printf("sock bind error!\n");
		closesocket(m_socketClient);
		WSACleanup();
		return;
	}

	Client.sin_family = AF_INET;
    Client.sin_addr.s_addr = inet_addr(DESTADDRESS);
    Client.sin_port = htons(SERVER_PORT);
	m_len = sizeof(Client);
    return;
}

std::vector<UINT> CUdpNonBlockingOutput::m_lackPackageNum;

void CUdpNonBlockingOutput::SendKeyPackage(char *eachBuf,UINT size)
{
    char recvKeyWord[4] = { 0 }, keyWord[4] = { 0 }, charFileSize[4] = { 0 };
    memcpy(keyWord, eachBuf, FILE_HEAD); //拷贝前4个字节
    unsigned long ul = 1;//------------------设置非阻塞模式
    int ret;
    ret = ioctlsocket(m_socketServer, FIONBIO, (unsigned long *)&ul);
    if (ret == SOCKET_ERROR)
    {
        printf("unblock failed!\n");
    }
    sendto(m_socketClient, (char*)eachBuf, size, 0, (SOCKADDR*)&Client, m_len);

//    DWORD lTimeOut = 1;
//   setsockopt(m_socketServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&lTimeOut, sizeof(DWORD));//设置超时重发

    for (int i = 0, startClock, endClock; i < 3; i++)
    {
        startClock = clock();
        while (1)
        {
            if (recvfrom(m_socketServer, recvKeyWord, FILE_HEAD, 0, (struct sockaddr*)&ClientAddr, &m_len) > 0)
            {
                if (!strcmp(recvKeyWord, keyWord))
                {
                    return;
                }
            }
            endClock = clock();
            if ((endClock - startClock) > 5)
                break;
        }
        std::cout << "123";
        sendto(m_socketClient, (char*)eachBuf, size, 0, (SOCKADDR*)&Client, m_len);
    }
}

void CUdpNonBlockingOutput::RecvKeyPackage(char *eachBuf,UINT size)
{
    char recvKeyWord[4] = { 0 };
    unsigned long ul = 1;
    int ret;
    ret = ioctlsocket(m_socketServer, FIONBIO, (unsigned long *)&ul);//设置非阻塞模式
    if (ret == SOCKET_ERROR)
    {
        printf("unblock failed!\n");
    }
    int startClock = clock(), endClock;//接收关键包
    while (1)
    {
        endClock = clock();
        if ((endClock - startClock) > 5)
            break;
        if (recvfrom(m_socketServer, eachBuf, size, 0, (struct sockaddr*)&ClientAddr, &m_len) > 0)
        {
            memcpy(recvKeyWord, eachBuf, FILE_HEAD);
            break;
        }
    }
    startClock = clock();
    while (1)
    {
        if (sendto(m_socketClient, recvKeyWord, sizeof(recvKeyWord), 0, (struct sockaddr*)&Client, m_len) > 0)
            break;
        endClock = clock();
        if ((endClock - startClock) > 5)
            break;
    }
}

//void CUdpNonBlockingOutput::RecvMsg(int size)
//{
//    recvfrom(m_socketServer, m_RecvBuff, BLOCK_HEAD + 1, 0, (struct sockaddr*)&Client, &m_len);
//	int DataPos = 0;
//	for (int i = 0; i < 4; i++)
//	{
//		DataPos += ((UCHAR)m_RecvBuff[i]) << (8 * (4 - i - 1)); //获取数据包序列号
//	}
//	m_lackPackageNum.push_back(DataPos);
//	std::cout << DataPos << std::endl;
//}
//
定时检测线程
//DWORD WINAPI CUdpNonBlockingOutput::ThreadFun(LPVOID lpParameter)
//{
//	printf("开启线程ID号为%d的子线程\n", GetCurrentThreadId());
//	CUdpNonBlockingOutput *pObj = (CUdpNonBlockingOutput *)lpParameter;
//	while (1)
//	{
//        //WaitForSingleObject(pObj->m_Mutex, INFINITE);
//		pObj->RecvMsg(BLOCK_HEAD);
//       // ReleaseMutex(pObj->m_Mutex);
//	}
//	return 0;
//}

void CUdpNonBlockingOutput::SendFile()
{
    //通信、数据、服务、业务模型
	//1、获得文件的大小
	int start, end;
	HANDLE  hFile;
	DWORD   dwHighSize, dwBytesRead;
    hFile = CreateFile(_T(FILENAME), GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	dwFileSize = GetFileSize(hFile, &dwHighSize);
	std::cout << "dwFileSize=" << dwFileSize << std::endl;
	//2、读文件内容到 BYTE * fileData 中
	BOOL bSuccess;
	fileData = (BYTE*)malloc(dwFileSize);
	bSuccess = ReadFile(hFile, fileData, dwFileSize, &dwBytesRead, NULL);
	CloseHandle(hFile);

	//3、判断文件是否成功读取
	if (!bSuccess || (dwBytesRead != dwFileSize))
	{
		std::cout << "读取失败" << std::endl;;
		free(fileData);
		return;
	}
	//开启一个线程
    /*  m_Mutex = CreateMutex(NULL, TRUE, NULL);
      HANDLE ThreadHandle = CreateThread(NULL, 0, &CUdpNonBlockingOutput::ThreadFun, (LPVOID)this, NULL, NULL);
      CloseHandle(ThreadHandle);*/

	//4、根据文件大小和设定的包大小,判断是一次发送还是分包发送
	DWORD  retval = 0;
	UINT   DataPos = 0;
	BYTE   *eachBuf = new BYTE[BLOCK_DATA_SIZE + 2 * FILE_HEAD];
	memset(eachBuf, 0, BLOCK_DATA_SIZE + 2 * FILE_HEAD);
	eachBuf[DataPos++] = BEGIN_NUM >> 24 & 0xff;//文件起始标识符
	eachBuf[DataPos++] = BEGIN_NUM >> 16 & 0xff;
	eachBuf[DataPos++] = BEGIN_NUM >> 8 & 0xff;
	eachBuf[DataPos++] = BEGIN_NUM & 0xff;
	eachBuf[DataPos++] = dwFileSize >> 24 & 0xff;
	eachBuf[DataPos++] = dwFileSize >> 16 & 0xff;
	eachBuf[DataPos++] = dwFileSize >> 8 & 0xff;
	eachBuf[DataPos++] = dwFileSize & 0xff;

	//5、若待发送的文件小于分包大小,则一次发完
    //start = clock();
	if (BLOCK_DATA_SIZE >= dwFileSize)
	{
		//二次封装要发送的数据包 //1、-----做关键包发送------
		memcpy(&eachBuf[DataPos], fileData, dwFileSize);//报文格式:报文起始标识符(4)+文件大小(4)+数据(dwFileSize)
        retval = sendto(m_socketClient, (char*)eachBuf, dwFileSize + 2 * FILE_HEAD, 0, (SOCKADDR*)&Client, m_len);
	}
	//6、分步发送
	else
	{
		DWORD n = dwFileSize / BLOCK_DATA_SIZE;  //共需要几次全额发送
		DWORD yu = dwFileSize % BLOCK_DATA_SIZE; //最后剩下的字 符大小
        std::cout << "n=" << n << "yu=" << yu << "retval=" << retval << std::endl;
		//6.1 首先发送文件起始标识符、文件大小,连发三次,建立UDP连接	//报文格式:文件起始位(4)+文件大小(4)
        //2、-----做关键包发送------
        SendKeyPackage((char*)eachBuf, 2 * FILE_HEAD);
        //sendto(m_socketClient, (char*)eachBuf, 2 * FILE_HEAD, MSG_DONTROUTE, (SOCKADDR*)&Client, m_len);

		//DWORD lTimeOut = 50;
		//setsockopt(m_socketClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&lTimeOut, sizeof(DWORD));//设置超时重发	
        //recvfrom(m_socketServer, m_RecvBuff, BLOCK_DATA_SIZE + 2 * FILE_HEAD, 0, (struct sockaddr*)&ClientAddr, &m_len);
		for (int i = 0; i < n; i++)
		{
			DataPos = 0;
            eachBuf[DataPos++] = DATA_NUM >> 24 & 0xff;//文件起始标识符
            eachBuf[DataPos++] = DATA_NUM >> 16 & 0xff;
            eachBuf[DataPos++] = DATA_NUM >> 8 & 0xff;
            eachBuf[DataPos++] = DATA_NUM & 0xff;
			eachBuf[DataPos++] = i >> 24 & 0xff;
			eachBuf[DataPos++] = i >> 16 & 0xff;
			eachBuf[DataPos++] = i >> 8 & 0xff;
			eachBuf[DataPos++] = i & 0xff;
			//std::cout << DataPos << std::endl;
			memcpy(&eachBuf[DataPos], fileData + i*BLOCK_DATA_SIZE, BLOCK_DATA_SIZE);//报文格式:数据包序列号(4)+数据(BLOCK_DATA_SIZE)
            if (sendto(m_socketClient, (char*)eachBuf, BLOCK_DATA_SIZE + 2 * BLOCK_HEAD, 0, (SOCKADDR*)&Client, m_len) < 0)
                std::cout << "Sendto error!!!";
		}
		DataPos = 0;
		eachBuf[DataPos++] = END_NUM >> 24 & 0xff;//文件结束标识符
		eachBuf[DataPos++] = END_NUM >> 16 & 0xff;
		eachBuf[DataPos++] = END_NUM >> 8 & 0xff;
		eachBuf[DataPos++] = END_NUM & 0xff;
		eachBuf[DataPos++] = n >> 24 & 0xff;
		eachBuf[DataPos++] = n >> 16 & 0xff;
		eachBuf[DataPos++] = n >> 8 & 0xff;
		eachBuf[DataPos++] = n & 0xff;
		memcpy(&eachBuf[DataPos], fileData + n*BLOCK_DATA_SIZE, yu);//报文格式:文件结束标识符(4)+ 数据包序列号(4)+数据(yu),断开UDP连接
		//3、-----做关键包发送------
        SendKeyPackage((char*)eachBuf, yu + FILE_HEAD + BLOCK_HEAD);
        //sendto(m_socketClient, (char*)eachBuf, yu + FILE_HEAD + BLOCK_HEAD, 0, (SOCKADDR*)&Client, m_len);
       
        int flag_recv=1, flag_status = 0; char charFileSize[4] = { 0 };
        char *RecvLackNumBuf = new char[2 * 1024 + BLOCK_HEAD + FILE_HEAD+1];//4、-----做关键包接收------ 
        while (flag_recv)
        {
            start = clock();
            //接收下一步 操作 命令 重传机制
            memset(RecvLackNumBuf, 0, 2 * 1024 + BLOCK_HEAD + FILE_HEAD + 1);
            int ret = recvfrom(m_socketServer, RecvLackNumBuf, 2 * 1024 + 2 * FILE_HEAD + 1, 0, (struct sockaddr*)&ClientAddr, &m_len);
            if (ret > 0)
            {
                std::cout << "Check Recv:" << ret <<std::endl;
               // break;
            }    
            //end = clock();
            flag_status = 0;
            memcpy(charFileSize, RecvLackNumBuf, FILE_HEAD); //拷贝前4个字节
            for (int i = 0; i < 4; i++)
            {
                flag_status += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件标识符
            }
            memcpy(charFileSize, RecvLackNumBuf + FILE_HEAD, FILE_HEAD); //拷贝第5-8个字节
            dwFileSize = 0;
            for (int i = 0; i < 4; i++)
            {
                dwFileSize += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件大小
            }
            if (flag_status == END_ALL)
            {
                std::cout << "end" << std::endl;
                memcpy(charFileSize, RecvLackNumBuf, FILE_HEAD);//关键数据包接收、回复 、
                sendto(m_socketClient, charFileSize, sizeof(charFileSize), 0, (struct sockaddr*)&Client, m_len);

                flag_recv = 0;
                break;
            }
            else if (flag_status == CHECK_NUM)
            {
                std::cout << "check" << std::endl;
                memcpy(charFileSize, RecvLackNumBuf, FILE_HEAD);//关键数据包接收、回复 、
                sendto(m_socketClient, charFileSize, sizeof(charFileSize), 0, (struct sockaddr*)&Client, m_len);

                if (dwFileSize > 510)
                    dwFileSize = 510;//需要改进,代码不规范,每次重发包数目 最大为510个
                for (int i = 0; i < dwFileSize; i++)
                {
                    UINT DataPos = 0;
                    memcpy(charFileSize, RecvLackNumBuf + i* FILE_HEAD + 2 * BLOCK_HEAD, FILE_HEAD); //拷贝第9个字节后面
                    for (int i = 0; i < 4; i++)
                    {
                        DataPos += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件大小
                    }
                    UINT Num = 0;
                    eachBuf[Num++] = DATA_NUM >> 24 & 0xff;//文件起始标识符
                    eachBuf[Num++] = DATA_NUM >> 16 & 0xff;
                    eachBuf[Num++] = DATA_NUM >> 8 & 0xff;
                    eachBuf[Num++] = DATA_NUM & 0xff;
                    eachBuf[Num++] = DataPos >> 24 & 0xff;
                    eachBuf[Num++] = DataPos >> 16 & 0xff;
                    eachBuf[Num++] = DataPos >> 8 & 0xff;
                    eachBuf[Num++] = DataPos & 0xff;
                    memcpy(&eachBuf[Num], fileData + DataPos*BLOCK_DATA_SIZE, BLOCK_DATA_SIZE);//报文格式:数据类型(4) +数据包序列号(4)+数据(BLOCK_DATA_SIZE)
                    if (sendto(m_socketClient, (char*)eachBuf, BLOCK_DATA_SIZE + FILE_HEAD + BLOCK_HEAD + 1, 0, (SOCKADDR*)&Client, m_len) < 0)
                    {
                        std::cout << "Check sendto error!!!" << std::endl;
                        break;
                    }
                }
                UINT Num = 0;
                eachBuf[Num++] = END_CHECK_NUM >> 24 & 0xff;//重发校验结束标识符
                eachBuf[Num++] = END_CHECK_NUM >> 16 & 0xff;
                eachBuf[Num++] = END_CHECK_NUM >> 8 & 0xff;
                eachBuf[Num++] = END_CHECK_NUM & 0xff;
                eachBuf[Num++] = END_CHECK_NUM >> 24 & 0xff;//重发校验结束标识符
                eachBuf[Num++] = END_CHECK_NUM >> 16 & 0xff;
                eachBuf[Num++] = END_CHECK_NUM >> 8 & 0xff;
                eachBuf[Num++] = END_CHECK_NUM & 0xff;
                for (int i = 0; i < 3; i++)//5、-----做关键包发送------
                if(sendto(m_socketClient, (char*)eachBuf, FILE_HEAD + BLOCK_HEAD, 0, (SOCKADDR*)&Client, m_len) < 0)
                {
                        std::cout << "Check sendto error!!!" << std::endl;
                        break;
                 }
            }
           
            //flag_recv = 0;
        }

        std::cout << "flag_status:" << flag_status << "-" << "dwFileSize:" << dwFileSize << std::endl;
        //Sleep(500);
		end = clock();
		std::cout << "耗时:" << end - start << std::endl;
	}
}

#ifndef UDPNONBLOCKINGINPUT
#define UDPNONBLOCKINGINPUT
#include "winsock.h"   
#include <iostream>  
#include <process.h>
#include "time.h" 
#include "windows.h" 
#include <stdio.h>
#include <vector>
#include <algorithm>
#pragma comment(lib,"wsock32.lib")   

#define SERVER_PORT 2000   
#define CLIENT_PORT 3000  
#define BEGIN_NUM 19900711
#define DATA_NUM  20160113
#define CHECK_NUM 20161817
#define END_CHECK_NUM 20160114
#define END_NUM   11700991
#define END_ALL   20160115
#define BLOCK_DATA_SIZE (10 * 1024)
#define FILE_HEAD  4
#define BLOCK_HEAD 4
#define DESTADDRESS "192.168.27.170"
#define FILENAME "D:\\file.MP4"
//每个包的协议头:数据类型(4字节,起始标识、终结标识、数据标识)+数据帧长度(4)+包序列号(4)

//UDT包头
typedef struct _UDP_HEAD_
{
	UINT     m_PacketType;			 //包类型 1数据包 2确认包
	UINT     m_PacketLen;			 //本次数据帧长度(不包括包头大小,仅仅数据部分大小)
	UINT     m_PacketId;             //包序列号
	UINT     m_Check;                //效验和 以上字段的相加
}UDP_HEAD;

class CUdpNonBlockingInput
{
public:
	CUdpNonBlockingInput()
	{
		m_flag_status = 0;
		m_dwFileSize = 0;
		m_flagNonBlocking = 0;
		InitSocket();
	}
	~CUdpNonBlockingInput()
	{
		closesocket(m_socketServer);
		WSACleanup();
	}

	//static DWORD WINAPI ThreadFun(LPVOID pM);

	void InitSocket();

    void SendKeyPackage(char *eachBuf, UINT size);

    void RecvKeyPackage(char *eachBuf, UINT size);

	void StartRevDataes();

	void SendLackPackageNum(std::vector<UINT> m_lackPackageNum);
private:

	char RecvBuff[1024];
	char SendBuff[1024];

	sockaddr_in Server;
	sockaddr_in ServerAddr;
	SOCKET m_socketServer;
	SOCKET m_socketClient;

	int m_len;
	int m_flagNonBlocking;
	DWORD  m_dwFileSize;
	UINT m_prePackageNum;
	UINT m_flag_status;
	HANDLE m_Mutex;

	static std::vector<UINT> m_lackPackageNum;
};


#endif 

#include "UdpNonBlockingInput.h"

void CUdpNonBlockingInput::InitSocket()
{
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		printf("sock init failed!\n");
		return;
	}
	m_socketServer = socket(AF_INET, SOCK_DGRAM, 0);//创建socket 
	m_socketClient = socket(AF_INET, SOCK_DGRAM, 0);//创建socket 
	if (m_socketServer == SOCKET_ERROR)
	{
		printf("sock create failed\n");
		WSACleanup();
		return;
	}

	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	ServerAddr.sin_port = htons(SERVER_PORT);
	if (bind(m_socketServer, (struct sockaddr*)&ServerAddr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)//绑定   
	{
		printf("sock bind error!\n");
		closesocket(m_socketServer);
		WSACleanup();
		return;
	}
	//
	Server.sin_family = AF_INET;
	Server.sin_addr.s_addr = inet_addr(DESTADDRESS);
	Server.sin_port = htons(CLIENT_PORT);
	m_len = sizeof(Server);
	return;
}

void CUdpNonBlockingInput::SendKeyPackage(char *eachBuf,UINT size)
{
    char recvKeyWord[4] = { 0 }, keyWord[4] = { 0 }, charFileSize[4] = { 0 };
    memcpy(keyWord, eachBuf, FILE_HEAD); //拷贝前4个字节
    sendto(m_socketClient, (char*)eachBuf, size, 0, (SOCKADDR*)&Server, m_len);

    //    DWORD lTimeOut = 1;
    //   setsockopt(m_socketServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&lTimeOut, sizeof(DWORD));//设置超时重发

    for (int i = 0, startClock, endClock; i < 100; i++)
    {
        startClock = clock();
        while (1)
        {
            if (recvfrom(m_socketServer, recvKeyWord, FILE_HEAD, 0, (struct sockaddr*)&ServerAddr, &m_len) < 0)
                return;
            else
            {
                if (!strcmp(recvKeyWord, keyWord))
                {
                    return;
                }
            }
            endClock = clock();
            if ((endClock - startClock) > 5)
                break;
        }
        sendto(m_socketClient, (char*)eachBuf, size, 0, (SOCKADDR*)&Server, m_len);
    }
}

void CUdpNonBlockingInput::RecvKeyPackage(char *eachBuf, UINT size)
{
    char recvKeyWord[4] = { 0 };
    int startClock = clock(), endClock;//接收关键包
    while (1)
    {
        if (recvfrom(m_socketServer, eachBuf, size, 0, (struct sockaddr*)&ServerAddr, &m_len) > 0)
        {
            memcpy(recvKeyWord, eachBuf, FILE_HEAD);
            break;
        }
        endClock = clock();
        if ((endClock - startClock) > 5)
            break;
    }
    startClock = clock();
    while (1)
    {
        if (sendto(m_socketClient, recvKeyWord, sizeof(recvKeyWord), 0, (struct sockaddr*)&Server, m_len) > 0)
            break;
        endClock = clock();
        if ((endClock - startClock) > 5)
            break;
    }
	
}

std::vector<UINT> CUdpNonBlockingInput::m_lackPackageNum;

void CUdpNonBlockingInput::SendLackPackageNum(std::vector<UINT> m_lackPackageNum)
{
	int startClock = clock(),endClock;
	if (m_lackPackageNum.size() > 0)
	{

    char *SendLackNumBuf = new char[2 * 1024 + BLOCK_HEAD + FILE_HEAD];
	int DataPos = 0;
    SendLackNumBuf[DataPos++] = CHECK_NUM >> 24 & 0xff;//报文格式:校验标识符CHECK_NUM + 包大小FileSize
    SendLackNumBuf[DataPos++] = CHECK_NUM >> 16 & 0xff;
    SendLackNumBuf[DataPos++] = CHECK_NUM >> 8 & 0xff;
    SendLackNumBuf[DataPos++] = CHECK_NUM & 0xff;
    SendLackNumBuf[DataPos++] = m_lackPackageNum.size() >> 24 & 0xff;
    SendLackNumBuf[DataPos++] = m_lackPackageNum.size() >> 16 & 0xff;
    SendLackNumBuf[DataPos++] = m_lackPackageNum.size() >> 8 & 0xff;
    SendLackNumBuf[DataPos++] = m_lackPackageNum.size() & 0xff;

    for (int i = 0; i < m_lackPackageNum.size(); i++)
    {
        if (DataPos >(2 * 1024))//防止数组下标越界 重发的数据包不会超过510个
            break;
        SendLackNumBuf[DataPos++] = m_lackPackageNum[i] >> 24 & 0xff;//报文格式:数据标识符+包大小+数据内容
        SendLackNumBuf[DataPos++] = m_lackPackageNum[i] >> 16 & 0xff;
        SendLackNumBuf[DataPos++] = m_lackPackageNum[i] >> 8 & 0xff;
        SendLackNumBuf[DataPos++] = m_lackPackageNum[i] & 0xff;
    }
    SendKeyPackage(SendLackNumBuf, 2 * 1024 + BLOCK_HEAD + FILE_HEAD + 1);
    //sendto(m_socketClient, SendLackNumBuf, 2 * 1024 + BLOCK_HEAD + FILE_HEAD + 1, 0, (struct sockaddr*)&Server, m_len);

	if (SendLackNumBuf)
	{
		delete SendLackNumBuf;
		SendLackNumBuf = NULL;
	}
	}
	endClock = clock();
    std::cout << "重发耗时:" << endClock - startClock << "ms" << m_lackPackageNum.size() << std::endl;
}
定时检测线程
//DWORD WINAPI CUdpNonBlockingInput::ThreadFun(LPVOID lpParameter)
//{
//	printf("开启线程ID号为%d的子线程\n", GetCurrentThreadId());
//	CUdpNonBlockingInput *pObj = (CUdpNonBlockingInput *)lpParameter;
//	WaitForSingleObject(pObj->m_Mutex, INFINITE);
//	pObj->SendLackPackageNum(m_lackPackageNum);
//	ReleaseMutex(pObj->m_Mutex);
//	return 0;
//}

void CUdpNonBlockingInput::StartRevDataes()
{
	//创建接收缓存区 、创建文件 
	//开启一个线程
	//m_Mutex = CreateMutex(NULL, TRUE, NULL);
	//HANDLE ThreadHandle = CreateThread(NULL, 0, &CUdpNonBlockingInput::ThreadFun, (LPVOID)this, NULL, NULL);
	//CloseHandle(ThreadHandle);
	char *eachBuf = new char[BLOCK_DATA_SIZE + 3 * FILE_HEAD];//开启接收缓存区
	memset(eachBuf, 0, BLOCK_DATA_SIZE + 2 * FILE_HEAD + 1);
	FILE *fp;//创建文件
	unsigned int RecvNum = 0, flag_recv = 1;
	m_prePackageNum = 0;//当前序列号
	m_lackPackageNum.clear();//待确认的序列号队列
    fp = fopen(FILENAME, "wb");
	//sendto(m_socketClient, "Recv", sizeof("Revc") + 1, 0, (struct sockaddr*)&Server, m_len);
	//1、读取第一组数据,获取文件大小,建立UDP连接--------1、做关键包接收
    RecvKeyPackage(eachBuf, BLOCK_DATA_SIZE + 2 * FILE_HEAD);
	//recvfrom(m_socketServer, eachBuf, BLOCK_DATA_SIZE + 2 * FILE_HEAD, 0, (struct sockaddr*)&ServerAddr, &m_len);
	//m_from.sin_addr.S_un.S_addr = inet_addr(inet_ntoa(m_from.sin_addr));//String转换成char型函数c_str()
	char charFileSize[4] = { 0 };
	memcpy(charFileSize, eachBuf, FILE_HEAD); //拷贝前4个字节
	for (int i = 0; i < 4; i++)
	{
		m_flag_status += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件起始符
	}
	memcpy(charFileSize, eachBuf + FILE_HEAD, FILE_HEAD); //拷贝第5-8个字节
	for (int i = 0; i < 4; i++)
	{
		m_dwFileSize += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件大小
	}
	int startClock = clock(), endClock;
	if (m_flag_status == BEGIN_NUM)//成功建立UDP连接
	{
		//2、根据文件大小,判断是一次接收完
		if (m_dwFileSize <= BLOCK_DATA_SIZE)
		{
			fwrite(eachBuf + 2 * FILE_HEAD, m_dwFileSize, 1, fp);
			fclose(fp);
			delete eachBuf;
		}
		else//还是分步接收
		{
			//开辟接收内存
			char *FileBuffer = new char[m_dwFileSize];
			memset(FileBuffer, 0, m_dwFileSize);
			DWORD n = m_dwFileSize / BLOCK_DATA_SIZE;  //共需要几次全额发送
			DWORD yu = m_dwFileSize % BLOCK_DATA_SIZE; //最后剩下的字符大小
			//DWORD lTimeOut = 2;
			//setsockopt(m_socketServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&lTimeOut, sizeof(DWORD));//设置接收超时 2ms
            unsigned int DataPos = 0, flag_check = 0;
			flag_recv = 1;
			//---
            unsigned long ul = 1;
            ioctlsocket(m_socketServer, FIONBIO, (unsigned long *)&ul);//设置0阻塞模式
			while (flag_recv)
			{
				if (recvfrom(m_socketServer, eachBuf, BLOCK_DATA_SIZE + 2 * BLOCK_HEAD + 1, 0, (struct sockaddr*)&ServerAddr, &m_len) == -1)
				{	
					//std::cout << "Recv error!!!";
					//break;
				}	
				else
				RecvNum++;
				memcpy(charFileSize, eachBuf, FILE_HEAD); //拷贝前4个字节
				m_flag_status = 0;
				for (int i = 0; i < 4; i++)
				{
					m_flag_status += ((UCHAR)charFileSize[i]) << (8 * (4 - i - 1)); //获取文件标识符
				}
				switch (m_flag_status)
				{
				case BEGIN_NUM:break;
				case DATA_NUM: {
								   DataPos = 0;
								   for (int i = 0; i < 4; i++)
								   {
									   DataPos += ((UCHAR)eachBuf[i+FILE_HEAD]) << (8 * (4 - i - 1)); //获取数据包序列号
								   }
								   if (DataPos >= 0 && DataPos <= n)
								   {
                                       if (flag_check == 0)
                                       {                                 
	                                        if ((DataPos - m_prePackageNum) > 1)//获取缺失的分包序列号
					                        {
						                        for (int i = DataPos - m_prePackageNum - 1; i > 0; i--)
						                        {
							                        m_lackPackageNum.push_back(DataPos - i);
						                        }
					                        }
                                       }
                                       else//去掉收到的分包序列号
                                       {
                                          //std:: cout << 8;
                                           m_lackPackageNum.erase(remove(m_lackPackageNum.begin(), m_lackPackageNum.end(), DataPos), m_lackPackageNum.end());
                                       }
									   m_prePackageNum = DataPos;
									   //std::cout << DataPos << std::endl;
									   memcpy(FileBuffer + DataPos*BLOCK_DATA_SIZE, eachBuf + BLOCK_HEAD + FILE_HEAD, BLOCK_DATA_SIZE);
								   }
								   break;
								}
				case END_NUM:  {	
                                   std::cout << "END_NUM:" << RecvNum << std::endl;
                                   flag_check = 1;//丢包序号队列
								   memcpy(FileBuffer + n*BLOCK_DATA_SIZE, eachBuf + FILE_HEAD + BLOCK_HEAD, yu);

                                   char recvKeyWord[4] = { 0 };
                                   memcpy(recvKeyWord, eachBuf, FILE_HEAD);//关键数据包接收、回复 、
                                   sendto(m_socketClient, recvKeyWord, sizeof(recvKeyWord), 0, (struct sockaddr*)&Server, m_len);

                                   if (m_lackPackageNum.size() > 0)//判断是否丢包
                                   {
                                       /*for (int i = 0; i < m_lackPackageNum.size(); i++)
                                           std::cout << m_lackPackageNum[i] << std::endl;*/
                                       //for (int i = 0; i < 3;i++)
                                       SendLackPackageNum(m_lackPackageNum);//2、-----做关键包发送------
                                   }	   
                                   else//发送结束标识符
                                   {
                                       std::cout << "SUCESS!!!" << std::endl;
                                       if (eachBuf)
                                       {
                                           delete eachBuf;
                                           eachBuf = NULL;
                                       }
                                       fwrite(FileBuffer, m_dwFileSize, 1, fp);
                                       fclose(fp);
                                       char SendCheckCountBuf[4];
                                       DataPos = 0;
                                       SendCheckCountBuf[DataPos++] = END_ALL >> 24 & 0xff;//报文格式:校验标识符CHECK_NUM + 包大小FileSize
                                       SendCheckCountBuf[DataPos++] = END_ALL >> 16 & 0xff;
                                       SendCheckCountBuf[DataPos++] = END_ALL >> 8 & 0xff;
                                       SendCheckCountBuf[DataPos++] = END_ALL & 0xff;//3、-----做关键包发送------
                                       SendKeyPackage(SendCheckCountBuf, BLOCK_HEAD + 1);
                                       // sendto(m_socketClient, SendCheckCountBuf, BLOCK_HEAD + 1, 0, (struct sockaddr*)&Server, m_len);
                                       flag_recv = 0;
                                   }
								   break;
							    }	
                case END_CHECK_NUM:{
                                       std::cout << "END_CHECK_NUM" << std::endl;
                                       flag_check = 1;//丢包序号队列
                                       if (m_lackPackageNum.size() > 0)//判断是否丢包
                                       {
                                           SendLackPackageNum(m_lackPackageNum);//4、-----做关键包发送------
                                       }
                                       else//发送结束标识符
                                       {                                       
                                           if (eachBuf)
                                           {
                                               delete eachBuf;
                                               eachBuf = NULL;
                                           }
                                           fwrite(FileBuffer, m_dwFileSize, 1, fp);
                                           fclose(fp);
                                           char SendCheckCountBuf[4];
                                           DataPos = 0;
                                           SendCheckCountBuf[DataPos++] = END_ALL >> 24 & 0xff;//报文格式:校验标识符CHECK_NUM + 包大小FileSize
                                           SendCheckCountBuf[DataPos++] = END_ALL >> 16 & 0xff;
                                           SendCheckCountBuf[DataPos++] = END_ALL >> 8 & 0xff;
                                           SendCheckCountBuf[DataPos++] = END_ALL & 0xff;
                                           //5、-----做关键包发送------
                                           SendKeyPackage(SendCheckCountBuf, BLOCK_HEAD + 1);
                                           //sendto(m_socketClient, SendCheckCountBuf, BLOCK_HEAD + 1, 0, (struct sockaddr*)&Server, m_len);
                                           flag_recv = 0;
                                           std::cout << "SUCESS!!!" << std::endl;
                                       }
                                       break;
                                   }
				}
			}
		}
	}
	else
	{
		return;
	}
	endClock = clock();
	std::cout << "收到数据:" << RecvNum << "-" << m_dwFileSize << "耗时:" << endClock - startClock << std::endl;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值