TFTP服务器接收文件

最近看到很多UDP可靠文件传输的文章,其中有提到TFTP协议,在CodeProject上面搜到一个客户端的,觉得很不错,http://www.codeproject.com/KB/IP/tftp_client.aspx

这里模仿者写了个服务器端,只实现了服务器端的接受文件,配合着基本上就可以作为一般的UDP文件接发例子了,大致代码如下

头文件:

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

#define TFTP_DEFAULT_PORT    69

#define TFTP_OPCODE_READ     1
#define TFTP_OPCODE_WRITE    2
#define TFTP_OPCODE_DATA     3
#define TFTP_OPCODE_ACK      4
#define TFTP_OPCODE_ERROR    5

#define TFTP_DEFAULT_MODE    "octet"

#define TFTP_DATA_PKT_SIZE   512

#define TFTP_ERROR_MSG_MAX1024
#define TFTP_PACKET_DATA_MAX1024

#define TFTP_RETRY     3

#define TFTP_RESULT_ERROR1
#define TFTP_RESULT_CONTINUE    2
#define TFTP_RESULT_DONE3

#define TFTP_STATUS_NOTHING1
#define TFTP_STATUS_GET2
#define TFTP_STATUS_PUT3
#define TFTP_STATUS_ERROR4

#define TFTP_TIMEOUT5000 //ms,5Ãë

#include <fstream>

class CTFTPServer  
{
protected:
class CPacket
{
public:
CPacket();
~CPacket();
bool AddByte(BYTE c);
bool AddWord(WORD c);
bool AddString(char* str);
bool AddMem(char* mem,int len);
unsigned char* GetData(int pos=0);
WORD GetWord(int pos);
BYTE GetByte(int pos);
bool GetMem(int pos,char* mem,int len);
int  GetSize();
void SetSize(int N);
void Clear();
bool IsValid();
bool IsNull();
bool IsACK(int blocknum);
bool IsDATA(int blocknum);
bool IsError();
bool GetError(char* buf,unsigned int buflen);

void MakeACK(int blocknum);
void MakeDATA(int blocknum,char* data,int size);
bool MakeWRQ(int opcode,char* filename);

protected:
unsigned char n_dataBuf[TFTP_PACKET_DATA_MAX];
int m_nSize;
};
public:
CTFTPServer();
virtual ~CTFTPServer();
bool Start(std::string strIP,USHORT nPort = TFTP_DEFAULT_PORT);
bool Stop();
protected:
static DWORD WINAPI WorkThread(LPVOID lParam);
HANDLE m_hWorkThread;
private:
SOCKETm_sockSvr;
SOCKADDR_INm_sockAddr;
SOCKADDR_INm_clientAddr;
boolm_bRunning;
CPacketm_packetRecv;
CPacketm_packetSend;

FILE*m_fileRecv;
unsigned intm_nPackNum;
unsigned intm_nTotalBytes;
charm_chFileName[64];
std::ofstreamm_logFile;
void ProcessRecvData();
};

实现文件:

 

// TFTPServer.cpp: implementation of the CTFTPServer class.
//
//////////////////////////////////////////////////////////////////////

#include “stdafx.h”

#include “TFTPServer.h”

//////////////////////////////////////////////////////////////////////
// TFTP packet class functions
//////////////////////////////////////////////////////////////////////
CTFTPServer::CPacket::CPacket()
{
Clear();
}
CTFTPServer::CPacket::~CPacket()
{

}

unsigned char* CTFTPServer::CPacket::GetData(int pos)
{
return &(n_dataBuf[pos]);
}

int CTFTPServer::CPacket::GetSize()
{
return m_nSize;
}

void CTFTPServer::CPacket::Clear()
{
m_nSize=0;
memset(n_dataBuf,0,TFTP_PACKET_DATA_MAX);
}

bool CTFTPServer::CPacket::IsValid()
{
return (m_nSize!=-1);
}

bool CTFTPServer::CPacket::IsNull()
{
return (m_nSize == 0);
}

void CTFTPServer::CPacket::SetSize(int N)
{
if ( N > TFTP_PACKET_DATA_MAX)
{
m_nSize =-1;
}
else
{
m_nSize =N;
}
}

bool CTFTPServer::CPacket::AddByte(BYTE c)
{
if (!IsValid()) return false;
if (m_nSize >= TFTP_PACKET_DATA_MAX)
{
m_nSize=-1;
return false;
}
n_dataBuf[m_nSize] = (unsigned char)c;
m_nSize++;
return true;
}

bool CTFTPServer::CPacket::AddMem(char* mem,int len)
{
if (!IsValid()) return false;
if ( m_nSize+len>= TFTP_PACKET_DATA_MAX)
{
m_nSize=-1;
return false;
}
memcpy(&(n_dataBuf[m_nSize]),mem,len);
m_nSize+=len;
return true;
}

bool CTFTPServer::CPacket::AddWord(WORD c)
{
//一个Word等于两个Byte,先存高一个Byte,再存低Byte
if (!AddByte( (((BYTE)&c)+1) )) return false;
return (!AddByte( ((BYTE)&c) ));
}

bool CTFTPServer::CPacket::AddString(char* str)
{
if ((str==NULL)||(strlen(str)==0)) return true;
int n=strlen(str);

for (int i=0;i<n;i++)
if (!AddByte(*(str+i)))
return false;
return true;//AddByte(0);
}

bool CTFTPServer::CPacket::GetMem(int pos,char* mem,int len)
{
if (pos>m_nSize) return false;
if (len<m_nSize-pos) return false;

memcpy(mem,&(n_dataBuf[pos]),m_nSize-pos);

return true;

}

WORD CTFTPServer::CPacket::GetWord(int pos)
{
WORD hi = GetByte(pos);
WORD lo = GetByte(pos+1);
return ((hi<<8)|lo);
}

BYTE CTFTPServer::CPacket::GetByte(int pos)
{
return (BYTE)n_dataBuf[pos];
}

bool CTFTPServer::CPacket::IsACK(int blocknum)
{
if (GetSize()!=4) return false;
WORD opcode = GetWord(0);
WORD block = GetWord(2);
if (opcode != TFTP_OPCODE_ACK) return false;
if (blocknum != block) return false;

return true;
}

bool CTFTPServer::CPacket::IsDATA(int blocknum)
{
if (GetSize()<4) return false;
WORD opcode = GetWord(0);
WORD block = GetWord(2);
if (opcode != TFTP_OPCODE_DATA) return false;
if (blocknum != block) return false;

return true;
}

bool CTFTPServer::CPacket::IsError()
{
if (GetSize()<4) return false;
WORD opcode = GetWord(0);
return (opcode == TFTP_OPCODE_ERROR);
}

bool CTFTPServer::CPacket::GetError(char buf,unsigned int buflen)
{
if (!IsError()) return false;
WORD errcode = GetWord(2);
char
errmsg = (char*)GetData(4);
if (buf==NULL) return false;
if (strlen(errmsg)+10>buflen) return false;
sprintf(buf,”#%i:%s”,errcode,errmsg);
return true;
}

void CTFTPServer::CPacket::MakeACK(int blocknum)
{
Clear();
AddWord(TFTP_OPCODE_ACK);
AddWord(blocknum);
}

bool CTFTPServer::CPacket::MakeWRQ(int opcode,char* filename)
{
ATLASSERT(filename!=NULL);
if (strlen(filename)>TFTP_DATA_PKT_SIZE) return false;
Clear();
AddWord(opcode);
AddString(filename);
AddByte(0);
AddString(TFTP_DEFAULT_MODE);
AddByte(0);
return true;
}

void CTFTPServer::CPacket::MakeDATA(int blocknum,char* data,int size)
{
ATLASSERT(data!=NULL);
ATLASSERT(size<=TFTP_DATA_PKT_SIZE);
Clear();
AddWord(TFTP_OPCODE_DATA);
AddWord(blocknum);
AddMem(data,size);
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTFTPServer::CTFTPServer():m_bRunning(false),m_sockSvr(INVALID_SOCKET)
{
::ZeroMemory(&m_sockAddr,sizeof(m_sockAddr));
::ZeroMemory(m_chFileName,64);
m_fileRecv = NULL;

m_logFile.open(“TFTPSvr.log”);
}

CTFTPServer::~CTFTPServer()
{

}

bool CTFTPServer::Start(std::string strIP,USHORT nPort/ = TFTP_DEFAULT_PORT/)
{
int nError = 0;
int nRet = 0;
m_sockSvr = ::socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(INVALID_SOCKET == m_sockSvr)
{
nError = ::WSAGetLastError();
return false;
}
//绑定到端口
m_sockAddr.sin_family = AF_INET;
m_sockAddr.sin_addr.s_addr = ::inet_addr(strIP.c_str());
m_sockAddr.sin_port = ::htons(nPort);
nRet = ::bind(m_sockSvr,(sockaddr*)&m_sockAddr,sizeof(m_sockAddr));
if (SOCKET_ERROR == nRet)
{
nError = ::WSAGetLastError();
return false;
}
m_bRunning = true;

m_hWorkThread = ::CreateThread(NULL,0,WorkThread,this,0,0);
if (m_hWorkThread != NULL)
{
::CloseHandle(m_hWorkThread);
}
return true;
}

bool CTFTPServer::Stop()
{
if (m_bRunning)
{
m_bRunning = false;
::closesocket(m_sockSvr);
}

return true;
}

DWORD WINAPI CTFTPServer::WorkThread( LPVOID lParam )
{
CTFTPServer pSvr = reinterpret_cast<CTFTPServer>(lParam);

if (pSvr != NULL)
{
while (pSvr->m_bRunning)
{
int nRecvLen;

int SenderAddrSize = sizeof(sockaddr_in);
nRecvLen = recvfrom(
pSvr->m_sockSvr,
(char)pSvr->m_packetRecv.GetData(),
TFTP_PACKET_DATA_MAX,
0,
(SOCKADDR
)&pSvr->m_clientAddr,
&SenderAddrSize
);

pSvr->m_logFile<<”CTFTPServer::WorkThread 接收到来自”<<::inet_ntoa(pSvr->m_clientAddr.sin_addr)<<”:”<<
::ntohs(pSvr->m_clientAddr.sin_port)<<std::endl;

if ( ( nRecvLen == 0) || (nRecvLen == WSAECONNRESET ) )
{
ATLTRACE(“Connection Closed./n”);
//break;
}
if ( nRecvLen == SOCKET_ERROR)
{
ATLTRACE(“recv error./n”);
//break;
}
char bufTrace[1024] = {0};
::sprintf(bufTrace,”%s/n”,pSvr->m_packetRecv.GetData());
ATLTRACE(bufTrace);
pSvr->m_packetRecv.SetSize(nRecvLen);
pSvr->ProcessRecvData();
//Sleep(1);
}
}

return 0;
}

void CTFTPServer::ProcessRecvData()
{
//处理接收数据
DWORD dwOpCode = m_packetRecv.GetWord(0);
switch (dwOpCode)
{
case TFTP_OPCODE_READ:
{
ATLTRACE(“客户端下载文件/n”);
break;
}
case TFTP_OPCODE_WRITE:
{
//获取文件名
::strcpy(m_chFileName,(const char*)m_packetRecv.GetData(2));
m_packetRecv.Clear();
char bufTrace[1024] = {0};
::sprintf(bufTrace,”客户端上传文件:%s/n”,m_chFileName);
ATLTRACE(bufTrace);
if( (m_fileRecv = fopen( m_chFileName, “wb” )) == NULL )
{
ATLTRACE(“创建文件失败!/n”);
}
//上传文件服务器确认包
m_packetSend.MakeACK(0);

//发送数据
int nLenSended = ::sendto(m_sockSvr,(char)m_packetSend.GetData(),m_packetSend.GetSize(),
0,(SOCKADDR
)&m_clientAddr,sizeof(m_clientAddr));
m_packetSend.Clear();

//下一个数据序号为1
m_nPackNum = 1;
m_nTotalBytes = 0;
break;
}
case TFTP_OPCODE_DATA:
{
DWORD dwDataNum = m_packetRecv.GetWord(2);

if (m_nPackNum != dwDataNum)
{
m_logFile<<”TFTP_OPCODE_DATA 期望数据序号:”<<m_nPackNum<<” 与发过来的不一致:”<<dwDataNum<<std::endl;
break;
}

char bufTrace[1024] = {0};
::sprintf(bufTrace,”客户端上传文件数据序号:%d/n”,dwDataNum);
ATLTRACE(bufTrace);
char dataBuf[TFTP_DATA_PKT_SIZE] = {0};
if (!m_packetRecv.GetMem(4,dataBuf,TFTP_DATA_PKT_SIZE))
{
ATLTRACE(“复制数据错误/n”);
}
//数据前面4个字节为opcode和packnum
int nDataLen = m_packetRecv.GetSize() - 4;
m_nTotalBytes += nDataLen;
m_nPackNum++;
//m_logFile<<”CTFTPServer::ProcessRecvData() 接收数据大小为:”<<nDataLen<<std::endl;

size_t nWrited = fwrite(dataBuf,1,nDataLen,m_fileRecv);

//m_logFile<<”CTFTPServer::ProcessRecvData() 写入文件大小为:”<<nWrited<<std::endl;
//发完了
if (nDataLen < TFTP_DATA_PKT_SIZE)
{
fclose(m_fileRecv);
m_logFile<<”CTFTPServer::ProcessRecvData() 文件接收完毕,关闭文件……,接收大小为:”<<m_nTotalBytes<<std::endl;
m_logFile<<”CTFTPServer::ProcessRecvData() 文件接收完毕,关闭文件……,接收数据块大小为:”<<m_nPackNum<<std::endl;
}
m_packetRecv.Clear();
m_packetSend.MakeACK(dwDataNum);
//发送数据
int nLenSended = ::sendto(m_sockSvr,(char)m_packetSend.GetData(),m_packetSend.GetSize(),
0,(SOCKADDR
)&m_clientAddr,sizeof(m_clientAddr));
m_packetSend.Clear();
break;
}
case TFTP_OPCODE_ACK:
{
ATLTRACE(“确认包/n”);
break;
}
case TFTP_OPCODE_ERROR:
{
ATLTRACE(“/n”);
break;
}
default:
{
ATLTRACE(“/n”);
break;
}
}
}

只在本机测试了下,不知道在internet上面是啥效果。