飞道的博客

1.网络编程UDP

385人阅读  评论(0)

计算机通讯:数据从一个IP的port出发(发送方),运输到另一个IP的port(接收方)

端口:0~65535

  • 0~1023已经被OS(操作系统)占用了,80是Web,23是telnet
  • 1024~65535,一般程序可使用(谨防冲突)
  • 两台机器通讯就是在IP+Port上进行的
  • 在Windows/Linux/Mac上都可以通过netstat -an来查询(netstat -an命令:统计当前这个网卡和外部之间的数据交换的数据netstat=network(网络)+statistics(统计))
  • 保留ip:127.01.01本机
  • 公网:(万维网/互联网)和内网(局域网)
  • 网络是分层的
  • 最外层是公网/互联网
  • 底下的每层都是内网
  • ip地址可以在每个层次的网重用
  • tracert看当前机器和目标机器的访问中继

我们关心的是数据传输在传输层,而传输层基本上就是TCP和UDP这两个协议。

UDP

  • 用户数据报协议,面向无连接协议
  • 不保证可靠的数据传输
  • 速度快,也可以在较差网络下使用
  • 发生方发送消息,如果接收方刚好在目的地,则可以接受。若不在,那么这个消息就丢失了
  • 发生方也无法得知是否发送成功
  • UDP的好处就是简单,节省,经济

DatagramSocket: 通讯的数据管道

  • send和receive方法
  • (可选,多网卡)绑定一个IP和Port

DatagramPacket

  • 集装箱:封装数据
  • 地址标签:目的地IP+Port

实例

  • 无主次之分
  • 接收方必须早于发起方执行

import java.net.*;
public class UdpRecv
{
	public static void main(String[] args) throws Exception
	{
		//建立接收方和发送方数据管道
		DatagramSocket	ds=new DatagramSocket(3000);//定义本机的3000端口
		byte [] buf=new byte[1024];                 //字节数组
		DatagramPacket dp=new DatagramPacket(buf,1024);//集装箱,用来封装数据
		
		System.out.println("UdpRecv: 我在等待信息");
		ds.receive(dp);
		System.out.println("UdpRecv: 我接收到信息");
		String strRecv=new String(dp.getData(),0,dp.getLength()) +
		" from " + dp.getAddress().getHostAddress()+":"+dp.getPort(); 
		System.out.println(strRecv);
		
		Thread.sleep(1000);//睡眠一秒钟,换我发送消息
		System.out.println("UdpRecv: 我要发送信息");
		String str="hello world 222";//要发送的数据
		//贴标签,虽然端口未知,但上一次联系过,通过dp.getPort()可以获取到61421端口
		DatagramPacket dp2=new DatagramPacket(str.getBytes(),str.length(), 
				InetAddress.getByName("127.0.0.1"),dp.getPort());
		ds.send(dp2);//解除UdpSend的ds.receive(dp2)阻塞状态
		System.out.println("UdpRecv: 我发送信息结束");
		ds.close();
	}
}


12行输出我在等待信息。13行的 receive就在等待消息过来,如果有信息过来数据就封装在dp里面,若没有消息过来13行会一直阻塞在这里,一直等,等到有消息过来为止。

运行结果:

程序没有结束,但12行输出了,14行没有输出,说明程序在13行阻塞了(没有收到消息)。

import java.net.*;
public class UdpSend
{
	public static void main(String [] args) throws Exception
	{
		DatagramSocket ds=new DatagramSocket();
		String str="hello world";//要发送的数据
		//集装箱,对str字符串数据封装,包括内容和长度
		DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),
				InetAddress.getByName("127.0.0.1"),3000);
		//在集装箱的头部贴纸条InetAddress.getByName("127.0.0.1"),3000,表明把这个集装箱
		//发送到127.0.0.1这台机器(本机)的3000端口
		System.out.println("UdpSend: 我要发送信息");
		ds.send(dp);                              //解除UdpRecv里面ds.receive(dp);行的阻塞状态
		System.out.println("UdpSend: 我发送信息结束");
		
		Thread.sleep(1000);//休眠一秒钟,换我接收消息
		byte [] buf=new byte[1024];//字节数组
		DatagramPacket dp2=new DatagramPacket(buf,1024);
		System.out.println("UdpSend: 我在等待信息");
		ds.receive(dp2);
		System.out.println("UdpSend: 我接收到信息");
		String str2=new String(dp2.getData(),0,dp2.getLength()) +
				" from " + dp2.getAddress().getHostAddress()+":"+dp2.getPort(); 
		System.out.println(str2);
				
		ds.close();
	}
}

UdpSend运行结果

有两个控制台,运行UdpSend时候UdpRecv也在运行。

UdpRecv运行结果

hello world from 127.0.0.1:61421 说明接收方收到的数据hello world 来自机器127.0.0.1(本机)的61421 端口。虽然61421 端口在程序里面没有写,但客户端UdpSend会随机选一个端口连过来,服务端UdpRecv捆绑在3000端口的。

在命令行下执行这两个程序
1、打开命令行窗口

2、复制项目地址:选中项目右键–>properties复制Location的内容D:\Code\JavaEclipse\源代码-进阶\MOOC18-02

3、跳转到其他驱动器的其他文件夹

当前在C盘,要跳转到D:\Code\JavaEclipse\源代码-进阶\MOOC18-02目录 cd /d D:\Code\JavaEclipse\源代码-进阶\MOOC18-02

cd bin进入bin的子目录
dir列出当前路径的所有子目录

从中可以看到编译出来的UdpRecv.class和UdpSend.class类
4、接下来执行UdpRecv

从中可以看出UdpRecv程序卡在3000端口上了

5、与此同时,再打开一个命令行窗口
输入netstat -an

提示不是内部或外部命令等原因是,cmd当前操作不在系统文件夹下。需要将当前操作路径切换到Windows操作系统的系统文件夹下,输入cd C:\Windows\system32

向下滑动可以看到一个UDP协议的端口3000

有个程序在3000端口等待别人连上来,第三列 *: *代表任意的地方都可以连过来
6、进入项目里启动UdpSend

这个实例完成两个简短的不同程序之间的基于UDP的数据交换,类似于一个原始版的QQ。一边各起一个应用程序,然后双方各发送和接收一条消息。

*注意:*尽量让接收方先启动,若发送方先启动,那么可能发过去一个信息,接收方没有接收,这条信息就出错了

示例:
先执行UdpSend


UdpRecv还在等待接收消息,但UdpSend已经发送了(但没有被接收消息就不见了)。现在双方都在等消息,陷入无限等待环节。


转载:https://blog.csdn.net/qq_44781756/article/details/106164288
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场