XMPP-gloox实现的文件传世协议分享
1.支持IBB传输
2.支持S5B传输
可以简单的改动即可传输二进制,在只做的过程中遇到一些很难理解的问题现在分享出来:
1.发送端使用s5b的时候,关于这句的理解new SOCKS5BytestreamServer(j->logInstance(), 25254, "0.0.0.0");如果按照例子提供的代码你说创建失败的,因为不传ip默认的af_inet就是AF_INET6,会返回10047的socket错误。
2.sock5代理服务器的使用流程是什么?
a。发送端首先需要监听本地端口,然后将本地监听的地址通过addStreamHost加入到sock5服务器候选(局域网或者本机传输就可以了,因为他会自动选中此端口作文文件传输端口),如果需要广域网传输文件那么你需要添加sock5服务器的ip,这里默认使用openfire的文件代理插件,此插件使用的端口是7777,我们将其添加到sock5候选即可
b。接收端相对简单只需要将代理服务器的地址加入候选即可,这里使用openfire的7777端口。
实现以上基本就可以成功了,以下给几张截图:
接收端:
发送端:
发送过程:
接收过程:
ok已经成功运行了,下面放出源码:
头文件
#ifndef __XmppFileSend_H
#define __XmppFileSend_H
#include
#include
#include
#include "client.h"
#include "connectionlistener.h"
#include "siprofilefthandler.h"
#include "bytestreamdatahandler.h"
#include "siprofileft.h"
#include "disco.h"
#pragma warning(disable:4996)
using stdstr = std::string;
using stdcstr = const std::string;
using ifstream = std::ifstream;
using namespace gloox;
class XmppFileSend : public LogHandler, ConnectionListener, SIProfileFTHandler, BytestreamDataHandler
{
public:
XmppFileSend( stdcstr & filename , XmppTransFileObserver* observer = nullptr, int s5port = 7777);
virtual ~XmppFileSend();
bool SendFile();
bool Start(const std::string& user ,
const std::string& toUser,
const std::string& pwd,
const std::string& serverip,
WORD port,
const std::string& domain);
bool Start(Client* client);
void Stop();
void SetToUser(const std::string& toUser);
public:
ConnectionError OnStreamRecv(int timeout);
public:
virtual void onConnect();
virtual void onDisconnect(ConnectionError e);
virtual bool onTLSConnect(const CertInfo& info);
public:
bool DoConnect();
protected:
bool _initFileInfo();
bool _readFile(stdstr& str);
void _closeFile();
protected:
bool _createStream();
protected:
virtual void handleLog(LogLevel level, LogArea area, const std::string& message);
virtual void handleFTRequest(const JID& from, const JID& /*to*/, const std::string& sid,
const std::string& name, long size, const std::string& hash,
const std::string& date, const std::string& mimetype,
const std::string& desc, int /*stypes*/);
// virtual void handleFTRequestResult( const JID& /*from*/, const std::string& /*sid*/ )
// {
// }
virtual void handleFTRequestError(const IQ& /*iq*/, const std::string& /*sid*/);
virtual void handleFTBytestream(Bytestream* bs);
virtual const std::string handleOOBRequestResult(const JID& /*from*/, const JID& /*to*/, const std::string& /*sid*/);;
virtual void handleBytestreamData(Bytestream* /*bs*/, const std::string& data);
virtual void handleBytestreamError(Bytestream* /*bs*/, const IQ& /*iq*/);
virtual void handleBytestreamOpen(Bytestream* /*bs*/);
virtual void handleBytestreamClose(Bytestream* /*bs*/);
private:
Client* j;
SIProfileFT* f;
Bytestream* m_bs;
SOCKS5BytestreamServer* m_server;
JID m_to;
std::string m_strtoName;
std::string m_file;
bool m_quit;
int m_size;
bool m_del;
int m_s5b_port;
stdstr m_filename;
size_t m_filesize;
ifstream m_ifs;
size_t m_curReadCnt;
XmppTransFileObserver* m_observer;
};
#endif //__XmppFileSend_H
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
#include "pch.h"
#include "XmppFileSend.h"
#include "base64.h"
#include "socks5bytestreamserver.h"
#define F_PRINT(fmt,...) printf("\n[%s:%d]\t" fmt , __FUNCTION__,__LINE__ , ##__VA_ARGS__)
XmppFileSend::XmppFileSend(stdcstr& filename, XmppTransFileObserver* observer, int s5port) :
m_bs(0), m_quit(false), m_del(true),
m_filename(filename), m_filesize(0), m_curReadCnt(0),
m_observer(observer), m_server(nullptr), f(nullptr), j(nullptr), m_size(0),
m_s5b_port(s5port)
{
}
XmppFileSend::~XmppFileSend()
{
Stop();
}
bool XmppFileSend::SendFile()
{
bool bSend = true;
if (m_bs && m_bs->isOpen())
{
stdstr sData;
if (bSend = _readFile( sData )) {
bSend = m_bs->send( sData );
}
}
return bSend;
}
bool XmppFileSend::Start(const std::string& user,
const std::string& toUser,
const std::string& pwd,
const std::string& serverip,
WORD port,
const std::string& domain)
{
if (!_initFileInfo())
{
return false;
}
m_del = true;
m_to = JID(toUser + "@" + domain + "/trans-file");
JID jid(user + "@" + domain + "/trans-file");
j = new Client(jid, pwd);
j->setUsername(user);
j->setPassword(pwd);
j->setTls(TLSDisabled);
j->setServer(serverip);
j->setPort(port);
j->disableRoster();
j->registerConnectionListener(this);
j->disco()->setVersion("trans-filesend", GLOOX_VERSION, "Linux");
j->disco()->setIdentity("client", "bot");
return DoConnect();
}
bool XmppFileSend::Start(Client* client)
{
if (!_initFileInfo())
{
return false;
}
m_del = false;
j = client;
if (m_strtoName.find("@") == m_strtoName.npos)
{
std::string sUser = m_strtoName + "@" + client->jid().server() +
(client->resource().size() ? "/" + client->resource() : "");
m_to.setJID(sUser);
}
else
{
m_to.setJID(m_strtoName);
}
return true;
}
void XmppFileSend::Stop()
{
m_quit = true;
if (j) j->disconnect();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (f)
{
if (m_bs)
{
m_bs->close();
f->dispose(m_bs);
}
delete f;
f = nullptr;
}
if (m_server)
{
delete m_server;
m_server = nullptr;
}
if (j)
{
if(m_del)delete j;
j = nullptr;
}
}
void XmppFileSend::SetToUser(const std::string& peer)
{
m_strtoName = peer;
}
gloox::ConnectionError XmppFileSend::OnStreamRecv(int timeout)
{
if (m_server)
{
ConnectionError se = m_server->recv(timeout);
if (se != ConnNoError)
{
return se;
}
}
if (m_bs)
{
m_bs->recv(timeout);
}
else if (m_bs)
m_bs->close();
return ConnNoError;
}
void XmppFileSend::onConnect()
{
//using namespace std::chrono;
F_PRINT("connected!!!\n");
std::string sFileName = m_filename;
char* pBackSlash = strrchr(&m_filename[0], '\\');
if (pBackSlash)
{
sFileName = pBackSlash + 1;
}
if (_createStream())
{
f->requestFT(m_to, Base64::encode64(sFileName), (int)m_filesize);
}
}
void XmppFileSend::onDisconnect(ConnectionError e)
{
F_PRINT("ft_send: disconnected: %d\n", e);
if (e == ConnAuthenticationFailed)
F_PRINT("auth failed. reason: %d\n", j->authError());
}
bool XmppFileSend::onTLSConnect(const CertInfo& info)
{
time_t from(info.date_from);
time_t to(info.date_to);
F_PRINT("status: %d\nissuer: %s\npeer: %s\nprotocol: %s\nmac: %s\ncipher: %s\ncompression: %s\n",
info.status, info.issuer.c_str(), info.server.c_str(),
info.protocol.c_str(), info.mac.c_str(), info.cipher.c_str(),
info.compression.c_str());
F_PRINT("from: %s", ctime(&from));
F_PRINT("to: %s", ctime(&to));
return true;
}
bool XmppFileSend::DoConnect()
{
bool b = j->connect(false);
m_quit = !b;
if (b && m_del)
{
ConnectionError ce = ConnNoError;
while (ce == ConnNoError)
{
if (m_quit) {
j->disconnect();
break;
}
ce = j->recv(1);
if (!SendFile()) {
m_quit = true;
}
if (ce == ConnNoError)
{
ce = OnStreamRecv(1);
}
}
F_PRINT("ce: %d\n", ce);
}
return b;
}
bool XmppFileSend::_initFileInfo()
{
m_curReadCnt = 0;
m_ifs.open(m_filename,std::ios::binary|std::ios::in);
if (m_ifs.is_open())
{
m_ifs.seekg(0, std::ios::end );
m_filesize = m_ifs.tellg();
m_ifs.seekg( 0, std::ios::beg );
return m_filesize>0;
}
return false;
}
bool XmppFileSend::_readFile( stdstr& str )
{
if (m_curReadCnt < m_filesize && m_ifs.good() && !m_ifs.eof())
{
char szBuf[1024];
m_ifs.read( szBuf, 1024 );
int nRealCnt = (int)m_ifs.gcount();
if (nRealCnt>0)
{
str.assign( szBuf, nRealCnt );
m_curReadCnt += nRealCnt;
return true;
}
}
return false;
}
void XmppFileSend::_closeFile()
{
if (m_ifs.is_open())
{
m_ifs.close();
}
}
bool XmppFileSend::_createStream()
{
f = new SIProfileFT(j, this);
if (m_s5b_port > 0)
{
//sock5
m_server = new SOCKS5BytestreamServer(j->logInstance(), 25254, "0.0.0.0");
ConnectionError ce = m_server->listen();
if (ce != ConnNoError)
{
F_PRINT("sock5 listen fail");
return false;
}
F_PRINT("sock5 listening");
//注册sock5
f->registerSOCKS5BytestreamServer(m_server);
f->addStreamHost(j->jid(), "127.0.0.1", 25254);
f->addStreamHost(JID("proxy." + j->jid().server()), j->server(), m_s5b_port);
F_PRINT("SOCK5 Proxy File Transfer[proxy port=%d]",m_s5b_port);
}
else
{
F_PRINT("IBB File Transfer");
}
return true;
}
void XmppFileSend::handleLog(LogLevel level, LogArea area, const std::string& message)
{
F_PRINT("\n: level: %d, area: %d, %s\n", level, area, message.c_str());
}
void XmppFileSend::handleFTRequest(const JID& from, const JID& /*to*/, const std::string& sid, const std::string& name, long size, const std::string& hash, const std::string& date, const std::string& mimetype, const std::string& desc, int /*stypes*/)
{
F_PRINT("received ft request from %s: %s (%ld bytes, sid: %s). hash: %s, date: %s, mime-type: %s\n"
"desc: %s\n",
from.full().c_str(), name.c_str(), size, sid.c_str(), hash.c_str(), date.c_str(),
mimetype.c_str(), desc.c_str());
f->acceptFT(from, sid, SIProfileFT::FTTypeS5B);
}
void XmppFileSend::handleFTRequestError(const IQ& iq, const std::string& sid)
{
F_PRINT("ft request error[%s]\n", __FUNCTION__);
m_quit = true;
}
void XmppFileSend::handleFTBytestream(Bytestream* bs)
{
F_PRINT("received bytestream of type: %s", bs->type() == Bytestream::S5B ? "s5b" : "ibb");
m_bs = bs;
m_bs->registerBytestreamDataHandler(this);
if (m_bs->connect())
{
if (bs->type() == Bytestream::S5B)
F_PRINT("ok! s5b connected to streamhost\n");
else
F_PRINT("ok! ibb sent request to remote entity\n");
if (m_observer)
{
m_observer->OnBegin(m_filename, (int)m_filesize, this);
}
}
}
const std::string XmppFileSend::handleOOBRequestResult(const JID& /*from*/, const JID& /*to*/, const std::string& /*sid*/)
{
return std::string();
}
void XmppFileSend::handleBytestreamData(Bytestream* /*bs*/, const std::string& data)
{
//F_PRINT("received %zd bytes of data:\n%s\n", data.length(), data.c_str());
if (m_observer)
{
m_observer->OnProgress(m_filename, (int)m_curReadCnt, (int)m_filesize, this);
}
}
void XmppFileSend::handleBytestreamError(Bytestream* /*bs*/, const IQ& /*iq*/)
{
F_PRINT("bytestream error\n");
}
void XmppFileSend::handleBytestreamOpen(Bytestream* /*bs*/)
{
F_PRINT("bytestream opened\n");
}
void XmppFileSend::handleBytestreamClose(Bytestream* /*bs*/)
{
F_PRINT("bytestream closed\n");
m_quit = true;
if (m_observer)
{
m_observer->OnEnd(m_filename, (int)m_curReadCnt, (int)m_filesize, this);
}
}
#ifndef __XmppFileRecv_H
#define __XmppFileRecv_H
#include
#include
调用例子
void TestFT()
{
std::cout << "(1) Sender\n"
<< "(2) Reciever\n";
int mode = 2;
std::cin >> mode;
if (mode != 1)
{
mode = 2;
}
std::cout << "s5 proxy port(0 use ibb):\n";
int s5_port = 0;
std::cin >> s5_port;
//-------------------------------------------------------------------------------
//文件传输测试
XmppTransFileObserverImp filetransob;
if (mode == 2)//接收端
{
static XmppFileRecv fileRecv((XmppTransFileObserver*)&filetransob, s5_port);
fileRecv.SetSaveDir("d:\\111");
fileRecv.Start(userA, pwd, ip, port, domain);
}
//-------------------------------------------------------------------------------
std::string sData = "adasdsadsaasdsadascacascascascascsa\r\n";
while (std::getline(std::cin, sData))
{
if (sData == "quit")
{
break;
}
//client1.SendMessage(userB, sData);
//client1.SendCmd(userB, "ls");
client2.SendCmd(userA,"return ls con");
//se.SendData(sData.data(), (int)sData.length());
if (mode == 1)
{
if (_access(sData.c_str(), 0) == 0)
{
XmppFileSend fileSend(sData, (XmppTransFileObserver*)&filetransob, s5_port);
fileSend.Start(userB, userA, pwd, ip, port, domain);
}
}
}
}
代理接口
/**
* @brief 发送接收文件回调
*/
class XmppTransFileObserver
{
public:
XmppTransFileObserver() = default;
virtual ~XmppTransFileObserver() = default;
public:
virtual void OnBegin(const std::string& filename, int len, void* pClient) = 0;
virtual void OnProgress(const std::string& filename, int cur, int total, void* pClient) = 0;
virtual void OnEnd(const std::string& filename, int cur, int total, void* pClient) = 0;
};