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方法,需要服务端。
假设有A、B两个客户端和S一个服务器
Step 1 : A、B发送UDP请求给S,S知道了A、B在公网的IP和端口。
Step 2: A从S中取B在公网的IP和端口。
B从S中取A在公网的IP和端口。
Step 3: A通过B在公网的IP和端口向B发送UDP请求。
B通过A在公网的IP和端口向A发送UDP请求。
Step Final:
A同B之间可以通过UDP直接互相发送信息。
目前看来这种方法最大的缺点, 还是需要服务器。
整理后的服务端文件
整理后的客户端文件
参考
[1]《P2P通信原理与实现(C++)》
http://www.tuicool.com/articles/YNBVrei
整理时间: 2016-03-24 by kagula
UDP Hole Punching方法,需要服务端。
假设有A、B两个客户端和S一个服务器
Step 1 : A、B发送UDP请求给S,S知道了A、B在公网的IP和端口。
Step 2: A从S中取B在公网的IP和端口。
B从S中取A在公网的IP和端口。
Step 3: A通过B在公网的IP和端口向B发送UDP请求。
B通过A在公网的IP和端口向A发送UDP请求。
Step Final:
A同B之间可以通过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++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性