您的位置:首页 > 编程语言 > C语言/C++

P2P通讯的C++实现-UDP Hole Punching方法

2016-03-24 14:35 656 查看
Title: P2P通讯的C++实现-UDP Hole Punching方法

整理时间: 2016-03-24 by kagula

 

UDP Hole Punching方法,需要服务端。

假设有AB两个客户端和S一个服务器

Step 1 : AB发送UDP请求给SS知道了AB在公网的IP和端口。

Step 2: AS中取B在公网的IP和端口。

             BS中取A在公网的IP和端口。

Step 3: A通过B在公网的IP和端口向B发送UDP请求。

              B通过A在公网的IP和端口向A发送UDP请求。

Step Final:

              AB之间可以通过UDP直接互相发送信息。

目前看来这种方法最大的缺点, 还是需要服务器。

整理后的服务端文件

#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

using boost::asio::ip::udp;

class udp_server
{
public:
udp_server(boost::asio::io_service &io_service) :
_sock(io_service, udp::endpoint(udp::v4(), 2333))
{
session_receive();
}
void session_receive();
void handle_receive(const boost::system::error_code &ec, std::size_t len);
void session_send(udp::endpoint &ep, std::string write_message);
void handle_send(const boost::system::error_code &ec, std::size_t len);
private:
udp::socket _sock;
boost::array<char, 256> _recv_buffer;
std::string _write_message;
std::list<udp::endpoint> _endpoint_list;
udp::endpoint _remote_endpoint;//current endpoint
};

void udp_server::session_receive()
{
_sock.async_receive_from(
boost::asio::buffer(_recv_buffer),
_remote_endpoint,
boost::bind(&udp_server::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}

void udp_server::handle_receive(const boost::system::error_code &ec, std::size_t len)
{
std::string receive_message(_recv_buffer.data(), len);
if (strcmp(receive_message.c_str(), "login") == 0)
{
int isLogged = 0;
for (std::list<udp::endpoint>::iterator iter = _endpoint_list.begin(); iter != _endpoint_list.end(); ++iter)
{
if (*iter == _remote_endpoint)
{
session_send(_remote_endpoint, "You have already logged in.\n");
isLogged = 1;
break;
}
if (!isLogged)
{
std::cout << "User login.\nAddress : " << _remote_endpoint.address().to_string() << std::endl;
std::cout << "Port : " << _remote_endpoint.port() << std::endl;
_endpoint_list.push_back(_remote_endpoint);
session_send(_remote_endpoint, "login success.\n");
}
}
}
else if (strcmp(receive_message.c_str(), "logout") == 0)
{
int isLogged = 0;
for (std::list<udp::endpoint>::iterator iter = _endpoint_list.begin(); iter != _endpoint_list.end(); ++iter)
{
if (*iter == _remote_endpoint)
{
isLogged = 1;
_endpoint_list.erase(iter);
std::cout << "User logout.\nAddress : " << _remote_endpoint.address().to_string() << std::endl;
std::cout << "Port : " << _remote_endpoint.port() << std::endl;
session_send(_remote_endpoint, "Logout success.\n");
break;
}
}
if (!isLogged)
session_send(_remote_endpoint, "Logout failed, you have not logged in.\n");
}
else if (strcmp(receive_message.c_str(), "list") == 0)
{
std::ostringstream message;
int i = 0;
for (std::list<udp::endpoint>::iterator iter = _endpoint_list.begin(); iter != _endpoint_list.end(); ++iter)
{
if (*iter == _remote_endpoint)
message << "[" << i << "]" << iter->address().to_string() << ":" << iter->port() << " (yourself)" << std::endl;
else
message << "[" << i << "]" << iter->address().to_string() << ":" << iter->port() << std::endl;
i++;
}
session_send(_remote_endpoint, message.str());
}
else if (strncmp(receive_message.c_str(), "punch", 5) == 0)
{
int punched_client = atoi(receive_message.c_str() + 6);
std::list<udp::endpoint>::iterator iter = _endpoint_list.begin();
for (int i = 0; i < punched_client && iter != _endpoint_list.end(); ++i, ++iter);
std::ostringstream message;
if (iter == _endpoint_list.end())
message << "Punch failed, no such client.";
else
{
std::ostringstream peer_message;
//udp::endpoint peer_endpoint(iter->address(), iter->port);
peer_message << "PUNCH_REQUEST " << _remote_endpoint.address().to_string() << ":" << _remote_endpoint.port() << std::endl;
session_send(*iter, peer_message.str());
message << "PUNCH_SUCCESS " << iter->address().to_string() << ":" << iter->port() << std::endl;
}
session_send(_remote_endpoint, message.str());
}
else if (strcmp(receive_message.c_str(), "help") == 0)
{
session_send(_remote_endpoint, "Command:\
\n\thelp : Show this information.\
\n\tlogin : Login p2p server to make you punchable.\
\n\tlogout : Logout p2p server so that other client(s) won't find you.\
\n\tlist: List all client(s) that have login.\
\n\tpunch <client_number>: send punch request to remote client and start a p2p session\n");
}
else
{
session_send(_remote_endpoint, "Unknown command, please type 'help' to see more options.\n");
}
session_receive();
}

void udp_server::session_send(udp::endpoint &ep, std::string write_message)
{
//std::getline(std::cin, _write_message);
_sock.async_send_to(boost::asio::buffer(write_message),
ep,
boost::bind(&udp_server::handle_send,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}

void udp_server::handle_send(const boost::system::error_code &ec, std::size_t len)
{
//session_send();
}

int main(int argc, char *argv[])
{
boost::asio::io_service io_service;
udp_server server(io_service);
io_service.run();
return 0;
}


整理后的客户端文件

#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <cstring>
#include <cstdlib>
boost::shared_mutex io_mutex;

namespace
{
int p2p_connect = 0;
}

using boost::asio::ip::udp;
class udp_client
{
public:
udp_client(boost::asio::io_service &io_service, const char *host, const char *port):_sock(io_service)
{
udp::resolver _resolver(io_service);
udp::resolver::query _query(udp::v4(), host, port);
_server_endpoint = *_resolver.resolve(_query);
_sock.open(udp::v4());
start_send(); //**Must send first**
}

void start_send();
void session_send();
void handle_send(const boost::system::error_code &ec, std::size_t len);
void session_receive();
void handle_recevie(const boost::system::error_code &ec, std::size_t len);
void p2p_receive(udp::socket &sock, udp::endpoint &peer_endpoint);
void p2p_send(udp::socket *sock, udp::endpoint *peer_endpoint);
private:
udp::socket _sock;
udp::endpoint _server_endpoint;
boost::array<char, 512> _recv_buffer;
std::string _write_message;
};

void udp_client::start_send()
{
_sock.send_to(boost::asio::buffer("help"), _server_endpoint);
session_receive();
}

void udp_client::session_send()
{
std::getline(std::cin, _write_message);
_sock.async_send_to(
boost::asio::buffer(_write_message),
_server_endpoint,
boost::bind(&udp_client::handle_send,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}

void udp_client::handle_send(const boost::system::error_code &ec, std::size_t len)
{
//if(p2p_connect)
//    return;
//else
//    session_send();
}

void udp_client::session_receive()
{
_sock.async_receive_from(
boost::asio::buffer(_recv_buffer),
_server_endpoint,
boost::bind(&udp_client::handle_recevie,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}

void udp_client::handle_recevie(const boost::system::error_code &ec, std::size_t len)
{
std::string receive_message(_recv_buffer.data(), len);
if (strncmp(receive_message.c_str(), "PUNCH_SUCCESS", 13) == 0)
{
//punch finished
//start a p2p session to remote peer
std::cout << receive_message << std::endl;
p2p_connect = 1;
char str_endpoint[127];
strcpy(str_endpoint, receive_message.c_str() + 14);
char *peer_ip = strtok(str_endpoint, ":");
char *peer_port = strtok(NULL, ":");
udp::endpoint request_peer(boost::asio::ip::address::from_string(peer_ip),
std::atoi(peer_port));
_sock.send_to(boost::asio::buffer("Sender peer connection complete."), request_peer);
boost::thread(boost::bind(&udp_client::p2p_send, this, &_sock, &request_peer));
p2p_receive(_sock, request_peer);
}
else if (strncmp(receive_message.c_str(), "PUNCH_REQUEST", 13) == 0)
{
//send something to request remote peer
//and start a p2p session
std::cout << receive_message << std::endl;
p2p_connect = 1;
char str_endpoint[127];
strcpy(str_endpoint, receive_message.c_str() + 14);
char *peer_ip = strtok(str_endpoint, ":");
char *peer_port = strtok(NULL, ":");
udp::endpoint request_peer(boost::asio::ip::address::from_string(peer_ip),std::atoi(peer_port));

std::cin.clear(std::cin.rdstate() & std::cin.eofbit);
_sock.send_to(boost::asio::buffer("Receiver peer connection complete."), request_peer);
boost::thread(boost::bind(&udp_client::p2p_send, this, &_sock, &request_peer));
p2p_receive(_sock, request_peer);
}
else
{
std::cout << receive_message << std::endl;
}
session_receive();
if (p2p_connect)
return;
else
session_send();
}

void udp_client::p2p_receive(udp::socket &sock, udp::endpoint &peer_endpoint)
{
for (;;)
{
boost::system::error_code error;
//blocked until successfully received
size_t len = sock.receive_from(boost::asio::buffer(_recv_buffer),
peer_endpoint, 0, error);
std::string receive_message(_recv_buffer.data(), len);
std::cout << receive_message << std::endl;
}
}

void udp_client::p2p_send(udp::socket *sock, udp::endpoint *peer_endpoint)
{
while (std::getline(std::cin, _write_message))
{
sock->send_to(boost::asio::buffer(_write_message), *peer_endpoint);//blocked
}
}

int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cerr << "Usage: client <Server IP> <Server Port>" << std::endl;
return 1;
}

boost::asio::io_service io_service;
udp_client client(io_service, argv[1], argv[2]);

//run in 2 threads
//boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
io_service.run();
return 0;
}


参考

[1]《P2P通信原理与实现(C++)》
http://www.tuicool.com/articles/YNBVrei
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++