所谓udp打洞就是指客户端A通过udp协议向服务器发送数据包,服务器收到后,获取数据包,并且

可获取客户端A地址和端口号。同样在客户端B发送给服务器udp数据包后,服务器同样在收到B发送过来

的数据包后获取B的地址和端口号,将A和B的地址与端口号分别发送给对方,这样双方可以继续用UDP协议

通信。这么做有什么用呢?因为对于一些应用或者需求,需要两个客户端临时做一些通信,而这种通信

不需要建立tcp就可以完成,所以才去udp打洞。

下面附上测试代码:

头文件

// udphole.cpp : 定义控制台应用程序的入口点。

#ifdef WIN32
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
typedef SOCKET socketfd;
typedef SOCKADDR_IN sockaddr_in;
#endif #ifdef __linux__ #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <errno.h>
#include <arpa/inet.h>
#include <pthread.h> typedef int socketfd;
#endif
#include <list>
#include <map>
#include <iostream>
using namespace std;

服务器端核心代码。

#include <list>
#include <map>
#include <iostream>
using namespace std; int main(int argc, char* argv[])
{
#ifdef WIN32
std::list<SOCKADDR_IN> addrList; WSADATA wsaData = {};
if ( != WSAStartup(MAKEWORD(,), &wsaData))
{
printf ("WSAStartup failed. errno=[%d]\n", WSAGetLastError());
return -;
}
#endif #ifdef __linux__
std::list<sockaddr_in> addrList; #endif //addrList 是地址列表,每次存放最新到来的。
socketfd sockServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (- == sockServer)
{ #ifdef WIN32
printf ("socket server failed. errno=[%d]\n", WSAGetLastError());
#endif #ifdef __linx__
printf("socket server failed. errno=[%d]\n", errno);
#endif return -;
} sockaddr_in addrServer = {}; addrServer.sin_family = AF_INET;
addrServer.sin_addr.s_addr = INADDR_ANY;//inet_addr("192.168.1.2");
addrServer.sin_port = htons();
if ( != bind(sockServer, (sockaddr*)&addrServer, sizeof(addrServer)))
{
#ifdef WIN32
printf ("bind server failed.errno=[%d]\n", WSAGetLastError());
#endif #ifdef __linux__
printf("bind server failed.errno=[%d]\n", errno);
#endif return -;
} cout << "okok6"<<endl;
while()
{
char pcContent1[] = {};
sockaddr_in addrUser1 = {};
#ifdef WIN32
int nLen1 = sizeof(addrUser1);
#endif #ifdef __linux__
socklen_t nLen1 = sizeof(addrUser1);
#endif
//服务器接收来自客户端的消息,并且用addrUser1保存地址和端口
if (- == recvfrom(sockServer, pcContent1, sizeof(pcContent1), , (sockaddr*)&addrUser1, &nLen1))
{
cout << "dfdfda"<<endl; #ifdef WIN32
printf ("recv user 1 failed.errno=[%d]", WSAGetLastError());
#endif #ifdef __linux__
printf ("recv user 1 failed.errno=[%d]", errno);
#endif return -;
}
else
{ //
printf ("connect user ip=[%s] port=[%d]\n", inet_ntoa(addrUser1.sin_addr), htons(addrUser1.sin_port));
//如果地址列表非空,那么取出列表中的地址,并且与最新到来的客户端通信
if(addrList.size())
{
sockaddr_in peerAddr = addrList.front();
int nLen2 = sizeof(peerAddr);
printf ("peer user ip=[%s] port=[%d]\n", inet_ntoa(peerAddr.sin_addr), htons(peerAddr.sin_port)); if (- == sendto(sockServer, (char*)&addrUser1, nLen1, , (sockaddr*)&peerAddr, nLen2))
{
#ifdef WIN32
printf ("send to peer user data failed.\n", WSAGetLastError());
#endif #ifdef __linux__
printf ("send to peer user data failed.\n", errno);
#endif
return -;
} if (- == sendto(sockServer, (char*)&peerAddr, nLen2, , (sockaddr*)&addrUser1, nLen1))
{
#ifdef WIN32
printf ("send to connect user data failed.\n", WSAGetLastError());
#endif #ifdef __linux__
printf ("send to connect user data failed.\n", errno);
#endif
return -;
} addrList.pop_front();
}
else
{
//如果列表为空,那么将该地址放入列表中。
addrList.push_back(addrUser1);
}
}
} #ifdef WIN32
Sleep(INFINITE);
#endif #ifdef __linux__
//sleep(1000);
#endif
return ;
}

下面是客户端发送消息的代码,比较简单。

#include "stdafx.h"

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib") int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData = {};
if ( != WSAStartup(MAKEWORD(,), &wsaData))
{
printf ("WSAStartup failed. errno=[%d]\n", WSAGetLastError());
return -;
}
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (SOCKET_ERROR == sockClient)
{
printf ("socket server failed. errno=[%d]\n", WSAGetLastError());
return -;
}
char pcContent1[UCHAR_MAX] = {};
SOCKADDR_IN addrServer = {};
addrServer.sin_family = AF_INET;
addrServer.sin_addr.s_addr = inet_addr("192.168.1.40");
addrServer.sin_port = htons();
int nLen1 = sizeof(addrServer);
//客户端发送自己的报文
if (SOCKET_ERROR == sendto(sockClient, pcContent1, , , (sockaddr*)&addrServer, nLen1))
{
printf ("recv user 1 failed.errno=[%d]", WSAGetLastError());
return -;
}
SOCKADDR_IN addrUser = {};
char pcContent2[UCHAR_MAX] = {};
//阻塞接收来自服务器的消息。
if (SOCKET_ERROR == recvfrom(sockClient, pcContent2, sizeof(pcContent2), , (sockaddr*)&addrServer, &nLen1))
{
printf ("recv user 1 failed.errno=[%d]", WSAGetLastError());
return -;
}
else
{
memcpy (&addrUser, pcContent2, sizeof(addrUser));
sprintf (pcContent2, "hello, user ip=[%s] port=[%d]\n", inet_ntoa(addrUser.sin_addr), htons(addrUser.sin_port));
//解析服务器消息后发送消息给另一个客户端。
if (SOCKET_ERROR == sendto(sockClient, pcContent2, strlen(pcContent2), , (sockaddr*)&addrUser, nLen1))
{
printf ("recv user 1 failed.errno=[%d]", WSAGetLastError());
return -;
}
else
{
//阻塞接收另一个客户端发送过来的消息
if (SOCKET_ERROR == recvfrom(sockClient, pcContent2, sizeof(pcContent2), , (sockaddr*)&addrServer, &nLen1))
{
printf ("recv user 1 failed.errno=[%d]", WSAGetLastError());
return -;
}
printf ("%s", pcContent2);
}
}
Sleep(INFINITE);
return ; }

效果如下,服务器收到来自客户端A和客户端B的报文后打印他们的信息,并且互相转发消息。

客户端A和客户端B分别打印对方的地址和端口号

到此为止,udp打洞的代码介绍完了。可以关注我的公众号,谢谢。

Udp打洞原理和源代码。的更多相关文章

  1. UDP打洞原理及代码

    来源:http://www.fenbi360.net/Content.aspx?id=1021&t=jc UDP"打洞"原理 1.       NAT分类 根据Stun协议 ...

  2. UDP 打洞 原理解释

    终于找到了一份满意的UDP打洞原理解释,附上正文,自己整理了一下源码 3.3. UDP hole punching UDP打洞技术 The third technique, and the one o ...

  3. [转]UDP穿透NAT的原理与实现(UDP“打洞”原理)

    NAT(The IP Network Address Translator) 的概念和意义是什么? NAT, 中文翻译为网络地址转换.具体的详细信息可以访问RFC 1631 - http://www. ...

  4. UDP"打洞"原理

    1. NAT分类 根据Stun协议(RFC3489),NAT大致分为下面四类 1) Full Cone 这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口 ...

  5. p2p的UDP打洞原理

    >>>>>>>>>>>>>>>>>>>>>>>>> ...

  6. UDP打洞原理介绍

     NAT穿越模块的设计与实现 Internet的快速发展以及IPv4地址数量的不足使得NAT设备得到了大规模的应用,然而这也给越来越多的端到端通信也带来了不少的麻烦.一般来说,NAT设备允许内网内主机 ...

  7. JedisPool使用原理和源代码

    1,JedisPool的使用 <!-- 连接池的配置信息 --><beanid="jedisConfig"class="redis.clients.je ...

  8. udp打洞( NAT traversal )的方法介绍

    http://www.cnblogs.com/whyandinside/archive/2010/12/08/1900492.html http://www.gzsec.com/oldversion/ ...

  9. UDP ------ UDP打洞

    为什么需要UDP打洞 处于两个不同局域网的主机不能直接进行UDP通信 UDP"打洞"原理 1.       NAT分类 根据Stun协议(RFC3489),NAT大致分为下面四类 ...

随机推荐

  1. ShipStation Now Uses AWS And Amazon Fulfillment To Automatically Ship From eBay, Sears And Other Marketplaces

    ShipStation today unveiled a first-of-its-kind service to leverage Amazon Web Services and Amazon.co ...

  2. Java 学习笔记 ------第六章 继承与多态

    本章学习目标: 了解继承的目的 了解继承与多态的关系 知道如何重新定义方法 认识java.lang.object 简介垃圾回收机制 一.继承 继承是java面向对象编程技术的一块基石,因为它允许创建分 ...

  3. Nginx 使用札记

    nginx是什么? nginx是俄罗斯人 Igor Sysoev为俄罗斯访问量第二的Rambler.ru站点开发的一个十分轻量级的HTTP服务器.它是一个高性能的HTTP和反向代理服务器,同时也可以作 ...

  4. Transparent Flow Migration for NFV

    Transparent Flow Migration for NFV 摘要 因为SDN提供的灵活性,NF之间存在着流量的迁入和迁出问题.而且NF也要根据相关的状态信息处理数据包,所以流量迁移必须满足以 ...

  5. vs中如何使用NuGet

    在vs中如何打开NuGet? 1.工具→NuGet程序包管理器→程序包管理控制台 2.没有的话,就去  工具→扩展和更新   搜索nuget 如果你点击工具,没看到Nuget这些字样,请注意汉化名字为 ...

  6. Alpha事后诸葛亮(团队)

    设想和目标 1.我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件要解决用手机使没有指纹验证硬件的电脑可以利用指纹进行文件的加密.定义的很清楚.我们针对的是 ...

  7. 《DWZ笔记一》<select>动态联动菜单

    联动菜单,即组合框Combo box,在DWZ文档中对组合框combox的是这样描述的: 在传统的select 用class 定义:class=”combox”, html 扩展:保留原有属性name ...

  8. 在原有的基础之上,启用NAT模型

    # 给虚拟主机实例添加一个网关 route add default gw 192.168.23.1   # 在宿主机打开网卡间转发功能 echo 1 > /proc/sys/net/ipv4/i ...

  9. 4th 课堂SCRM会议旁观记录

    项目名称:基于C#的连连看设计 小组名称:待定 小组成员:张政.张金生.武志远.李权 Master:张政 项目已完成部分: 现阶段已经实现了一定的功能,可以运行使用,进行第一关的游戏. 今天计划要完成 ...

  10. 微信获取 openid 静默及非静默

    <?php /* 需要的微信公众号配置信息 APPID : 绑定支付的APPID APPSECRET : 公众帐号secert */ class Index { // 配置账号信息 privat ...