java实现P2P通信(含安卓实现的基于IPV6的p2p通信代码)

java实现P2P通信(含安卓实现的基于IPV6的p2p通信代码)

什么是P2P网络

p2p网络又叫对等网络,顾名思义就是在该网络中所有节点都是平等的,都可以共享自己的硬件资源和数据资源。每个节点都能被其它对等节点直接访问而无需经过中间实体。换句话来说,目前绝大多数应用都是基于C/S或者B/S架构的,就拿微信来说,当A要通过微信给B发一条消息时,消息首先会从A发送到服务器,然后服务器在转发给B。这样服务器就会知道AB之间的聊天记录。而P2P网络来说,A可以直接发送消息给B不需要中转服务器。这样的好处就不言而喻了。

用udp打洞的三种方式

本来按照互联网之前的设计p2p通信不是什么大问题。A想给B发消息只需要知道B的IP地址和端口号就可以直接发送消息给B了。但是由于IPV4地址数量只有42亿多个,已经远远不能满足人们的上网需求,因此出现了NAT技术,通过绑定端口让多台计算机可以共用一个公网IP从而缓解公网IP地址空间的枯竭。但这样就带来了新的问题。A不知道B的公网IP和映射端口号就找不到B了。那么我就有了个大胆的想法,通过中间服务器存储A和B的公网IP以及映射的端口号,然后分别发送给A和B,这样A和B就知道对方的公网IP和端口号,不就可以访问了吗。这个也叫做udp打洞。如图所示:在这里插入图片描述
基于上图我用java的udp协议包实现了程序代码如下:
服务器:

// An highlighted block
public class p2pService {
	//map存储每个注册用户的名字和公网地址
	Map<String,String>map =new HashMap<String,String>();
	byte[] inBuff=new byte[2048];
	DatagramPacket inpacket=new DatagramPacket(inBuff,2048);
	public void init() {
		try {
			System.out.println("服务器启动");
	DatagramSocket socket=new DatagramSocket(30000);
	while(true) {
		socket.receive(inpacket);
		System.out.println("地址:"+inpacket.getSocketAddress()+" 端口:"+inpacket.getPort()+" 消息:"+new String(inBuff,0,inpacket.getLength()));
		JSONObject json=JSONObject.fromObject(new String(inBuff,0,inpacket.getLength()));
		//message封装通信消息格式
	    message mess=(message)JSONObject.toBean(json, message.class);
	    //link类消息为注册消息
		if("link".equals(mess.type))
		{
			map.put(mess.name, inpacket.getSocketAddress()+"");
			List<Map<String,String>>list=new ArrayList<>();
			for(String key:map.keySet())
			{
				Map<String,String> map1=new HashMap<>();
				map1.put("name", key);
				map1.put("IP", map.get(key));
				list.add(map1);
			}
			String messs=JSONArray.fromObject(list)+"";
			message mes=new message();
			mes.type="list";
			mes.value=messs;
			messs=JSONObject.fromObject(mes)+"";
			System.out.println(messs);
			byte[] outmess=messs.getBytes("utf-8");
			DatagramPacket out=new DatagramPacket(outmess,outmess.length,inpacket.getSocketAddress());
			//返回所有以注册用户的姓名
		socket.send(out);
		}
		//comm是需要获取通信对方的ip
		else if("comm".equals(mess.type)) {
		//B的IP
			String IPB=map.get(mess.name);
			//A的IP
			String IPA=inpacket.getSocketAddress()+"";
			message result=new message();
			result.type="link";
			result.IP=IPB.substring(1,IPB.lastIndexOf(":"));
			System.out.println("comm:"+result.IP);
			result.port=IPB.split(":")[8];
			System.out.println("link:"+result.IP+" port"+result.port);
			byte[] buff=JSONObject.fromObject(result).toString().getBytes();
		DatagramPacket out=new DatagramPacket(buff,buff.length,inpacket.getSocketAddress());
		
		//返回B的IP给A
			socket.send(out);
			result.IP=IPA.substring(1,IPB.lastIndexOf(":"));
			result.port=IPA.split(":")[8];
			
			 buff=JSONObject.fromObject(result).toString().getBytes();
			 out=new DatagramPacket(buff,buff.length,InetAddress.getByName(IPB.substring(2,IPB.lastIndexOf(":")-1)),Integer.parseInt(IPB.split(":")[8]));
			 //返回A的IP给B
			 socket.send(out);
			 
		}
		
		
	}
	
		}
		catch(Exception ex) {
			
			ex.printStackTrace();
		}
		
	}

	
	public static void main(String[] args) {
		p2pService service=new p2pService();
		
		service.init();
		
	}
}

客户端:

public class p2pClient {
	byte[] inBuff=new byte[2048];
	DatagramPacket inpacket=new DatagramPacket(inBuff,2048);
	 String name;//用户名字
	DatagramSocket socket;
	String IP=null;//通信对方的IP
	int port=0;//通信对方的端口
	String ServiceIP="32.33.133.23";//服务器IP
	int ServicePort=30000;//服务器端口
public void init(int part) {
	try {
		 socket=new DatagramSocket(part);
		 message mes=new message();
		 mes.setName(name);
		 mes.setType("link");
		byte[] outbyte=JSONObject.fromObject(mes).toString().getBytes("utf-8");
		DatagramPacket out=new DatagramPacket(outbyte,outbyte.length,InetAddress.getByName(ServiceIP),ServicePort);
		socket.send(out);
		socket.receive(inpacket);
		System.out.println("地址1:"+inpacket.getSocketAddress()+" 端口:"+inpacket.getPort()+" 消息:"+new String(inBuff,0,inpacket.getLength()));
		new Thread(new receive(this)).start();
	}
	catch(Exception ex) {
		ex.printStackTrace();
	}
}	
public void p2pcomm()throws Exception {
	Scanner sc=new Scanner(System.in);
	while(sc.hasNext())
	{
		String message=sc.next();
		//A获得B的IPcomm:B
		if(message.startsWith("comm:")) {
			DatagramPacket out=new DatagramPacket(message.getBytes(),message.getBytes().length,InetAddress.getByName(ServiceIP),ServicePort);
			socket.send(out);
		}
		//A改变通信IP为B chang:IP。以下是基于IPV6,可自己改成IPV4
		else if(message.startsWith("change:")) {
		
			IP=message.substring(message.indexOf(":")+1, message.lastIndexOf(":"));
			System.out.println("IP:"+IP);
			port=Integer.parseInt(message.split(":")[9]);
			System.out.println("转换成功");
			
		}
		//直接发送消息给B
		else {
			DatagramPacket out=new DatagramPacket(message.getBytes(),message.getBytes().length,InetAddress.getByName(IP),port);
			socket.send(out);
		}
	
	}
	
}
	
public static void main(String[] args) {
	p2pClient client=new p2pClient();
	try {
	Scanner sc=new Scanner(System.in);

	if(sc.hasNext()) {
		client.name=sc.next();
	}
	System.out.println("客户端"+client.name+"启动");
	if(sc.hasNext()) {
	client.init(Integer.parseInt(sc.next().substring(0, 5)));
	client.p2pcomm();
	}
	}
	catch(Exception ex) {
		
		ex.printStackTrace();
	}
	
	
	
}
}
class receive implements Runnable{
	p2pClient client;
	//String IP=null;
	//int port=0;
	//byte[] inBuff=new byte[2048];
	//DatagramPacket inpacket=new DatagramPacket(inBuff,2048);
	String name;
	
	public receive(p2pClient client) {
		this.client=client;
		name=client.name;
		
	}
	//监听接收的消息
public void run() {
	try {
		while(true) {
			
			client.socket.receive(client.inpacket);
			if(new String(client.inBuff,0,client.inpacket.getLength()).startsWith("commIP:"))
			{
				String message=new String(client.inBuff,0,client.inpacket.getLength());
				System.out.println();
				client.IP=message.substring(message.indexOf(":")+3, message.lastIndexOf(":")-1);
				//client.IP=new String(client.inBuff,0,client.inpacket.getLength()).split(":")[1].substring(1);
				System.out.println("IP:"+client.IP);
				client.port=Integer.parseInt(new String(client.inBuff,0,client.inpacket.getLength()).split(":")[2]);
				DatagramPacket out=new DatagramPacket((name+":1").getBytes(),(name+":1").getBytes().length,InetAddress.getByName(client.IP),client.port);
				client.socket.send(out);
			}
			else if(new String(client.inBuff,0,client.inpacket.getLength()).startsWith("link:")) {
				String message=new String(client.inBuff,0,client.inpacket.getLength());
				System.out.println("message:"+message);
				client.IP=message.substring(message.indexOf(":")+3, message.lastIndexOf(":")-1);
				System.out.println("IP:"+client.IP);
				client.port=Integer.parseInt(new String(client.inBuff,0,client.inpacket.getLength()).split(":")[9]);
				DatagramPacket out=new DatagramPacket((name+" link you").getBytes(),(name+" link you").getBytes().length,InetAddress.getByName(client.IP),client.port);
				client.socket.send(out);
			}
			
			
			System.out.println(name+"收到消息:"+new String(client.inBuff,0,client.inpacket.getLength())+"  来自"+client.inpacket.getAddress()+":"+client.inpacket.getPort());
	
			
		}
		
	}
	catch(Exception ex)
	{
		ex.printStackTrace();
	}	
}	
}

按照这个思路代码应该是可以实现不同内网下的计算机直接通信。但当我测试的时候却无法实现。而如果我让B直接处于公网状态,则可以通信。这个问题让我听困惑。难道是哪出问题了。我仔细把我的方案屡了一遍发现,是在NAT的时候出问题了。我设想的NAT是固定的,即从A的30001端口映射的公网端口就是41200,但实际却不是这样的。这个就要好好补一下NAT了。NAT被分为四类:
1、full cone 全椎
2、Restricted Cone ip受限
3、port Restricted Cone 端口受限
4、Symmetric 对称。
1.full cone:完全圆锥型的NAT,将从同一内部IP地址和端口来的所有请求,都映射到相同的外部IP地址和端口。而且,任何外部主机通过向映射的外部地址发送报文,可以实现和内部主机进行通信。

2.Restricted Cone ip: 受限圆锥型NAT也是将从相同的内部IP地址和端口来的所有请求,映射到相同的公网IP地址和端口。但是与完全圆锥型NAT不同,外部主机B不能直接通过映射到相同的公网IP地址和端口访问A。而是需要内部主机A主动向外部主机B发送了消息后,外部主机B才能使用公网IP地址和端口访问A。

3.port Restricted Cone 端口受限: 类似于受限圆锥型NAT,但更严格。端口受限圆锥型NAT增加了端口号的限制,要求B访问A时B的端口号必须和A访问B时B的端口号相同才能通信。

4.Symmetric:对称型NAT把从同一内网地址和端口到相同的地址和端口的所有请求,都映射到同一个公网地址和端口。
即A访问服务器时留下的的端口,B是不能用的。B无法知道要访问什么端口才能和A通信,同时如果B也处在对称型NAT下,那么A和B都无法找到通讯端口。可见,对称性NAT是所有NAT类型中限制最为严格的。
从上可以知道我的方案设计时默认了移动通信运营商是采用端口受限NAT来设计的。但事实上目前基本上用的都是对称型NAT。A向服务器端口port0注册时留下的映射端口port1只能服务器端口port0才能访问,B是访问不了的。同理B向服务器注册时留下的映射端口port2,A也无法访问。就无法打洞。后来我看了一些文章,又说用猜端口的方法打洞。怎么说了,理论上是没问题的,但是实际操作的话十分困难,首先A不知道自己映射的端口号porta,同时也不知道B留给A的端口号portb。同理B也不知道porta和portb。在A向B打洞时A去猜portb,那么porta就会一直变化。同理b向a打洞时也要猜porta,那么portb也要变化。两个都在变,就很难猜了。因此在双方都处在对称的NAT下的话是无法打洞的。
到这感觉P2P通讯就此破产了?,不,此路不通还有他路。

IPV6实现P2P通信

终于在我快要放弃的时候,我突然想到了IPV6,既然IPV4地址不够使得我们都处在NAT下。那IPV6总没有问题把。于是我又动手用IPV6实现了P2P通信。方法也很简单 ,只需要A和B双方都有IPV6地址,同时双方都知道对方的IPV6地址和端口号就可以通信了,为了实现双方都知道对方的IPV6地址和端口号,我同样设计了服务器用来获取A和B的IPV6和端口号并通知对方。如下图:在这里插入图片描述
效果图如下:
1.向服务器注册填写名字

2.服务器返回通讯列表

在这里插入图片描述
3.点击zzz和他通讯
在这里插入图片描述
4.给zzz发送信息并接受zzz发来的信息
在这里插入图片描述
5.zzz的屏幕显示
在这里插入图片描述
因为时间比较仓促而且以及好几年没有碰过安卓开发了,所以界面很丑哈哈哈哈。并且通讯的手机都必须处于wifi模式下才能通讯,如果是4G模式下会出现问题。具体问题我还没有研究出来,可能和运营商有关系。总之暂时先这样吧。如果有知道的小伙伴可以留言讨论。
代码地址:链接:https://pan.baidu.com/s/188SLidW8t–BBBxBz1TMBQ
提取码:gob9

qq_39415224
关注 关注
  • 13
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Qt实现P2P的局域网即时通信系统(附完整源码)
希望我的博客,能帮上你解决学习中工作中所遇到的问题
11-16 197
Qt实现P2P的局域网即时通信系统(附完整源码)
ipv6 socket编程实践
robin912的专栏
04-24 1498
Table of Contents 1. ipv6 socket编程实践 2. 示例程序ipv6 socket编程实践 ip地址长度变化,按照ipv4 255.255.255.255的地址格式,最长为15,加上结束符16字节长度;ipv6 的地址格式为XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX,最长为39,加上结束符为40字节长度。 struct sockadd
UDP打洞,内网穿透(附JAVA代码)
Bob_java的博客
05-19 1581
举个例子:在网页上输入www.baidu.com 就可以打开百度,这是因为知道了域名,域名在DNS服务器上映射到了对应的公网IP,域名是和ip地址绑定的,访问www.xxx.com 等于访问 公网IP,可以使用cmd的ping命令去看返回ip地址。每次发送信息到百度服务器返回的地址其实是nat的地址和端口,然后由nat转发给你的内网地址和端口并生成记录,记录类似于: (nat地址:端口——内网地址:端口)。因为A,B都是内网,无法直接通信,所以需要一个公网服务器 来记录A,B的nat地址和端口。
转:IPv6网络编程步骤(linux)
cloudary的专栏
07-20 980
大家都知道,随着互联网上主机数量的增多,现有的32位IP地址已经不够用了,所以推出了下一代IP地址IPv6,写网络程序的要稍微改变一下现有的网络程序适应IPv6网络是相当容易的事。对于我们来说就是IP地址变化了,所以程序里在用到IP地址的地方做相应的改变就可以了。记住:主要是改变程序里设置IP地址和端口等部分的代码。服务器端源代码如下: #include #include #inc
Android开发之Socket通信
weixin_30745553的博客
05-18 298
博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved !在项目开发过程中,不免要与后台进行交互,这时候我们就需要研究一下通信,做一个选择。Http通信采用UDP,传输过但可能会丢数据,一般我们用来传递字符串或者下载图片时采用,轻量级。Socket通信采用TCP,安全但慢,一般用来做大型项目中注册、定单信息的传输操...
IPv6网络编程代码示例
kayshi的博客
01-07 1381
server端 #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include<netinet/in.h> #include<sys/socket.h> #include...
区块链节点同步Java代码_如何实现一个自己的区块链。第二部分:从不同的节点同步区块链...
weixin_39557797的博客
02-27 872
Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。欢迎来到Jack区块链第二部分,这一部分我将介绍不同节点之间的通信。我原本的目标是写节点之间的同步以及如何与其它节点通信,包括挖矿和对其它节点广播获胜区块信息。最后我发现实现这所有目标的代码量和需要的解释放在这一篇里实在太多。所以我决定在第二部分只对这些话题开个头。读完文章之后你应该能大概了解我做了些什么...
Java角度看区块链实践系列3——P2P网络:区块链P2P网络拓扑结构的演变史
热门推荐
邪恶八进制
03-30 18万+
这一节我将从网络连接开始为大家介绍主流的几种区块链p2p网络结构,让大家对区块链网络更加深层次的理解。 网络连接 网络连接是指网络在应用级的互联。在我们生活中无处不在,比如:人们使用的手机Wifi无限网络、有点交换机网络网络连接类型定义了网络所连接设备的多少和方式,从而决定了网络通信机制。网络连接类型在物理上仅有点到点连接和多点连接两种。 点到点连接 点到点连接,这样的网络只涉及两台设...
基于P2P的局域网即时通信系统(Java版)
06-30
一、设计题目:基于P2P的局域网即时通信系统 二、语言环境:Java 三、已知技术参数和设计要求: 1、实现一个图形用户界面局域网内的消息系统。 2、功能:建立一个局域网内的简单的P2P消息系统,程序既是服务器又是...
精选_基于JAVA和TCP SOCKET实现P2P的局域网即时通信系统_源码打包
03-08
基于JAVA和TCP SOCKET实现P2P的局域网即时通信系统
java实现p2p通信
12-04
课程设计报告,已通过检查,即下即用,简单易懂。
JAVA实现P2P网络通信
06-08
分析了P2P基本概念及其基本工作原理,探讨了用JAVA实现p2p网络通信的技术,并用一个实例作了进一步阐述
java socket 单线程实现P2P通信
05-01
简单大循环 单线程实现通信 1. TCP连接通信 2. Server先监听,等待Client连接 3. 双方都可以发"Stop!"停止通信,但此程序Client只会停止,Server可以一直监听,即断开后,Client可以再次连接 4. 不能一对多通信,只能一对一
java UDP的一个封装
09-19
UDP 在无线通信中用处非常之广, 比如某个终端硬件采集数据, 会使用UDP的方式进行通信。 本文为UDP的一个小程序。 包括两个部分: 1. 在本机监听某个端口, 接受其它PC发过来的数据 2. 本机主动向其它PC的特定端口发送数据 3. 附调试工具 NetAssis
Java Socket实现一个简单的基于P2P范型的即时聊天系统。
04-18
暂时仅仅设计了以下几个功能点: 1.点对点单人聊天; 2.多人在线同时聊天; 3.用户可以自由加入和退出系统; 4.具备用户在线状态监听;
基于Jxta的P2P即时通信软件的Java实现.pdf
最新发布
12-31
基于Jxta的P2P即时通信软件的Java实现.pdf
IPv6网络编程socket, TCP和UDP例子,以及兼容IPV4和IPV6的类
hepeng597的专栏
08-02 5万+
一、TCP socket ipv6与ipv4的区别 服务器端源代码如下: #include #include #include #include #include #include #include #include #include #include #define MAXBUF 1024 int main(int argc, char **argv) { i
android ipv6测试,搭建IPv6本地环境测试App
weixin_42131439的博客
05-27 1761
一、前言最近有很多人都在关注支持IPv6的事情,果然是苹果打个哈欠,iOS行业内就得起一次风暴呀。自从5月初Apple明文规定所有开发者在6月1号以后提交新版本必须需要支持IPv6-Only的网络。二、IPv6-Only支持是什么?IPv6是对IPv4地址空间的扩充,IPv4 和 IPv6的区别就是 IP 地址前者是 .(dot)分割,后者是以 :(冒号)分割的(更多详细信息自行搜索)。目前当我们...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • java实现P2P通信(含安卓实现的基于IPV6的p2p通信代码) 3810
  • java RXTXcomm.jar的串口通信 1665
  • springboot开发---用密文方式登录连接数据库 1066
  • exe和dll的内存加载 675
  • apk反编译教程+工具 437

最新评论

  • java RXTXcomm.jar的串口通信

    牛仔少林: 跳转到香港的网站去解压码!不支持这种做法!

  • java实现P2P通信(含安卓实现的基于IPV6的p2p通信代码)

    je鸡儿: 4G模式下会出问题多半是因为,手机移动网络是一般是限制ipv6入站的,出站没问题(不过不同地方基站设置的规则不同,也不一定限制)

  • java实现P2P通信(含安卓实现的基于IPV6的p2p通信代码)

    唔66: 表情包下载链接失效了表情包

  • java RXTXcomm.jar的串口通信

    m0_72644690: 可以 我解压了 提示资源失效 然后跳转页面 让注册东西 可以可以

  • springboot开发---用密文方式登录连接数据库

    CSDN-Ada助手: 不知道 云原生入门 技能树是否可以帮到你:https://edu.csdn.net/skill/cloud_native?utm_source=AI_act_cloud_native

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • springboot开发---用密文方式登录连接数据库
  • exe和dll的内存加载
  • apk反编译教程+工具
2022年1篇
2021年1篇
2020年1篇
2019年2篇

目录

目录

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

哆哆女性网好妻子演员表五兄弟结拜起名姓聂女孩起名大全王亚平女儿出征现场为妈妈加油lsasecretsview1.21100个男孩起名好听的公司起名开店起名打分免费免费小说书城属牛人的宝宝起名姓周起名注意什么景起名续写穷人生辰八字起公司名免费起公司名称公司起名带土带水属牛宝宝起名宜用的字有哪些珍惜粮食汽车之家2019最新报价姓史的女宝宝起名大全美名腾宝宝起名起名李云什么男孩951王氏起名字大全男宝二手房中介公司宝宝起名名字大全生辰八字长缨在手给水排水制图标准山海情剧情潘氏女儿高分起名大全淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻不负春光新的一天从800个哈欠开始有个姐真把千机伞做出来了国产伟哥去年销售近13亿充个话费竟沦为间接洗钱工具重庆警方辟谣“男子杀人焚尸”男子给前妻转账 现任妻子起诉要回春分繁花正当时呼北高速交通事故已致14人死亡杨洋拄拐现身医院月嫂回应掌掴婴儿是在赶虫子男孩疑遭霸凌 家长讨说法被踢出群因自嘲式简历走红的教授更新简介网友建议重庆地铁不准乘客携带菜筐清明节放假3天调休1天郑州一火锅店爆改成麻辣烫店19岁小伙救下5人后溺亡 多方发声两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#青海通报栏杆断裂小学生跌落住进ICU代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了当地回应沈阳致3死车祸车主疑毒驾武汉大学樱花即将进入盛花期张立群任西安交通大学校长为江西彩礼“减负”的“试婚人”网友洛杉矶偶遇贾玲倪萍分享减重40斤方法男孩8年未见母亲被告知被遗忘小米汽车超级工厂正式揭幕周杰伦一审败诉网易特朗普谈“凯特王妃P图照”考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼恒大被罚41.75亿到底怎么缴男子持台球杆殴打2名女店员被抓校方回应护栏损坏小学生课间坠楼外国人感慨凌晨的中国很安全火箭最近9战8胜1负王树国3次鞠躬告别西交大师生房客欠租失踪 房东直发愁萧美琴窜访捷克 外交部回应山西省委原副书记商黎光被逮捕阿根廷将发行1万与2万面值的纸币英国王室又一合照被质疑P图男子被猫抓伤后确诊“猫抓病”

哆哆女性网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化