C# TCP 如何打洞实现P2P传输。

扬帆破浪 2013-08-02 02:27:06
C# TCP 如何打洞实现P2P传输。有源码的提供一份吧。谢谢。
...全文
1387 43 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
TCP可以打洞。但是 P2P 不一定是通过打洞来实现的,甚至80%以上的时间(在打洞失败后)都是用非打洞方式实现P2P。 要注意这个技术。当你想写一个P2P系统时,先不要考虑任何“打洞”概念,先把非打洞的P2P系统设计好吧!
  • 打赏
  • 举报
回复
这些都是真正的打洞 --> 这些都不一定是真正的打洞 这里漏写了一个词儿,意思完全反了。实际上我想说的是:P2P不需要打洞。
crystal_lz 2015-08-11
  • 打赏
  • 举报
回复
引用 39 楼 crystal_lz 的回复:
[quote=引用 38 楼 dongxinxi 的回复:] YY好像不行,因为C1跟C2根本没有TCP的必需的“三次握手”,收到数据只会被丢弃 还是需要中间转发的服务器(路由器)
我已经用实践证明了我的YY 64的IP 是我的一台vps 开放10086端口 218的IP是我 连接到vps上面的IP得到的IP 24295 是端口号码 vps上面那个 Form1 的代码大概如下:

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Bind(new IPEndPoint(IPAddress.Any, 10086));//监听vps 10086端口
sock.Listen(5);
while (true) {
    try {
        Socket sock_client = sock.Accept();
        textBox1.BeginInvoke(new MethodInvoker(() => {
            textBox1.Text += sock_client.RemoteEndPoint.ToString() + " -> in\r\n";
        }));
        sock_client.Send(Encoding.UTF8.GetBytes("welcome"));//回应连接好了
}
然后 上图黑框我是 telnet到 vps 的 10086 端口的 可以看到上面有 welcome字样 说明我连接过去了 然后下面是我用原始套接字发送的数据包

Socket sock_raw = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
sock_raw.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);
//TcpDefine 自己定义的一个类
//获取IP报文数据包 64 那个ip 差不多就是(服务器上 sock.RemoteEndPoint.IP的值)这里可乱填 因为不需要服务器来回应我
TcpDefine.IP_HEADER ipHeader = TcpDefine.GetIPHeader("64.xxx.xxx.xxx", "218.xxx.xxx.xxx");
//获取tcp报文数据包 
TcpDefine.TCP_HEADER tcpHeader = TcpDefine.GetTcpHeader(
    10086,  //对方能获取到的远程主机的端口 差不多就是(服务器上 sock.RemoteEndPoint.Port的值) 这里可乱填不需要回应
    24295,  //到达端口
    BitConverter.ToInt32(new byte[] { 0xc6, 0x2a, 0x35, 0x73 }, 0), //seq验证序列号 抓包抓出来的 这个不能错
    BitConverter.ToInt32(new byte[] { 0x4e, 0x78, 0x28, 0x7a }, 0), //ack回应序列号(服务器回应时候 seq = ack + 1)
    TcpDefine.Flag.ACK | TcpDefine.Flag.PSH //标志
    );
//构造数据包
byte[] bySend = TcpDefine.GetPacketHeader(ipHeader, tcpHeader, Encoding.UTF8.GetBytes("this is a test hahaha "));
sock_raw.SendTo(bySend, new IPEndPoint(IPAddress.Parse("218.xxx.xxx.xxx"), 24295));//发送数据包
可见我执行这个程序后 在我上面 telnet 的框中出现了 test 字样 说明我的数据包正确的被接收了 下面是抓包数据 172.16.8.152那个是我win7所在的局域网IP 前三个数据包 是 三步握手 数据包 接下 psh,ack 那个是服务器回应的 welcome 然后再下去那个ack是我 win7 telnet 收到welcome消息的确认回应 最后一个数据包 就是我上面的那段代码发送出来的 运行上面的代码后 telnet窗口出现了我的 test 的那条消息 不过不知道怎么回事 被welcome挡住了 实践说明 我的YY不是不可能[/quote] 我在三台电脑上完成了数据的对接 vps上放在一个服务程序 用于接收客户端连接的 win7上是telnet 连接过去 服务端上 打印出对应的IP和端口 然后我在虚拟机2003里面 直接发送TCP IP 数据包 由于TCP和IP的包头都是我自己构造的数据包 所以我想要让这个数据到里就到哪里 我Telnet到vps得到的端口好和IP是 自动做了映射的 所以我可以直接利用这个IP和端口号直接发送数据包 而不用去建立连接 因为 连接本来就是建立好了的 所以我虚拟机里面的 程序直接用我win7连接到vps上的端口和ip 构造数据包直接发送出去 而消息也确实被发送到了我win7的电脑上 还有我之所以在虚拟的2003上面 发送数据包 是因为。。。。。貌似从xp开始 就限制了 原始套接字的使用 而2003可以使用。。要在win7发原始套接字是发送不了的 没研究过 winpcap 估计他可以。。 好了 我装逼完了。。
  • 打赏
  • 举报
回复
tcp打洞跟udp的原理是一样的,只不过由于tcp的特点,那么需要专门设置socket的 Attribute 为“端口复用的”(tcp默认是不允许端口复用的,而udp则没有这个限制)。 QQ之类的在局域网内直接传文件,或者 Skype 之类的通过临时分配的某个用户节点来直接“点对点”通讯,这些都是真正的打洞。 比如说你有很多客户端,你的客户端可以向服务器提交客户端在局域网内部的IP地址、监听端口号,那么你的服务器就可以帮助两个客户端先尝试在局域网内部直接连(比如说发送文件的QQ是客户端、接收文件的QQ是服务器端)。如果连不上,再通过服务器中转。 而skype就是这种原理。当两个节点不能直接连时,它就会临时使用离两个用户最近的一个“肉鸡节点”当作服务器,直接为两个客户端中转语音消息。如果找不到肉鸡节点,那么就不连了(它自己的master服务器从来不直接做业务处理)。有人说P2P就是打洞,这是不了解情况。 实际上,打洞的成功率很低。P2P系统完全可能是非打洞方式的!
crystal_lz 2015-08-11
  • 打赏
  • 举报
回复
引用 38 楼 dongxinxi 的回复:
YY好像不行,因为C1跟C2根本没有TCP的必需的“三次握手”,收到数据只会被丢弃
还是需要中间转发的服务器(路由器)

我已经用实践证明了我的YY

64的IP 是我的一台vps 开放10086端口
218的IP是我 连接到vps上面的IP得到的IP 24295 是端口号码
vps上面那个 Form1 的代码大概如下:

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Bind(new IPEndPoint(IPAddress.Any, 10086));//监听vps 10086端口
sock.Listen(5);
while (true) {
try {
Socket sock_client = sock.Accept();
textBox1.BeginInvoke(new MethodInvoker(() => {
textBox1.Text += sock_client.RemoteEndPoint.ToString() + " -> in\r\n";
}));
sock_client.Send(Encoding.UTF8.GetBytes("welcome"));//回应连接好了
}

然后 上图黑框我是 telnet到 vps 的 10086 端口的 可以看到上面有 welcome字样 说明我连接过去了
然后下面是我用原始套接字发送的数据包

Socket sock_raw = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
sock_raw.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);
//TcpDefine 自己定义的一个类
//获取IP报文数据包 64 那个ip 差不多就是(服务器上 sock.RemoteEndPoint.IP的值)这里可乱填 因为不需要服务器来回应我
TcpDefine.IP_HEADER ipHeader = TcpDefine.GetIPHeader("64.xxx.xxx.xxx", "218.xxx.xxx.xxx");
//获取tcp报文数据包
TcpDefine.TCP_HEADER tcpHeader = TcpDefine.GetTcpHeader(
10086, //对方能获取到的远程主机的端口 差不多就是(服务器上 sock.RemoteEndPoint.Port的值) 这里可乱填不需要回应
24295, //到达端口
BitConverter.ToInt32(new byte[] { 0xc6, 0x2a, 0x35, 0x73 }, 0), //seq验证序列号 抓包抓出来的 这个不能错
BitConverter.ToInt32(new byte[] { 0x4e, 0x78, 0x28, 0x7a }, 0), //ack回应序列号(服务器回应时候 seq = ack + 1)
TcpDefine.Flag.ACK | TcpDefine.Flag.PSH //标志
);
//构造数据包
byte[] bySend = TcpDefine.GetPacketHeader(ipHeader, tcpHeader, Encoding.UTF8.GetBytes("this is a test hahaha "));
sock_raw.SendTo(bySend, new IPEndPoint(IPAddress.Parse("218.xxx.xxx.xxx"), 24295));//发送数据包

可见我执行这个程序后 在我上面 telnet 的框中出现了 test 字样 说明我的数据包正确的被接收了
下面是抓包数据

172.16.8.152那个是我win7所在的局域网IP 前三个数据包 是 三步握手 数据包
接下 psh,ack 那个是服务器回应的 welcome
然后再下去那个ack是我 win7 telnet 收到welcome消息的确认回应
最后一个数据包 就是我上面的那段代码发送出来的 运行上面的代码后 telnet窗口出现了我的 test 的那条消息 不过不知道怎么回事 被welcome挡住了
实践说明 我的YY不是不可能
  • 打赏
  • 举报
回复
YY好像不行,因为C1跟C2根本没有TCP的必需的“三次握手”,收到数据只会被丢弃 还是需要中间转发的服务器(路由器)
crystal_lz 2015-08-11
  • 打赏
  • 举报
回复
我可以YY一下么 一台服务器S 两台客户端C1,C2 若要C1 -> C2 两个C分别连接到S S必然会得到一个C2的 临时通讯接口 IP:PORT 而这个个 ip和端口 已经被映射了 可以通过这个ip和端口 直接访问到C2 下面就是见证奇迹的时刻了 C1直接发送TCPIP数据包 将IP包头改为C1的ip 回应IP改为连接到S的自己的外网ip 将TCP包头中目标端口改为C2端口 回应端口改为自己连接到S的自己的端口 然后发送数据包出去 欺骗C2客户端 让他以为是在和S通讯 然后你要做的 就是自己模拟tcp通信发送原始数据包。。 YY完毕。。。
wanghui0380 2015-08-11
  • 打赏
  • 举报
回复
tcp不好弄,如果环境支持可以用upnp http://www.cnblogs.com/yuanfan/archive/2011/03/13/1982551.html 这其实就是一个硬件级别的代理转发协议,你可以直接用代码在路由上开NAT,这样就无需操心什么,服务直接代理转发就是 (其实目前大部分人的路由都支持这个,只有少量行内人才会专门去把路由上这个功能给关闭掉) 如果硬件级别环境不允许搞,那就自己用代码实现,这种玩意很早以前叫“tcp端口反弹技术”,就是自己做一个双server端的公网服务,同时保持和双方连接,然后做代理桥接工作。
  • 打赏
  • 举报
回复
引用 27 楼 gaige0312 的回复:
我看网上有说用路由器映射一下就可以当外网测试 我不是很明白 你明白么
就是路由器管理界面中的“虚拟服务器”,如果没有可能就没这项功能,家用的可能就没有
微差 2015-08-11
  • 打赏
  • 举报
回复
我也无语了,我也是来学习的
modyaj 2015-06-02
  • 打赏
  • 举报
回复
X 这么久了 怎么还没人来 啊啊啊啊啊 我也碰到这个问题啊!
gaoxiaoqingini 2013-10-14
  • 打赏
  • 举报
回复
我也在找tcp打洞的C#源码啊,和楼主一起等吧,在高手出现之前,先用udp打洞吧!
拥抱开源 2013-09-13
  • 打赏
  • 举报
回复
坐等高手了
feiniao19830822 2013-09-13
  • 打赏
  • 举报
回复
引用 9 楼 wangyue4 的回复:
以前有看书上说tcp打洞不是所有路由器都支持的
+1 我查网上资料,看到的也是这个结论。qq传文件的时候,如果不在同一个局域网应该是用tcp中转方式。我测试过qq跨网传输文件,在一开始有个明显的等待时间。
异常异长 2013-09-13
  • 打赏
  • 举报
回复
等待高手。。。。。
gaige0312 2013-09-13
  • 打赏
  • 举报
回复
我看网上有说用路由器映射一下就可以当外网测试 我不是很明白 你明白么
扬帆破浪 2013-09-13
  • 打赏
  • 举报
回复
需要一个外网服务器,有些也是打不通的,有几个类型是打不通的。
gaige0312 2013-09-13
  • 打赏
  • 举报
回复
UDP打洞需要用外网的服务器么
扬帆破浪 2013-09-13
  • 打赏
  • 举报
回复
引用 27 楼 gaige0312 的回复:
我看网上有说用路由器映射一下就可以当外网测试 我不是很明白 你明白么
就是用花生壳之类的做个解析,进入路由把你开通的端口指定到你的计算机,当你连接你的公网IP和端口时,就连接到了你的服务器,这样就当了一个外网服务器。
  • 打赏
  • 举报
回复
的确不是一个复杂问题。
加载更多回复(22)

111,097

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧