计算机通讯:数据从一个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