【C++】Windows,实现TFTP下载客户端(不支持上传功能)

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

//初始化winsock,获取udp sokcet
int getUdpSocket()
{
	WORD ver = MAKEWORD(2, 2);
	WSADATA lpData;
	int err = WSAStartup(ver, &lpData);
	if (err != 0)
		return -1;
	int udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsocket == INVALID_SOCKET)
		return -2;
	return udpsocket;
}

//获取指定地址信息数据结构
sockaddr_in getAddr(char *ip, int port)
{
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.S_un.S_addr = inet_addr(ip);
	return addr;
}

//组装请求下载报文
char *RequestDownloadPack(const char *content,int &datalen)
{
	int len = strlen(content);
	char *buf = new char[len + 2 + 2 + 5];
	buf[0] = 0x00;
	buf[1] = 0x01;
	memcpy(buf + 2, content, len);
	memcpy(buf + 2 + len, "\0", 1);
	memcpy(buf + 2 + len + 1, "octet", 5);
	memcpy(buf + 2 + len + 1 + 5, "\0", 1);
	datalen = len + 2 + 2 + 5;
	return buf;
}


int main()
{
	SOCKET sock = getUdpSocket();	
	sockaddr_in addr = getAddr("192.168.23.1", 69);//TFTP服务器地址
	int datalen;
	char *sendData = RequestDownloadPack("123.flac", datalen);//请求下载123.flac文件

	int addrlen;
	int res = sendto(sock, sendData, datalen, 0, (sockaddr*)&addr, sizeof(addr));
	if (res != datalen)
	{
		std::cout << "sendto failed" << std::endl;
		goto END;
	}
	delete[]sendData;

	FILE *f = fopen("123.flac", "wb");//本地保存为123.flac
	if (f == NULL)
	{
		std::cout << "File open failed!" << std::endl;
		goto END;
	}

	while (true)
	{
		char buf[1024];
		sockaddr_in server;
		int len = sizeof(server);
		res = recvfrom(sock, buf, 1024, 0, (sockaddr*)&server, &len);
        //发送请求下载到服务器69端口后,服务端会随机找一个端口来发送数据给客户端,客户端返回确认报文时,不能再向69端口发送ACK,而是向该端口发送ACK,所以需要保存新的地址信息server,用该地址信息来进行数据传输
		if (res > 0)
		{
			short flag;
			memcpy(&flag, buf, 2);
			flag = ntohs(flag);//网络字节序转换为主机字节序,很重要
			if (flag == 3)
			{
				short no;
				memcpy(&no, buf + 2, 2);
				fwrite(buf + 4, res - 4, 1, f);
				if (res > 0 && res < 512)
				{
					std::cout << "download finished!" << std::endl;
					break;
				}
				std::cout << "Pack No=" << ntohs(no) << std::endl;
                //发送确认报文
				char ack[4];
				ack[0] = 0x00;
				ack[1] = 0x04;
				memcpy(ack + 2, &no, 2);
				int sendlen = sendto(sock, ack, 4, 0, (sockaddr*)&server, sizeof(server));
				if (sendlen != 4)
				{
					std::cout << "ack error" << WSAGetLastError() << std::endl;
					break;
				}
			}
			if (flag == 5)
			{
                //服务端返回了错误信息
				short errorcode;
				memcpy(&errorcode, buf + 2, 2);
				errorcode = ntohs(errorcode);
				char strError[1024];
				int iter = 0;
				while ((*buf+iter+4) != 0)
				{
					memcpy(strError + iter, buf + iter + 4, 1);
					++iter;
				}
				std::cout << "Error " << errorcode << "  " << strError << std::endl;
				break;
			}
		}
	}
	fclose(f);
END:
	system("pause");
	return 0;
}

TFTP协议,报文格式参考链接:https://www.cnblogs.com/newwy/p/3231681.html

猜你喜欢

转载自blog.csdn.net/shihoongbo/article/details/81744083