18,356
社区成员
发帖
与我相关
我的任务
分享
//------------------------------ SocketTcp 类------------------------------//
class SocketTCP
{
public :
SocketTCP();
void SetBlocking(bool Blocking);
Socket::Status Connect(unsigned short Port, const IPAddress& HostAddress, float Timeout = 0.f);
bool Listen(unsigned short Port);
Socket::Status Accept(SocketTCP& Connected, IPAddress* Address = NULL);
Socket::Status Send(const char* Data, std::size_t Size);
Socket::Status Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived);
bool Close(); // 关闭套接字
bool IsValid() const; // 验证套接字是否有效
bool operator ==(const SocketTCP& Other) const;
bool operator !=(const SocketTCP& Other) const;
bool operator <(const SocketTCP& Other) const;
private :
friend class Selector<SocketTCP>;
SocketTCP(SocketHelper::SocketType Descriptor);
void Create(SocketHelper::SocketType Descriptor = 0);
// 成员变量
SocketHelper::SocketType mySocket; ///< Socket descriptor
Uint32 myPendingHeader; ///< Data of the current pending packet header, if any
Uint32 myPendingHeaderSize; ///< Size of the current pending packet header, if any
std::vector<char> myPendingPacket; ///< Data of the current pending packet, if any
Int32 myPendingPacketSize; ///< Size of the current pending packet, if any
bool myIsBlocking; ///< Is the socket blocking or non-blocking ?
};
//---------------------------------Ftp 类--------------------------------------//
class SFML_API Ftp : NonCopyable
{
public :
enum TransferMode
{
Binary, ///< Binary mode (file is transfered as a sequence of bytes)
Ascii, ///< Text mode using ASCII encoding
Ebcdic ///< Text mode using EBCDIC encoding
};
// ---------------------------报文类----------------------------------------//
class Response
{
public :
enum Status
{
// 枚举的报文常量,就没列出来了
};
Response(Status Code = InvalidResponse, const std::string& Message = "");
bool IsOk() const;
Status GetStatus() const;
const std::string& GetMessage() const;
private :
Status myStatus; ///< Status code returned from the server
std::string myMessage; ///< Last message received from the server
};
// 其他成员函数就没列举出来了,不然太长了
Ftp(bool translateMode = false);
~Ftp();
Response Download(const std::string& DistantFile, const std::string& DestPath, TRANSLATEINFO* pTransInfo = NULL, TransferMode Mode = Binary);
Response Upload(const std::string& LocalFile, const std::string& DestPath, TransferMode Mode = Binary);
private:
Response SendCommand(const std::string& Command, const std::string& Parameter = "");
Response GetResponse();
class DataChannel;
friend class DataChannel;
SocketTCP myCommandSocket; ///< Socket holding the control connection with the server>
};
//----------------------------------DataChannel 类----------------------------------//
class Ftp::DataChannel : NonCopyable
{
public :
DataChannel(Ftp& Owner);
~DataChannel();
Ftp::Response Open(Ftp::TransferMode Mode);
void Send(const std::vector<char>& Data);
void Receive(std::vector<char>& Data);
bool RecevieFile(std::string &filename, DWORD filesize, TRANSLATEINFO* pTransInfo = NULL);
private :
Ftp& myFtp; ///< Reference to the owner Ftp instance
SocketTCP myDataSocket; ///< Socket used for data transfers
SocketTCP FtpCommandSocket; ///< Ftp command socket>
CallBackShowSpeed cbShowSpeed; ///回调函数指针变量
};
// 文件下载函数
Ftp::Response Download(const std::string& DistantFile, const std::string& DestPath, TRANSLATEINFO* pTransInfo, TransferMode Mode)
{
// Open a data channel using the given transfer mode
DataChannel Data(*this); // 打开数据通道,接收数据《DataChannel是'数据类'》
Response Resp = Data.Open(Mode); /*Response 是'ftp报文类',记录命令通道返回的命令消息*/
if (Resp.IsOk()) // 命令通道返回的信息表示数据通道连接成功
{
// Get the remote file size
Ftp::FileInfoResponse ResSize = GetFileSize(DistantFile);
std::string remoteStr = ResSize.GetFileInfo();
DWORD dwRometeSize = atol(remoteStr.c_str());
// Tell the server to start the transfer
Resp = SendCommand("RETR", DistantFile);
if (Resp.IsOk())
{
// Extract the filename from the file path
std::string Filename = DistantFile;
std::string::size_type Pos = Filename.find_last_of("/\\");
if (Pos != std::string::npos)
Filename = Filename.substr(Pos + 1);
// Make sure the destination path ends with a slash
std::string Path = DestPath;
if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
Path += "/";
Path += Filename;
if (!Data.RecevieFile(Path, dwRometeSize, pTransInfo))
return Response(Response::InvalidFile); //error
Resp = GetResponse(); /*程序就卡在这一步了*/《获取完成下载后,命令通道返回的信息》
}
}
return Resp;
}
// 从命令通道获取报文方法
Ftp::Response Ftp::GetResponse()
{
for (;;)
{
char Buffer[1024];
std::size_t Length;
// 调试中,发现阻塞在Receive()这里了!!!(myCommandSocket是Ftp类的命令通道套接字类对象)
if (myCommandSocket.Receive(Buffer, sizeof(Buffer), Length) != sf::Socket::Done)
{
return Response(Response::ConnectionClosed);
}
// 消息处理...
}
}
// 命令通道接收文件函数
bool Ftp::DataChannel::RecevieFile(std::string &filename, DWORD filesize, TRANSLATEINFO* pTransInfo)
{
// Receive data
char Buffer[1024] = {0};
std::size_t Received = 0;
DWORD size = filesize;
float receivedSpeed = 0.0f;
// Create file in local computer
std::ofstream localfile(filename.c_str(), std::ios_base::binary);
if (!localfile)
return false;
while (myDataSocket.Receive(Buffer, sizeof(Buffer), Received) == sf::Socket::Done)
{
size -= Received;
localfile.write(Buffer, static_cast<int>(Received));
localfile.flush();
memset(Buffer, 0, sizeof(Buffer));
receivedSpeed = 100*((filesize-size)/static_cast<double>(filesize));
cbShowSpeed(filename, receivedSpeed, pTransInfo);
}
// Closed the localfile
localfile.close();
myDataSocket.Close();
return true;
}
// 套接字接收数据函数
Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
{
// First clear the size received
SizeReceived = 0;
// Check that socket is valid
if (!IsValid())
return Socket::Error;
// Check parameters
if (Data && MaxSize)
{
// Receive a chunk of bytes
int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
// Check the number of bytes received
if (Received > 0)
{
SizeReceived = static_cast<std::size_t>(Received);
return Socket::Done;
}
else if (Received == 0)
{
return Socket::Disconnected;
}
else
{
return SocketHelper::GetErrorStatus();
}
}
else
{
// Error...
std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}
//------------------------------ SocketTcp 类------------------------------//
class SocketTCP
{
public :
SocketTCP();
void SetBlocking(bool Blocking);
Socket::Status Connect(unsigned short Port, const IPAddress& HostAddress, float Timeout = 0.f);
bool Listen(unsigned short Port);
Socket::Status Accept(SocketTCP& Connected, IPAddress* Address = NULL);
Socket::Status Send(const char* Data, std::size_t Size);
Socket::Status Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived);
bool Close(); // 关闭套接字
bool IsValid() const; // 验证套接字是否有效
bool operator ==(const SocketTCP& Other) const;
bool operator !=(const SocketTCP& Other) const;
bool operator <(const SocketTCP& Other) const;
private :
friend class Selector<SocketTCP>;
SocketTCP(SocketHelper::SocketType Descriptor);
void Create(SocketHelper::SocketType Descriptor = 0);
// 成员变量
SocketHelper::SocketType mySocket; ///< Socket descriptor
Uint32 myPendingHeader; ///< Data of the current pending packet header, if any
Uint32 myPendingHeaderSize; ///< Size of the current pending packet header, if any
std::vector<char> myPendingPacket; ///< Data of the current pending packet, if any
Int32 myPendingPacketSize; ///< Size of the current pending packet, if any
bool myIsBlocking; ///< Is the socket blocking or non-blocking ?
};
//---------------------------------Ftp 类--------------------------------------//
class SFML_API Ftp : NonCopyable
{
public :
enum TransferMode
{
Binary, ///< Binary mode (file is transfered as a sequence of bytes)
Ascii, ///< Text mode using ASCII encoding
Ebcdic ///< Text mode using EBCDIC encoding
};
// ---------------------------报文类----------------------------------------//
class Response
{
public :
enum Status
{
// 枚举的报文常量,就没列出来了
};
Response(Status Code = InvalidResponse, const std::string& Message = "");
bool IsOk() const;
Status GetStatus() const;
const std::string& GetMessage() const;
private :
Status myStatus; ///< Status code returned from the server
std::string myMessage; ///< Last message received from the server
};
// 其他成员函数就没列举出来了,不然太长了
Ftp(bool translateMode = false);
~Ftp();
Response Download(const std::string& DistantFile, const std::string& DestPath,
TRANSLATEINFO* pTransInfo = NULL, TransferMode Mode = Binary);
Response Upload(const std::string& LocalFile, const std::string& DestPath,
TransferMode Mode = Binary);
private:
Response SendCommand(const std::string& Command, const std::string& Parameter = "");
Response GetResponse();
class DataChannel;
friend class DataChannel;
SocketTCP myCommandSocket; ///< Socket holding the control connection with the server>
};
//----------------------------------DataChannel 类----------------------------------//
class Ftp::DataChannel : NonCopyable
{
public :
DataChannel(Ftp& Owner);
~DataChannel();
Ftp::Response Open(Ftp::TransferMode Mode);
void Send(const std::vector<char>& Data);
void Receive(std::vector<char>& Data);
bool RecevieFile(std::string &filename, DWORD filesize, TRANSLATEINFO* pTransInfo = NULL);
private :
Ftp& myFtp; ///< Reference to the owner Ftp instance
SocketTCP myDataSocket; ///< Socket used for data transfers
SocketTCP FtpCommandSocket; ///< Ftp command socket>
CallBackShowSpeed cbShowSpeed; ///回调函数指针变量
};
// 文件下载函数
Ftp::Response Download(const std::string& DistantFile, const std::string& DestPath, TRANSLATEINFO* pTransInfo, TransferMode Mode)
{
// Open a data channel using the given transfer mode
DataChannel Data(*this); // 打开数据通道,接收数据《DataChannel是'数据类'》
Response Resp = Data.Open(Mode); /*Response 是'ftp报文类',记录命令通道返回的命令消息*/
if (Resp.IsOk()) // 命令通道返回的信息表示数据通道连接成功
{
// Get the remote file size
Ftp::FileInfoResponse ResSize = GetFileSize(DistantFile);
std::string remoteStr = ResSize.GetFileInfo();
DWORD dwRometeSize = atol(remoteStr.c_str());
// Tell the server to start the transfer
Resp = SendCommand("RETR", DistantFile);
if (Resp.IsOk())
{
// Extract the filename from the file path
std::string Filename = DistantFile;
std::string::size_type Pos = Filename.find_last_of("/\\");
if (Pos != std::string::npos)
Filename = Filename.substr(Pos + 1);
// Make sure the destination path ends with a slash
std::string Path = DestPath;
if (!Path.empty() && (Path[Path.size() - 1] != '\\')
&& (Path[Path.size() - 1] != '/'))
{
Path += "/";
}
Path += Filename;
if (!Data.RecevieFile(Path, dwRometeSize, pTransInfo))
return Response(Response::InvalidFile); //error
Resp = GetResponse(); /*程序就卡在这一步了*/《获取完成下载后,命令通道返回的信息》
}
}
return Resp;
}
// 从命令通道获取报文方法
Ftp::Response Ftp::GetResponse()
{
for (;;)
{
char Buffer[1024];
std::size_t Length;
// 调试中,发现阻塞在Receive()这里了!!!(myCommandSocket是Ftp类的命令通道套接字类对象)
if (myCommandSocket.Receive(Buffer, sizeof(Buffer), Length) != sf::Socket::Done)
{
return Response(Response::ConnectionClosed);
}
// 消息处理...
}
}
// 命令通道接收文件函数
bool Ftp::DataChannel::RecevieFile(std::string &filename, DWORD filesize, TRANSLATEINFO* pTransInfo)
{
// Receive data
char Buffer[1024] = {0};
std::size_t Received = 0;
DWORD size = filesize;
float receivedSpeed = 0.0f;
// Create file in local computer
std::ofstream localfile(filename.c_str(), std::ios_base::binary);
if (!localfile)
return false;
while (myDataSocket.Receive(Buffer, sizeof(Buffer), Received) == sf::Socket::Done)
{
size -= Received;
localfile.write(Buffer, static_cast<int>(Received));
localfile.flush();
memset(Buffer, 0, sizeof(Buffer));
receivedSpeed = 100*((filesize-size)/static_cast<double>(filesize));
cbShowSpeed(filename, receivedSpeed, pTransInfo);
}
// Closed the localfile
localfile.close();
myDataSocket.Close();
return true;
}
// 套接字接收数据函数
Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
{
// First clear the size received
SizeReceived = 0;
// Check that socket is valid
if (!IsValid())
return Socket::Error;
// Check parameters
if (Data && MaxSize)
{
// Receive a chunk of bytes
int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
// Check the number of bytes received
if (Received > 0)
{
SizeReceived = static_cast<std::size_t>(Received);
return Socket::Done;
}
else if (Received == 0)
{
return Socket::Disconnected;
}
else
{
return SocketHelper::GetErrorStatus();
}
}
else
{
// Error...
std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}