停止等待arq协议:
停止等待协议(stop-and-wait)是最简单但也是最基础的数据链路层协议。很多有关协议的基本概念都可以从这个协议中学习到。“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
由于这里我们采用的是端对端的传输,所以我们并没有采用tcp进行流式套接字的传输,而是采用udp,进行的数据报的传输。
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17
UDP协议全称是用户数据报协议[1] ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。
与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
再解释说明一下,由于系统层面的原因,会有自动校验的措施,而且因为我做的是本机的测试,出错率也会很低,所以我们需要模拟出错的情况。我们利用随机数的算法,做了一个模拟的随机数算法:
package UDP;
/**
* Created by linSir on 16/10/5.用来模拟传输过程(udp)
*/
public class ImitateTransfer {
/**
* 用来模拟传输过程中的各种情况
* 1.无差错情况(60%) 状态码:100
* 2.出现差错
* ①.接收到了,做校验时发现有错(20%) 状态码:101
* ②.传输过程中丢失数据(20%) 状态码:102
* 3.确认丢失和确认迟到
* ①.确认丢失(20%)状态码:103
* ②.确认迟到(20%)状态码:104
*/
public static String transfer_send() {//发送数据的三种情况
int x = (int) (Math.random() * 10);//0-9
switch (x) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
return "100";
case 6:
case 7:
return "101";
case 8:
case 9:
return "102";
}
return "100";
}
public static String transfer_confirm() {//发送确认命令的三种情况
int x = (int) (Math.random() * 10);//0-9
switch (x) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
return "100";
case 6:
case 7:
return "103";
case 8:
case 9:
return "104";
}
return "100";
}
}
这样我们就做完了,模拟出错的情况。下面我们看一下代码,再解释:
客户端A(负责发送):
package UDP;
import 实验二.ImitateTransfer;
import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.net.*;
import java.util.Timer;
class Window extends JFrame {
private static JTextArea showArea;
private JTextField inputField;
private static String IPadress;
public static String[] datas = new String[1000];
public static Timer timer = new Timer();
public static int m = 0;
public static void sentData(String Message) {
byte[] dataarr = new byte[100010];
dataarr = Message.getBytes();
try {
Thread.currentThread().sleep(500);//毫秒
} catch (Exception e) {
}
try {
InetAddress sentIP = InetAddress.getByName(IPadress);
DatagramSocket dsset = new DatagramSocket(62000);
DatagramPacket dprec = new DatagramPacket(dataarr, dataarr.length, sentIP, 64650);
System.out.println(sentIP);
// 从己方62000 端口发送到客户端64650端口
dsset.send(dprec);
dsset.close();
timer1(1);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void recieveData() {
byte[] dataarr = new byte[100010];
try {
DatagramSocket dsset = new DatagramSocket(63300);
// 监听己方63300端口收到的消息
DatagramPacket dprec = new DatagramPacket(dataarr, dataarr.length);
dsset.receive(dprec);
dsset.close();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append(format.format(new Date()) + "\n" + "新消息: " + new String(dataarr).trim() + "\n");
String content2 = new String(dataarr).trim().split("\\(")[1].split("\\)")[0];
System.out.println(content2);
switch (content2) {
case "成功接受":
timer1(0);
m++;
String type = "";
type = ImitateTransfer.transfer_send();
switch (type) {
case "100":
type = "成功发送";
break;
case "101":
type = "做校验时出错";
break;
case "102":
type = "传送时丢失";
break;
}
sentData("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (正常发送)");
showArea.append("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (正常发送)" + "\n");
break;
case "确认丢失":
break;
case "确认超时":
break;
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startChat() {
for (int i = 0; i < datas.length; i++) {
datas[i] = String.valueOf(i);
}
JLabel displyjLabel = new JLabel("客户端A");
displyjLabel.setBounds(150, 10, 100, 15);
JPanel myJPanel = new JPanel();
myJPanel.setLayout(null);
this.setContentPane(myJPanel);
myJPanel.add(displyjLabel);
showArea = new JTextArea();
showArea.setLineWrap(true);
JScrollPane scrollpane = new JScrollPane(showArea);
scrollpane.setBounds(20, 30, 350, 350);
scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED, Color.CYAN, Color.BLUE, null, null));
scrollpane.setVisible(true);
inputField = new JTextField();
inputField.setBounds(20, 410, 280, 25);
inputField.setVisible(true);
myJPanel.add(scrollpane);
JButton mybutton = new JButton("发送");
mybutton.setBounds(310, 410, 60, 25);
myJPanel.add(mybutton);
myJPanel.add(inputField);
myJPanel.setVisible(true);
JButton quitjButton = new JButton("退出");
quitjButton.setBounds(250, 450, 100, 30);
myJPanel.add(quitjButton);
quitjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.exit(0);
}
});
JButton returnjButton = new JButton("返回主界面");
returnjButton.setBounds(20, 450, 100, 30);
myJPanel.add(returnjButton);
returnjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
initWindow();
}
});
this.setVisible(true);
inputField.requestFocus();
inputField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyChar() == '\n') {
String type = "";
type = ImitateTransfer.transfer_send();
switch (type) {
case "100":
type = "成功发送";
break;
case "101":
type = "做校验时出错";
break;
case "102":
type = "传送时丢失";
break;
}
//A发送: 数据为:1 类型为:成功发送; (开始发送)
sentData("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (开始发送)");
inputField.setText("");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (开始发送)" + "\n");
//sendMessage("A(发送)-" + datas[m] + "-" + 实验二.ImitateTransfer.transfer_send() + "---开始发送");
}
}
});
mybutton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 当发送键被按下
String type = "";
type = ImitateTransfer.transfer_send();
switch (type) {
case "100":
type = "成功发送";
break;
case "101":
type = "做校验时出错";
break;
case "102":
type = "传送时丢失";
break;
}
//A发送: 数据为:1 类型为:成功发送; (开始发送)
sentData("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (开始发送)");
inputField.setText("");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (开始发送)" + "\n");
//sendMessage("A(发送)-" + datas[m] + "-" + 实验二.ImitateTransfer.transfer_send() + "---开始发送");
}
});
}
public void initWindow() {
JPanel myjPanel = new JPanel();
myjPanel.setLayout(null);
this.setContentPane(myjPanel);
JLabel myjLabel = new JLabel("欢迎使用本聊天程序");
myjLabel.setBounds(50, 100, 300, 40);
myjLabel.setForeground(Color.cyan);
myjLabel.setFont(new Font("HirakakuProN-W6", Font.BOLD, 30));
JLabel tishiJLabel = new JLabel("请输入对方的IP地址:");
tishiJLabel.setBounds(15, 300, 150, 20);
final JTextField ipJTextField = new JTextField("127.128.0.1");
ipJTextField.setBounds(150, 300, 115, 20);
JButton okJButton = new JButton("确定");
okJButton.setBounds(280, 300, 70, 20);
ipJTextField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyChar() == '\n') {
startChat();
}
}
});
okJButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
IPadress = ipJTextField.getText();
startChat();
}
});
myjPanel.add(tishiJLabel);
myjPanel.add(myjLabel);
myjPanel.add(ipJTextField);
myjPanel.add(okJButton);
JButton quitjButton = new JButton("退出");
quitjButton.setBounds(150, 350, 100, 30);
myjPanel.add(quitjButton);
quitjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.exit(0);
}
});
this.setVisible(true);
}
public Window() {
this.setBounds(420, 100, 400, 550);
this.setLayout(null);
this.setTitle("客户端A");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initWindow();
while (true) {
recieveData();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Window();
}
public static void timer1(int q) {
if (q == 1) {
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
String type = "";
type = ImitateTransfer.transfer_send();
switch (type) {
case "100":
type = "成功发送";
break;
case "101":
type = "做校验时出错";
break;
case "102":
type = "传送时丢失";
break;
}
sentData("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (超时重发)");
showArea.append("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (超时重发)" + "\n");
}
}, 2000);
}
if (q == 0) {
timer.cancel();
}
}
}
客户端B(负责接收):
package UDP;
import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.net.*;
class Window2 extends JFrame {
private JTextArea showArea;
private JTextField inputField;
private String IPadress;
public void sentData(String Message) {
byte[] dataarr = new byte[100010];
try {
Thread.currentThread().sleep(500);//毫秒
} catch (Exception e) {
}
dataarr = Message.getBytes();
try {
InetAddress sentIP = InetAddress.getByName(IPadress);
DatagramSocket dsset = new DatagramSocket(60010);
DatagramPacket dprec = new DatagramPacket(dataarr, dataarr.length, sentIP, 63300);
// 从己方60010端口发送到对方63300端口
dsset.send(dprec);
dsset.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void recieveData() {
byte[] dataarr = new byte[100010];
try {
DatagramSocket dsset = new DatagramSocket(64650);
DatagramPacket dprec = new DatagramPacket(dataarr, dataarr.length);
dsset.receive(dprec);
dsset.close();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append(format.format(new Date()) + "\n" + "新消息: " + new String(dataarr).trim() + "\n");
String a_type = new String(dataarr).trim().split(":")[3].split(";")[0];
String content = new String(dataarr).trim().split(":")[2].split("类")[0];
//sentData("A发送: 数据为:" + datas[m] + "类型为:" + type + "; (开始发送)");
String type = "";
type = ImitateTransfer.transfer_confirm();
switch (type) {
case "100":
type = "成功接受";
break;
case "103":
type = "确认丢失";
break;
case "104":
type = "确认超时";
break;
}
switch (a_type) {
case "成功发送":
sentData("B发送:" + "收到的数据为:" + content + " 类型(" + type + ") ");
break;
case "做校验时出错":
break;
case "传送时丢失":
break;
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startChat() {
JLabel displyjLabel = new JLabel("客户端B");
displyjLabel.setBounds(150, 10, 100, 15);
JPanel myJPanel = new JPanel();
myJPanel.setLayout(null);
this.setContentPane(myJPanel);
myJPanel.add(displyjLabel);
showArea = new JTextArea();
showArea.setLineWrap(true);
JScrollPane scrollpane = new JScrollPane(showArea);
scrollpane.setBounds(20, 30, 350, 350);
scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED, Color.CYAN, Color.BLUE, null, null));
scrollpane.setVisible(true);
inputField = new JTextField();
inputField.setBounds(20, 410, 280, 25);
inputField.setVisible(true);
myJPanel.add(scrollpane);
JButton mybutton = new JButton("发送");
mybutton.setBounds(310, 410, 60, 25);
myJPanel.add(mybutton);
myJPanel.add(inputField);
myJPanel.setVisible(true);
JButton quitjButton = new JButton("退出");
quitjButton.setBounds(250, 450, 100, 30);
myJPanel.add(quitjButton);
quitjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.exit(0);
}
});
JButton returnjButton = new JButton("返回主界面");
returnjButton.setBounds(20, 450, 100, 30);
myJPanel.add(returnjButton);
returnjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
initWindow();
}
});
this.setVisible(true);
inputField.requestFocus();
inputField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyChar() == '\n') {
String Message = inputField.getText().trim();
sentData(Message);
inputField.setText("");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append(format.format(new Date()) + "\n" + "我发送: " + Message + "\n");
}
}
});
mybutton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 当发送键被按下
String Message = inputField.getText().trim();
sentData(Message);
inputField.setText("");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
showArea.append(format.format(new Date()) + "\n" + "我发送: " + Message + "\n");
}
});
}
public void initWindow() {
JPanel myjPanel = new JPanel();
myjPanel.setLayout(null);
this.setContentPane(myjPanel);
JLabel myjLabel = new JLabel("欢迎使用本聊天程序");
myjLabel.setBounds(50, 100, 300, 40);
myjLabel.setForeground(Color.cyan);
myjLabel.setFont(new Font("HirakakuProN-W6", Font.BOLD, 30));
JLabel tishiJLabel = new JLabel("请输入对方的IP地址:");
tishiJLabel.setBounds(15, 300, 150, 20);
final JTextField ipJTextField = new JTextField("127.128.0.1");
ipJTextField.setBounds(150, 300, 115, 20);
JButton okJButton = new JButton("确定");
okJButton.setBounds(280, 300, 70, 20);
ipJTextField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyChar() == '\n') {
startChat();
}
}
});
okJButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
IPadress = ipJTextField.getText();
startChat();
}
});
myjPanel.add(tishiJLabel);
myjPanel.add(myjLabel);
myjPanel.add(ipJTextField);
myjPanel.add(okJButton);
JButton quitjButton = new JButton("退出");
quitjButton.setBounds(150, 350, 100, 30);
myjPanel.add(quitjButton);
quitjButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.exit(0);
}
});
this.setVisible(true);
}
public Window2() {
this.setBounds(420, 100, 400, 550);
this.setLayout(null);
this.setTitle("客户端B");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initWindow();
while (true) {
recieveData();
System.out.println("-----");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Window2();
}
}
相信,有很多人,已经看懂了,这个程序了,先声明一下,本程序是我在上网找的,只是修改了一点点。
这个程序说的就是,一个java的可视化界面+一个发送数据的函数+一个接收数据的函数,无论发送还是接收,我都做了500毫秒的延迟,用来模拟在路上的时间,也都需要指定ip和端口号,就可以从这个端口发送到对方的端口了,对方只需要监视自己的端口就可以了。
然后,在发送数据的过程中,加了一个利用随机数产生的。
发送的类型,有三种,分别是:1.成功数据发送 2.发送的数据做校验会出错 3.发送的数据超时 。
接收的类型也有三种,分别是:1.发送确认成功 2.发送确认超时
3.发送确认丢失
当A向B发送数据时,B会判断,如果是出错或者超时,B不会做任何处理,这个时候便会触发A的超时计时器,重传。
当B向A发送确认时,如果确认超时,或者确认丢失,A都会重新向B发送正确的数据。
如果A接收到B的一次成功接收的指令,就会将数据向下滚动一位,传一下数据。
超时定时器(函数timer1)也很好理解,只是让它两秒的等待,然后去重新发送数据,如果在两秒内,收到了确认了,成功接收的指令,就会关闭这个超时定时器。并且再向B发送一个数据,同时再打开超时计时器就可以了。
当然,操作也非常简单,只需要在,客户端A的输入窗口,敲一下回车,或者点击一下发送就行,内容是自动填充的,不用手动输入。
效果图:



大概这个模拟的过程就是这个样子,当然其中肯定有很多,代码规范,代码格式,代码复用做的很不好的地方~ 欢迎大家提意见,我都会改正的~
网友评论