本文最后更新于:星期二, 八月 2日 2022, 9:32 晚上
实验二
一、实验目的
尝试实现一个无连接的数据报Socket进程间通信(UDP)
二、实验内容
- 创建两个进程,使用无链接的数据报Socket实现交换一个字符串。一个叫做Sender.java ,用于向一个名为Receiver.java的进程发送一个字符串。
- Sender.java需要一个命令行参数,用于表示传递消息的端口号.
- Receiver.java 需要一个命令行参数用于表示接受消息的端口号。
- 接受方需要阻塞直到从发送方收到一个消息。如果发送放在接受运行之前发送出消息,消息会丢失。这种情况在此实验中是允许的。
- 消息缓冲可以是定长的。如果发送的消息比消息缓冲长,接受方就看不到完整的消息。这种情况在此实验中是允许的。例如,消息缓冲的长度是5,而发送方发送一个消息 “123456789”,则接受方只能看到“12345”。
三、实验设计
实验背景:
UDP简介
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。
UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
什么时候应该使用UDP:
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。
比如,日常生活中,常见使用UDP协议的应用如下:
QQ语音、QQ视频……
Java中的UDP
在Java中,实现UDP连接和数据传输的类主要是DatagramPacket和DatagramSocket。
DatagramSocket类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。在接收端(Receiver),DatagramSocket只需输入空闲端口即可初始化对象。在消息发送端(Sender),DatagramSocket不需要任何参数初始化,直接新建对象。
DatagramSocket对象拥有send()和receive()方法,参数是DatagramPacket对象,即发送和接收数据包。
DatagramPacket类表示数据报包。
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
发送方(sender)要负责在数据报包上面贴好标签,注明收信人(地址和端口),这样才能够准确地被收信人(receiver)收到。
在DatagramPacket包中的函数 int getLength()返回实际接受的字节数,
byte[] getData()返回接受到的数据。
要想接受端给发送端回信息,就需要知道发送端的IP地址InetAddress getAddress()和发送端进程所绑定的端口号int getPort()。
数据报套接字发送成功之后,就相当于建立了一个虚连接,双方可以发送数据。
实验环境:
Intellij IDEA, JDK 1.8, Windows 10
实验思路:
- 新建两个类,Sender和Receiver。由于逻辑简单,具体代码可直接在main中实现;
- Sender和Receiver类需要接收命令行参数,可利用Intellij IDEA的类运行设置来输入参数,利用main函数的args参数来传递命令行参数,避免了操作命令行界面的不便;
- Sender:定义地址、端口号、数据;创建数据报包,包含发送的数据信息;创建DatagramSocket对象;向服务器发送数据报包。
- Receiver:创建服务器端DatagramSocket,指定端口;创建数据报,用于接收客户端发送的数据;接收客户端发送的数据;读取数据。
相关代码:
Sender.java
public class Sender {
public static void main(String[] args) throws IOException {
/*
* 向Receiver发送数据
* java Sender 192.168.1.101 12 hello
*/
// 1.定义服务器的地址、端口号、数据
if (args.length != 3) {
System.out.println("参数个数不正确!" + args.length);
return;
}
System.out.println(args[0]);
System.out.println(args[1]);
System.out.println(args[2]);
InetAddress address = InetAddress.getByName(args[0]);
//InetAddress address = InetAddress.getByName("localhost");
int port = Integer.parseInt(args[1]);
byte[] data = args[2].getBytes();
// 2.创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// 3.创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
// 4.向服务器端发送数据报
socket.send(packet);
}
}
Receiver.java
public class Receiver {
/*
* 接收Sender发送的数据
* java Receiver 12
*/
public static void main(String[] args) throws IOException {
// 设置缓冲区大小
int bufferLength = 5;
// 设置参数格式
if (args.length != 1) {
System.out.println("参数个数不正确!" + args.length);
return;
}
// 1.创建服务器端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(Integer.parseInt(args[0]));
// 2.创建数据报,用于接收客户端发送的数据
byte[] data = new byte[bufferLength];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3.接收客户端发送的数据
System.out.println("****服务器端已经启动,等待客户端发送数据");
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞
// 4.读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端说:" + info);
}
}
四、实验结果与分析
编译过程略去。
- 运行Reciever
- 运行Sender
- 此时的Receiver
实验分析:
缓冲区大小可以修改。修改后的缓冲区大小为5,可以看到,Sender中的消息Hello!
发送给Receiver后,尾部的感叹号被削去。
通过本次实验,我了解了UDP连接的原理与优缺点,掌握了在Java中建立UDP连接的方法。
实验三
一、实验目的
尝试通过面向流模式的socket实现通信。
二、实验内容
- 创建一个名为Acceptor.java的程序。此程序可以接受一个连接并用流模式socket接受一个消息。创建一个名为 Requestor.java 的程序。此程序可以请求一个连接,并使用流模式socket。
- Acceptor.java 有2个命令行参数,分别用于表示本进程使用的服务器socket的端口号,以及要发送的消息。
- Requestor.java 有2个命令行参数,分别表示连接acceptor的主机名和连接acceptor的端口号。
三、实验设计
实验背景:
TCP简介
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
Java Socket简介
所谓socket 通常也称作”套接字“,用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。
Java Socket常用方法
. Accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。
. getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。
. getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。
注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。
Java 操作流对象
建立Socket连接后,两台机器之间便以流模式进行通信。在Java API中,可以从其中读入一个字节序列的对象称做输入流,而可以向其中写入一个字节序列的对象称做输出流。这些字节序列的来源地和目的地可以是文件,而且通常都是文件,但是也可以是网络连接,甚至是内存块。抽象类InputStream和OutputStream构成了输入/输出(I/O)类层次结构的基础。
本次实验我选择利用Scanner作为读取流的手段,用PrintWriter作为写入流的手段。
实验环境:
Intellij IDEA, JDK 1.8, Windows 10
实验思路:
- 新建两个类,ConnectionAcceptor和ConnectionRequestor。由于逻辑简单,具体代码可直接在main中实现;
- ConnectionAcceptor和ConnectionRequestor类需要接收命令行参数,可利用Intellij IDEA的类运行设置来输入参数,利用main函数的args参数来传递命令行参数,避免了操作命令行界面的不便;
- ConnectionRequestor:建立连接;接收数据;发送数据;关闭连接。
- ConnectionAcceptor:接收请求;发送数据;接收数据;断开连接。
相关代码:
ConnectionRequestor.java
public class ConnectionRequestor {
/*
* java ConnectionRequestor 192.168.1.101 12
*/
public static void main(String[] args) throws IOException {
// 检查参数个数
if (args.length != 2) {
System.out.println("Wrong parameters!");
return;
}
// 1. 建立连接
String host = args[0];
int port = Integer.parseInt(args[1]);
Socket socket = new Socket(host, port);
// 2. 接收数据
InputStream inputStream = socket.getInputStream();
Scanner scanner = new Scanner(inputStream);
String line = scanner.nextLine();
System.out.println("收到来自发送方的消息:" + line);
// 3. 发送数据
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(outputStream, true);
printWriter.println("我已收到消息,内容是" + line);
// 4. 关闭连接
socket.close();
}
}
ConnectionAcceptor.java
public class ConnectionAcceptor {
/*
* java ConnectionAcceptor 12 Hello
*/
public static void main(String[] args) throws IOException {
// 检查参数个数
if (args.length != 2) {
System.out.println("Wrong parameters!");
return;
}
// 1. 接受请求
int port = Integer.parseInt(args[0]);
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
// 2. 发送数据
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(outputStream, true);
printWriter.println(args[1]);
// 3. 接收数据
InputStream inputStream = socket.getInputStream();
Scanner scanner = new Scanner(inputStream);
String line = scanner.nextLine();
System.out.println("服务器应答:" + line);
// 4. 断开连接
socket.close();
}
}
四、实验结果与分析
编译过程略去。
- 运行ConnectionAcceptor
- 运行ConnectionRequestor
- 此时的ConnectionAcceptor
实验分析:
通过本次实验,我了解了TCP连接的原理与优缺点,掌握了TCP和UDP的主要不同之处,掌握了在Java中建立TCP Socket连接的方法,掌握了流处理的基本方法。
实验四
一、实验目的
创建进程之间的多播。
二、实验内容
- MulticastSender.java用于发送多播消息给多播接收程序。多播IP地址是239.1.2.3 端口号为1234。
- MulticastReceiver.java 用于接收多播消息并显示消息。
- 实验最终效果要求:至少开启两个以上的MulticastReceiver进程,MulticastSender发送的消息,均可被MulticastReceiver收到。
三、实验设计
实验背景:
java————多播编程——-MulticastSocket
单播:
一个单个的发送者和一个接受者之间通过网络进行的通信。
1、服务器及时响应客户机的请求
2、服务器针对每个客户不同的请求发送不同的数据,容易实现个性化服务。
多播:
一个发送者和多个接受者之间的通信。
广播特点:主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要)。
1、网络设备简单,维护简单,布网成本低廉。
2、由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低。
多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址。
多播程序设计的框架
要进行多播的编程,需要遵从一定的编程框架。多播程序框架主要包含套接字初始化、设置多播超时时间、加入多播组、发送数据、接收数据以及从多播组中离开几个方面。其步骤如下:
(1)建立一个socket。
(2)然后设置多播的参数,例如超时时间TTL、本地回环许可LOOP等。
(3)加入多播组。
(4)发送和接收数据。
(5)从多播组离开。
Java MulticastSocket
多播通过多播数据报套接MulticastSocket类来实现
重要的构造方法:
MulticastSocket()//创建多播套接字
MulticastSocket(int port)//创建多播套接字并将其绑定到特定端口
MulticastSocket(SocketAddress bindaddr)//创建绑定到指定套接字地址的MulticastSocket
常用的方法:
void joinGroup(InetAddress meastaddr)//加入多播组
void leaveGroup(InetAddress meastaddr)//离开多播组
void send(DatagramPacket p)//从此套接字发送数据包
public void receive(DatagramPacket p)//从此套接字接收数据包
实验环境:
Intellij IDEA, JDK 1.8, Windows 10
实验思路:
- 新建两个类,MulticastSender和MulticastReciever。由于逻辑简单,具体代码可直接在main中实现;
- 为了体现多播的广播特性,MulticastReciever的进程要多几个,随着MulticastSender的启动,全部收到信息才行。
- MulticastSender:新建多播连接;构造数据包;广播数据;关闭连接。
- MulticastReciever:新建多播连接;构造数据包;接收数据;关闭连接。
相关代码:
MulticastSender.java
public class MulticastSender {
public static void main(String[] args) throws IOException {
// 新建多播连接
int port = 1234;
String address = "239.1.2.3";
MulticastSocket multicastSocket = new MulticastSocket(port);
InetAddress groupAddress = InetAddress.getByName(address);
multicastSocket.joinGroup(groupAddress);
// 构造数据包
byte[] message = "Hello!".getBytes();
DatagramPacket datagramPacket = new DatagramPacket(message, message.length, InetAddress.getByName(address), port);
// 广播数据
multicastSocket.send(datagramPacket);
// 关闭连接
multicastSocket.close();
}
}
MulticastReciever.java
public class MulticastReciever {
public static void main(String[] args) throws IOException {
// 新建多播连接
int port = 1234;
String address = "239.1.2.3";
MulticastSocket multicastSocket = new MulticastSocket(port);
InetAddress groupAddress = InetAddress.getByName(address);
multicastSocket.joinGroup(groupAddress);
// 构造数据包
byte[] message = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(message, message.length, InetAddress.getByName(address), port);
// 接收数据
multicastSocket.receive(datagramPacket);
System.out.println("接收到了广播消息:" + new String(message));
// 关闭连接
multicastSocket.close();
}
}
四、实验结果与分析
编译过程略去。
- 运行MulticastReciever(6个)
- 运行MulticastSender
- 此时的MulticastReciever
剩下三个略去不表,结果相同。
实验分析:
通过本次实验,我了解了多播的原理和相对于单播的优点,掌握了在Java中建立多播MulticastSocket连接的方法。
record distribute system OS java
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!