ページへ戻る
印刷
技術系備忘録/C++/Boost/boost.asioでUDPホールパンチング
をテンプレートにして作成 ::
シンクリッジ
xpwiki
:技術系備忘録/C++/Boost/boost.asioでUDPホールパンチング をテンプレートにして作成
開始行:
NAT 越えの技術は skype が出てきたころには話題になってる気...
UDP ホールパンチングによる NAT 越えです。
で、boost::asio の udp の勉強も兼ねてサンプルアプリを作っ...
とはいえ、boost::asio の udp については難しいことは何もな...
さて、UDP ホールパンチングについて。
wikipedia みると NAT の実装にはいろいろあるみたいですが、...
Full cone NAT とか Address-Restricted cone NAT の環境は試...
で、Symmetric NAT って言われる環境はやっぱり無理そうでし...
docomo の LTE回線、純正のSPモード と MVNO (nifty の一番安...
スマホのテザリングで繋げた PC で試したので、Symmetric NAT...
で、以下は自分自身向けの備忘録もかねて、コードの抜粋と、...
- ヘッダ
#prettify{{
namespace RLib
{
class CUdpSocket
:private boost::noncopyable
{
class CSocket;
const boost::shared_ptr<CSocket> m_spSocket;
public:
CUdpSocket(asio::io_service &ioService);
~CUdpSocket();
void Close();
bool Bind(unsigned short nPort,boost::system::error_cod...
bool Connect(const std::string &sDomain,const std::stri...
bool SendTo(const boost::asio::ip::udp::endpoint &endPo...
const boost::shared_ptr<const vector<char>> &spData,
boost::system::error_code &ec=boost::system::error_c...
bool Send(const boost::shared_ptr<const vector<char>> &...
boost::system::error_code &ec=boost::system::error_...
// データ受信
// ・次の読み込み待ちにする場合には Receive(); をコール...
typedef boost::function<void (CUdpSocket &udpSocket,con...
const boost::asio::ip::udp::endpoint &endPointRem...
const boost::shared_ptr<const vector<char>> &spRe...
bool Receive(const FuncOnReceived &funcOnReceived,unsig...
public:
static CString GetTextAddress(const asio::ip::udp::sock...
{
return CRString::Format(_T("%s:%d"),CString(ep.addres...
}
};
}
class CUdpHolepunching
{
class CMain;
CMain &m_main;
public:
typedef boost::function<void (const CString &s)> FuncMes...
CUdpHolepunching(const FuncMessage &funcMessage);
~CUdpHolepunching();
void Server(unsigned short nPort);
void Client(const std::string &sDomain,const std::string...
};
}}
- .cpp
#prettify{{
class CUdpSocket::CSocket
{
public:
CUdpSocket *m_pUdpSocket;
ip::udp::socket m_socket;
public:
CSocket(CUdpSocket &udpSocket,io_service &ioService)
:m_pUdpSocket(&udpSocket)
,m_socket(ioService)
{
m_socket.open(ip::udp::v4());
}
};
CUdpSocket::CUdpSocket(asio::io_service &ioService)
:m_spSocket(new CSocket(*this,ioService))
{
}
CUdpSocket::~CUdpSocket()
{
m_spSocket->m_pUdpSocket = NULL; // 破棄されたマーク
}
void CUdpSocket::Close()
{
m_spSocket->m_socket.close();
}
bool CUdpSocket::Bind(unsigned short nPort,boost::system:...
{
return m_spSocket->m_socket.bind( ip::udp::endpoint(ip::...
}
bool CUdpSocket::Receive(const FuncOnReceived &funcOnRece...
{
struct F{
static void OnReceived(const boost::system::error_code ...
boost::shared_ptr<CSocket> spSocket,
boost::shared_ptr<ip::udp::endpoint> spEndpoint...
boost::shared_ptr<vector<char>> spBuffer,const ...
{
if( !spSocket->m_pUdpSocket ) return; // 破棄さ...
if( ec ){ // Error
if( ec == boost::asio::error::operation_aborted ) ret...
//return; エラーでもコールはする
}
spBuffer->resize(bytesReceived);
if( funcOnReceived ){
funcOnReceived( *spSocket->m_pUdpSocket, ec, *spEndpo...
}
}
};
boost::shared_ptr<ip::udp::endpoint> spEndpoint(new ip::...
boost::shared_ptr<vector<char>> spBuffer(new vector<char...
m_spSocket->m_socket.async_receive_from(
asio::buffer(*spBuffer),
*spEndpoint,
boost::bind(
&F::OnReceived,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
m_spSocket,spEndpoint,spBuffer,funcOnReceived)
);
return true;
}
bool CUdpSocket::SendTo(const boost::asio::ip::udp::endpo...
const boost::shared_ptr<const vector<char>> &spData...
{
if( !spData ){
BOOST_ASSERT(false);
return false;
}
const size_t size = m_spSocket->m_socket.send_to( asio::...
if( !ec ) return size == spData->size();
ATLTRACE( _T("\nCUdpSocket Error Send -> %s"), CString(e...
return false;
}
bool CUdpSocket::Send(const boost::shared_ptr<const vecto...
{
if( !spData ){
BOOST_ASSERT(false);
return false;
}
const size_t size = m_spSocket->m_socket.send( asio::buf...
if( !ec ) return size == spData->size();
ATLTRACE( _T("\nCUdpSocket Error Send -> %s"), CString(e...
return false;
}
////////////////////////////////////////////////
class CUdpHolepunching::CMain
:public CWindowImpl<CMain>
{
public:
DECLARE_WND_CLASS( _T("CMain") );
BEGIN_MSG_MAP(CMain)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
END_MSG_MAP()
public:
LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)
{
m_ioService.poll();
return 0;
}
public:
const CRUid m_id;
FuncMessage m_funcMessage;
io_service m_ioService;
CUdpSocket m_udpSocket;
auto_ptr<deadline_timer> m_apTimer;
#pragma pack(push,1)
struct CEndpoint
{
unsigned long m_nIpv4;
unsigned short m_nPort;
CEndpoint()
{}
CEndpoint(const ip::udp::endpoint &endPoint)
{
m_nIpv4 = endPoint.address().to_v4().to_ulong();
m_nPort = endPoint.port();
}
};
#pragma pack(pop)
std::map<CRUid,CEndpoint> m_mapMember;
void OnReceived(CUdpSocket &udpSocket,const boost::syste...
const boost::asio::ip::udp::endpoint &endPointRemote,
const boost::shared_ptr<const vector<char>> &spBuffer)
{
if( ec ){ // Error
m_funcMessage( CRString::Format(_T("%s : OnRecived Er...
CString(m_id.GetText().c_str()), CUdpSocket::Ge...
CString(ec.message().c_str())) );
}else{
m_funcMessage( CRString::Format(_T("%s : OnRecived [%...
CUdpSocket::GetTextAddress(endPointRemote) ) );
// メンツリスト更新
vector<char> vBuffer(*spBuffer);
if( vBuffer.size() >= sizeof(CRUid) ){ // このソケッ...
CRUid id;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(i...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
m_mapMember[id] = CEndpoint(endPointRemote);
}else BOOST_ASSERT(false);
// 送り主からのメンツリストを自身のメンツリストにマージ
while( vBuffer.size() >= sizeof(CRUid)+sizeof(CEndpoi...
CRUid id;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(i...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
CEndpoint ep;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(e...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
if( id != m_id ){ // 自分自身は除く
m_mapMember[id] = ep;
}
}
}
udpSocket.Receive( boost::bind(&CMain::OnReceived,this...
}
public:
CMain(const FuncMessage &funcMessage)
:m_id(CRUid::RandomGenerator())
,m_funcMessage(funcMessage)
,m_udpSocket(m_ioService)
,m_apTimer(new deadline_timer(m_ioService))
{
Create(HWND_MESSAGE,CRect(0,0,0,0),_T(""),NULL);
::SetTimer( m_hWnd, 0, 1, NULL );
m_apTimer->expires_from_now( boost::posix_time::milli...
m_apTimer->async_wait( boost::bind( &CMain::OnDeadline...
}
~CMain()
{
m_apTimer.reset();
m_udpSocket.Close();
if( m_hWnd ) DestroyWindow();
}
void OnDeadlineTimer(const boost::system::error_code& er...
{
if( error || error==boost::asio::error::operation_abor...
if( m_apTimer.get() ){ // 終了していないなら次の...
m_apTimer->expires_at( m_apTimer->expires_at() + boos...
m_apTimer->async_wait( boost::bind( &CMain::OnDeadlin...
}
// メンツリスト送信
boost::shared_ptr<vector<char>> spData(new vector<char...
std::copy( reinterpret_cast<const char*>(&m_id), reint...
std::back_inserter(*spData) );
for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapM...
const CRUid &id = i->first;
std::copy( reinterpret_cast<const char*>(&id), reinte...
std::back_inserter(*spData) );
const CEndpoint &ep = i->second;
std::copy( reinterpret_cast<const char*>(&ep), reinte...
std::back_inserter(*spData) );
}
CString sSendTo;
for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapM...
const CEndpoint &ep = i->second;
ip::address addr(boost::asio::ip::address_v4(ep.m_nIp...
ip::udp::endpoint endpoint( addr, ep.m_nPort );
m_udpSocket.SendTo( endpoint, spData );
sSendTo.Format(_T("%s [%s]"),CString(sSendTo),CUdpSoc...
}
if( !sSendTo.IsEmpty() ){
m_funcMessage( CRString::Format(_T("%s : SendTo %s"),...
}
}
void Server(unsigned short nPort)
{
boost::system::error_code ec;
if( !m_udpSocket.Bind(nPort,ec) ){
m_funcMessage( CRString::Format(_T("%s : Bind Error [...
CString(ec.message().c_str()) ) );
return;
}
m_udpSocket.Receive( boost::bind(&CMain::OnReceived,th...
}
void Client(const std::string &sDomain,const std::string...
{
boost::shared_ptr<vector<char>> spData(new vector<char...
std::copy( reinterpret_cast<const char*>(&m_id),
reinterpret_cast<const char*>(&m_id)+sizeof(m_id)...
ip::udp::resolver resolver(m_ioService);
ip::udp::resolver::query query(ip::udp::v4(), sDomain,...
boost::system::error_code ec;
ip::udp::resolver::iterator iterator = resolver.resolv...
if( ec ){
m_funcMessage( CRString::Format(_T("%s : resolve Erro...
CString(m_id.GetText().c_str()), CString(ec....
return;
}
m_udpSocket.SendTo( *iterator,spData );
m_udpSocket.Receive( boost::bind(&CMain::OnReceived,th...
}
};
CUdpHolepunching::CUdpHolepunching(const FuncMessage &fun...
:m_main(*new CMain(funcMessage))
{
}
CUdpHolepunching::~CUdpHolepunching()
{
delete &m_main;
}
void CUdpHolepunching::Server(unsigned short nPort)
{
m_main.m_ioService.post( boost::bind( &CMain::Server, &m...
}
void CUdpHolepunching::Client(const std::string &sDomain,...
{
m_main.m_ioService.post( boost::bind( &CMain::Client, &m...
}
}}
UDP ホールパンチングの仕組み上、穴をあけずとも UDP が通る...
STUN サーバーと言われるもの。
サンプルアプリでは「サーバー起動」ってすると、UDP ポート...
(とはいえ、標準化されてるプロトコルを実装してるわけでは...
各端末(クライアントPC)は、このサーバーに接続することで、...
あとは、各端末同士がそのエンドポイントに対して送信し合う...
サンプルアプリでは、サーバーもクライアントも、すべての相...
というわけで、NAT 越えの P2P 通信自体は出来ることがわかっ...
Symmetric NAT 内の端末に対しては、サーバーなりが橋渡しを...
UDP すら通さないような環境向けに TCP で橋渡しするようなサ...
UDP を使うので、TCP 並の信頼性とか順序の安定性が必要なら...
同じ NAT 内の端末同士は、ローカルIPで通信させるなり対策し...
この程度は世の P2P アプリ(skypeとか)は当然のように実現...
これらも楽しそうなので作ってみたいですが、仕事かなにかで...
上記のソース一式はこちらからお願いします。例によって wind...
&ref(TestUdpHolepunching.zip);
なにか間違いとかご意見とかればぜひお願いします。
ありがとうございました。
終了行:
NAT 越えの技術は skype が出てきたころには話題になってる気...
UDP ホールパンチングによる NAT 越えです。
で、boost::asio の udp の勉強も兼ねてサンプルアプリを作っ...
とはいえ、boost::asio の udp については難しいことは何もな...
さて、UDP ホールパンチングについて。
wikipedia みると NAT の実装にはいろいろあるみたいですが、...
Full cone NAT とか Address-Restricted cone NAT の環境は試...
で、Symmetric NAT って言われる環境はやっぱり無理そうでし...
docomo の LTE回線、純正のSPモード と MVNO (nifty の一番安...
スマホのテザリングで繋げた PC で試したので、Symmetric NAT...
で、以下は自分自身向けの備忘録もかねて、コードの抜粋と、...
- ヘッダ
#prettify{{
namespace RLib
{
class CUdpSocket
:private boost::noncopyable
{
class CSocket;
const boost::shared_ptr<CSocket> m_spSocket;
public:
CUdpSocket(asio::io_service &ioService);
~CUdpSocket();
void Close();
bool Bind(unsigned short nPort,boost::system::error_cod...
bool Connect(const std::string &sDomain,const std::stri...
bool SendTo(const boost::asio::ip::udp::endpoint &endPo...
const boost::shared_ptr<const vector<char>> &spData,
boost::system::error_code &ec=boost::system::error_c...
bool Send(const boost::shared_ptr<const vector<char>> &...
boost::system::error_code &ec=boost::system::error_...
// データ受信
// ・次の読み込み待ちにする場合には Receive(); をコール...
typedef boost::function<void (CUdpSocket &udpSocket,con...
const boost::asio::ip::udp::endpoint &endPointRem...
const boost::shared_ptr<const vector<char>> &spRe...
bool Receive(const FuncOnReceived &funcOnReceived,unsig...
public:
static CString GetTextAddress(const asio::ip::udp::sock...
{
return CRString::Format(_T("%s:%d"),CString(ep.addres...
}
};
}
class CUdpHolepunching
{
class CMain;
CMain &m_main;
public:
typedef boost::function<void (const CString &s)> FuncMes...
CUdpHolepunching(const FuncMessage &funcMessage);
~CUdpHolepunching();
void Server(unsigned short nPort);
void Client(const std::string &sDomain,const std::string...
};
}}
- .cpp
#prettify{{
class CUdpSocket::CSocket
{
public:
CUdpSocket *m_pUdpSocket;
ip::udp::socket m_socket;
public:
CSocket(CUdpSocket &udpSocket,io_service &ioService)
:m_pUdpSocket(&udpSocket)
,m_socket(ioService)
{
m_socket.open(ip::udp::v4());
}
};
CUdpSocket::CUdpSocket(asio::io_service &ioService)
:m_spSocket(new CSocket(*this,ioService))
{
}
CUdpSocket::~CUdpSocket()
{
m_spSocket->m_pUdpSocket = NULL; // 破棄されたマーク
}
void CUdpSocket::Close()
{
m_spSocket->m_socket.close();
}
bool CUdpSocket::Bind(unsigned short nPort,boost::system:...
{
return m_spSocket->m_socket.bind( ip::udp::endpoint(ip::...
}
bool CUdpSocket::Receive(const FuncOnReceived &funcOnRece...
{
struct F{
static void OnReceived(const boost::system::error_code ...
boost::shared_ptr<CSocket> spSocket,
boost::shared_ptr<ip::udp::endpoint> spEndpoint...
boost::shared_ptr<vector<char>> spBuffer,const ...
{
if( !spSocket->m_pUdpSocket ) return; // 破棄さ...
if( ec ){ // Error
if( ec == boost::asio::error::operation_aborted ) ret...
//return; エラーでもコールはする
}
spBuffer->resize(bytesReceived);
if( funcOnReceived ){
funcOnReceived( *spSocket->m_pUdpSocket, ec, *spEndpo...
}
}
};
boost::shared_ptr<ip::udp::endpoint> spEndpoint(new ip::...
boost::shared_ptr<vector<char>> spBuffer(new vector<char...
m_spSocket->m_socket.async_receive_from(
asio::buffer(*spBuffer),
*spEndpoint,
boost::bind(
&F::OnReceived,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
m_spSocket,spEndpoint,spBuffer,funcOnReceived)
);
return true;
}
bool CUdpSocket::SendTo(const boost::asio::ip::udp::endpo...
const boost::shared_ptr<const vector<char>> &spData...
{
if( !spData ){
BOOST_ASSERT(false);
return false;
}
const size_t size = m_spSocket->m_socket.send_to( asio::...
if( !ec ) return size == spData->size();
ATLTRACE( _T("\nCUdpSocket Error Send -> %s"), CString(e...
return false;
}
bool CUdpSocket::Send(const boost::shared_ptr<const vecto...
{
if( !spData ){
BOOST_ASSERT(false);
return false;
}
const size_t size = m_spSocket->m_socket.send( asio::buf...
if( !ec ) return size == spData->size();
ATLTRACE( _T("\nCUdpSocket Error Send -> %s"), CString(e...
return false;
}
////////////////////////////////////////////////
class CUdpHolepunching::CMain
:public CWindowImpl<CMain>
{
public:
DECLARE_WND_CLASS( _T("CMain") );
BEGIN_MSG_MAP(CMain)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
END_MSG_MAP()
public:
LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)
{
m_ioService.poll();
return 0;
}
public:
const CRUid m_id;
FuncMessage m_funcMessage;
io_service m_ioService;
CUdpSocket m_udpSocket;
auto_ptr<deadline_timer> m_apTimer;
#pragma pack(push,1)
struct CEndpoint
{
unsigned long m_nIpv4;
unsigned short m_nPort;
CEndpoint()
{}
CEndpoint(const ip::udp::endpoint &endPoint)
{
m_nIpv4 = endPoint.address().to_v4().to_ulong();
m_nPort = endPoint.port();
}
};
#pragma pack(pop)
std::map<CRUid,CEndpoint> m_mapMember;
void OnReceived(CUdpSocket &udpSocket,const boost::syste...
const boost::asio::ip::udp::endpoint &endPointRemote,
const boost::shared_ptr<const vector<char>> &spBuffer)
{
if( ec ){ // Error
m_funcMessage( CRString::Format(_T("%s : OnRecived Er...
CString(m_id.GetText().c_str()), CUdpSocket::Ge...
CString(ec.message().c_str())) );
}else{
m_funcMessage( CRString::Format(_T("%s : OnRecived [%...
CUdpSocket::GetTextAddress(endPointRemote) ) );
// メンツリスト更新
vector<char> vBuffer(*spBuffer);
if( vBuffer.size() >= sizeof(CRUid) ){ // このソケッ...
CRUid id;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(i...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
m_mapMember[id] = CEndpoint(endPointRemote);
}else BOOST_ASSERT(false);
// 送り主からのメンツリストを自身のメンツリストにマージ
while( vBuffer.size() >= sizeof(CRUid)+sizeof(CEndpoi...
CRUid id;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(i...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
CEndpoint ep;
std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(e...
stdext::checked_array_iterator<char*>(reinterpr...
vBuffer.erase( vBuffer.begin(), vBuffer.begin()+size...
if( id != m_id ){ // 自分自身は除く
m_mapMember[id] = ep;
}
}
}
udpSocket.Receive( boost::bind(&CMain::OnReceived,this...
}
public:
CMain(const FuncMessage &funcMessage)
:m_id(CRUid::RandomGenerator())
,m_funcMessage(funcMessage)
,m_udpSocket(m_ioService)
,m_apTimer(new deadline_timer(m_ioService))
{
Create(HWND_MESSAGE,CRect(0,0,0,0),_T(""),NULL);
::SetTimer( m_hWnd, 0, 1, NULL );
m_apTimer->expires_from_now( boost::posix_time::milli...
m_apTimer->async_wait( boost::bind( &CMain::OnDeadline...
}
~CMain()
{
m_apTimer.reset();
m_udpSocket.Close();
if( m_hWnd ) DestroyWindow();
}
void OnDeadlineTimer(const boost::system::error_code& er...
{
if( error || error==boost::asio::error::operation_abor...
if( m_apTimer.get() ){ // 終了していないなら次の...
m_apTimer->expires_at( m_apTimer->expires_at() + boos...
m_apTimer->async_wait( boost::bind( &CMain::OnDeadlin...
}
// メンツリスト送信
boost::shared_ptr<vector<char>> spData(new vector<char...
std::copy( reinterpret_cast<const char*>(&m_id), reint...
std::back_inserter(*spData) );
for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapM...
const CRUid &id = i->first;
std::copy( reinterpret_cast<const char*>(&id), reinte...
std::back_inserter(*spData) );
const CEndpoint &ep = i->second;
std::copy( reinterpret_cast<const char*>(&ep), reinte...
std::back_inserter(*spData) );
}
CString sSendTo;
for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapM...
const CEndpoint &ep = i->second;
ip::address addr(boost::asio::ip::address_v4(ep.m_nIp...
ip::udp::endpoint endpoint( addr, ep.m_nPort );
m_udpSocket.SendTo( endpoint, spData );
sSendTo.Format(_T("%s [%s]"),CString(sSendTo),CUdpSoc...
}
if( !sSendTo.IsEmpty() ){
m_funcMessage( CRString::Format(_T("%s : SendTo %s"),...
}
}
void Server(unsigned short nPort)
{
boost::system::error_code ec;
if( !m_udpSocket.Bind(nPort,ec) ){
m_funcMessage( CRString::Format(_T("%s : Bind Error [...
CString(ec.message().c_str()) ) );
return;
}
m_udpSocket.Receive( boost::bind(&CMain::OnReceived,th...
}
void Client(const std::string &sDomain,const std::string...
{
boost::shared_ptr<vector<char>> spData(new vector<char...
std::copy( reinterpret_cast<const char*>(&m_id),
reinterpret_cast<const char*>(&m_id)+sizeof(m_id)...
ip::udp::resolver resolver(m_ioService);
ip::udp::resolver::query query(ip::udp::v4(), sDomain,...
boost::system::error_code ec;
ip::udp::resolver::iterator iterator = resolver.resolv...
if( ec ){
m_funcMessage( CRString::Format(_T("%s : resolve Erro...
CString(m_id.GetText().c_str()), CString(ec....
return;
}
m_udpSocket.SendTo( *iterator,spData );
m_udpSocket.Receive( boost::bind(&CMain::OnReceived,th...
}
};
CUdpHolepunching::CUdpHolepunching(const FuncMessage &fun...
:m_main(*new CMain(funcMessage))
{
}
CUdpHolepunching::~CUdpHolepunching()
{
delete &m_main;
}
void CUdpHolepunching::Server(unsigned short nPort)
{
m_main.m_ioService.post( boost::bind( &CMain::Server, &m...
}
void CUdpHolepunching::Client(const std::string &sDomain,...
{
m_main.m_ioService.post( boost::bind( &CMain::Client, &m...
}
}}
UDP ホールパンチングの仕組み上、穴をあけずとも UDP が通る...
STUN サーバーと言われるもの。
サンプルアプリでは「サーバー起動」ってすると、UDP ポート...
(とはいえ、標準化されてるプロトコルを実装してるわけでは...
各端末(クライアントPC)は、このサーバーに接続することで、...
あとは、各端末同士がそのエンドポイントに対して送信し合う...
サンプルアプリでは、サーバーもクライアントも、すべての相...
というわけで、NAT 越えの P2P 通信自体は出来ることがわかっ...
Symmetric NAT 内の端末に対しては、サーバーなりが橋渡しを...
UDP すら通さないような環境向けに TCP で橋渡しするようなサ...
UDP を使うので、TCP 並の信頼性とか順序の安定性が必要なら...
同じ NAT 内の端末同士は、ローカルIPで通信させるなり対策し...
この程度は世の P2P アプリ(skypeとか)は当然のように実現...
これらも楽しそうなので作ってみたいですが、仕事かなにかで...
上記のソース一式はこちらからお願いします。例によって wind...
&ref(TestUdpHolepunching.zip);
なにか間違いとかご意見とかればぜひお願いします。
ありがとうございました。
ページ名: