📄 comchannel.java
字号:
//: ComChannel.java
//: 模拟实现全双工通信信道
//package classes;
import java.io.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
class ComChannel
{
private boolean isUsable; // 该信道是否可用的标志
private PipedOutputStream dataIn; // 数据传输信道输入端
private PipedInputStream dataOut; // 数据传输信道输出端
private ObjectOutputStream dataWrite; // 负责将帧写入数据传输信道
private ObjectInputStream dataRead; // 负责将帧读出数据传输信道
private ACKChannel ackChannel; // 负责确认帧得传输
private ChannelPanel panel; // 演示面板
private int dataNum; // 通过管道的数据帧的数目
ComChannel(ChannelPanel panel)
{
this.panel = panel; // 参数传递
try
{
ackChannel= new ACKChannel();
isUsable = true;
dataNum = 0;
dataIn = new PipedOutputStream();
dataOut = new PipedInputStream(dataIn);
dataWrite = new ObjectOutputStream(dataIn);
dataRead = new ObjectInputStream(dataOut);
}
catch(Exception e)
{
isUsable = false;
e.printStackTrace();
}
}
// 判断该信道是否可用
public boolean isUsable()
{
if(ackChannel.isUsable()==false)
{
return false;
}
return isUsable;
}
// 开始通信
public void start()
{
dataNum = 0;
ackChannel.start(); // 通知确认帧接收信道
panel.start(); // 通知演示面板
}
// 关闭该信道,关闭成功返回true,否则返回false
public boolean close()
{
try
{
if(ackChannel.close()==false)
{
return false;
}
dataIn.close();
dataOut.close();
dataWrite.close();
dataRead.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
// 将数据放入信道,若成功返回true,否则返回false
synchronized public boolean pushData(DataFrame frame)
{
//System.out.println("pushData()");//!!!!!!!!!!!!!!!!!!!!!!!!!!
try
{
dataWrite.writeObject(frame);
dataWrite.flush();
panel.push(frame); // 通知演示面板
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
// 将确认放入信道,若成功返回true,否则返回false
public boolean pushAck(DataFrame frame)
{
if(ackChannel.pushAck(frame)==false)
{
return false;
}
return true;
}
// 从信道中取出数据,若读取失败返回null
public DataFrame popData()
{
DataFrame frame = null;
try
{
//System.out.println("dataNum="+dataNum);//!!!!!!
if(dataNum%32==26) // 制造超时
{
frame = (DataFrame)dataRead.readObject();
panel.pop(frame); // 通知演示面板
dataNum++;
}
frame = (DataFrame)dataRead.readObject();
//System.out.println("popData()()"+"frame="+frame.getSeq());//!!!!!!!!!!!!!!!!!!!!!!!!!!
panel.pop(frame); // 通知演示面板
dataNum++;
if(dataNum%32==10) // 制造错误帧
{
System.out.println("dataNum="+dataNum);//!!!!!!!
System.out.println("错误帧frame="+frame.getSeq());//!!!!!!!
frame.setCheckSum(false);
}
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
synchronized(this)
{
//System.out.println("popData()return");//!!!!!!!!!!!!!!!!!!!!!!!!!!
return frame;
}
}
// 从信道中取出确认,若读取失败返回null
public DataFrame popAck()
{
DataFrame frame = null;
frame = ackChannel.popAck();
return frame;
}
// 负责确认的传输
class ACKChannel
{
private boolean isUsable; // 该信道是否可用的标志
private PipedOutputStream in; // 确认传输信道输入端
private PipedInputStream out; // 确认传输信道输出端
private ObjectOutputStream write; // 负责将帧写入确认传输信道
private ObjectInputStream read; // 负责将帧读出确认传输信道
private int ackNum; // 通过管道的确认帧的数目
ACKChannel()
{
try
{
isUsable = true;
ackNum = 0;
in = new PipedOutputStream();
out = new PipedInputStream(in);
write = new ObjectOutputStream(in);
read = new ObjectInputStream(out);
}
catch(Exception e)
{
isUsable = false;
e.printStackTrace();
}
}
// 判断该信道是否可用
public boolean isUsable()
{
return isUsable;
}
// 开始通信
public void start()
{
ackNum = 0;
}
// 关闭该信道,关闭成功返回true,否则返回false
public boolean close()
{
try
{
in.close();
out.close();
write.close();
read.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
// 将确认放入信道,若成功返回true,否则返回false
synchronized public boolean pushAck(DataFrame frame)
{
try
{
write.writeObject(frame);
write.flush();
panel.push(frame); // 通知演示面板
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
// 从信道中取出确认,若读取失败返回null
public DataFrame popAck()
{
DataFrame frame = null;
try
{
if(ackNum%32==10) // 制造超时
{
frame = (DataFrame)read.readObject();
ackNum++;
}
frame = (DataFrame)read.readObject();
//System.out.println(frame.getAck());
synchronized(this)
{
ackNum++;
/*
if(ackNum%32==26) // 制造错误帧
{
frame.setCheckSum(false);
}
*/
panel.pop(frame); // 通知演示面板
}
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
return frame;
}
}
}
class ChannelPanel extends JPanel
{
private int width; // 面板的大小
private int height;
private Color backColor; // 背景颜色
private LinkedList dataList; // 管道中存在的数据帧
private LinkedList ackList; // 管道中存在的确认帧
ChannelPanel(int width, int height)
{
this.width = width; // 参数传递
this.height = height;
backColor = Color.white; // 变量初始化
dataList = new LinkedList();
ackList = new LinkedList();
}
// 停止演示
public void start()
{
if(!(dataList.isEmpty()))
{
dataList.clear();
}
if(!(ackList.isEmpty()))
{
ackList.clear();
}
this.repaint();
}
// 将帧frame压入管道
synchronized public void push(DataFrame frame)
{
//System.out.println("kind = "+frame.getKind());//!!!!!!
if(frame.getKind()==DataFrame.DATA)
{
dataList.addFirst(frame);
}
else
{
ackList.addFirst(frame);
}
//System.out.println("kind = "+frame.getKind()+"datalist.size = "+dataList.size());//!!!!!!
this.repaint();
}
// 将帧frame弹出管道
synchronized public void pop(DataFrame frame)
{
if(frame.getKind()==DataFrame.DATA)
{
if(!(dataList.isEmpty()))
{
//System.out.println(" dataList.removeLast();");//!!!!!!
dataList.removeLast();
}
//System.out.println("pop(DataFrame frame)repaint();");//!!!!!!
}
else
{
if(!(ackList.isEmpty()))
{
ackList.removeLast();
}
}
this.repaint();
}
// 绘制以(x0,y0)为中心的管道,宽为width,高为height
private void pipeline(int x0, int y0, int width, int height, Graphics g)
{
int ellipseH = height; // 椭圆的大小
int ellipseW = ellipseH/2;
int ellipseLX = x0 - width/2; // 左椭圆外接矩形左上角坐标
int ellipseLY = y0 - height/2;
//g.fillOval(ellipseLX,ellipseLY,ellipseW,ellipseH); // 左椭圆
int ellipseRX = x0+width/2-ellipseW; // 右椭圆外接矩形左上角坐标
int ellipseRY = y0 - height/2;
//g.fillOval(ellipseRX,ellipseRY,ellipseW,ellipseH); // 右椭圆
int lux = ellipseLX + ellipseW/2; // 上横线左坐标
int luy = ellipseLY;
int rux = ellipseRX + ellipseW/2; // 上横线右坐标
int ruy = luy;
//g.drawLine(lux,luy,rux,ruy); // 上横
int llx = lux; // 下横线左坐标
int lly = luy + height;
int rlx = rux; // 下横线右坐标
int rly = lly;
//g.drawLine(llx,lly,rlx,rly); // 下横
g.setColor(Color.gray); // 设置颜色
g.fillRect(0, ellipseLY, rlx+45, rly-10);
int arrowW = 4; // 箭头宽度
// 绘制确认帧出管道方向指示,与发送方面板有衔接
int ackArrowX0 = lux; // 箭头尾部中心坐标
int ackArrowY0 = y0 - height/4;
int exitLX = 0; // 与发送方衔接参数
int exitLY = ackArrowY0 - 3;
g.drawLine(ackArrowX0,ackArrowY0-arrowW/2,0,exitLY-arrowW/2);
g.drawLine(ackArrowX0,ackArrowY0+arrowW/2,0,exitLY+arrowW/2);
// 绘制数据帧出管道方向指示,与接收方面板有衔接
int dataArrowX0 = rux; // 箭头尾部中心坐标
int dataArrowY0 = y0 + height/4;
int exitRX = 0; // 与接收方衔接参数
int exitRY = dataArrowY0 + 16;
g.drawLine(dataArrowX0,dataArrowY0-arrowW/2,this.width,exitRY-arrowW/2);
g.drawLine(dataArrowX0,dataArrowY0+arrowW/2,this.width,exitRY+arrowW/2);
}
// 以mid为纵坐标,在start和end之间绘出管道中存在的数据帧
private void paintData(int start, int end, int mid, Graphics g)
{
//int size = 7;
int size = dataList.size();
if(size>0)
{
int len = end - start; // 总长度
int w = 33; // 帧的大小
int h = 15;
int lx = start; // 帧左上角的X坐标
int ly = mid - h/2; // 帧左上角的Y坐标
int dis = len/size; // 两帧起点之间的间隔
//System.out.println("paintData!!!!!!!!");
for(int i=0;i<size;i++)
{
//System.out.println("frameX0="+lx+" size="+size);
DataFrame frame = (DataFrame)dataList.get(i);
int seq = frame.getSeq();
g.drawRect(lx,ly,w,h);
g.drawString("data"+seq,lx+2,ly+h-3);
g.drawLine(lx+w,mid,lx+w+12,mid);
g.drawLine(lx+w+12,mid,lx+w+7,mid-3);
g.drawLine(lx+w+12,mid,lx+w+7,mid+3);
lx = lx + dis;
}
}
}
// 以mid为纵坐标,在start和end之间绘出管道中存在的确认帧
private void paintACK(int start, int end, int mid, Graphics g)
{
//int size = 7;
int size = ackList.size();
if(size>0)
{
int len = start - end; // 总长度
int w = 33; // 帧的大小
int h = 15;
int lx = start; // 帧右上角的X坐标
int ly = mid - h/2; // 帧右上角的Y坐标
int dis = len/size; // 两帧起点之间的间隔
for(int i=0;i<size;i++)
{
//System.out.println("frameX0="+lx+" size="+size);
DataFrame frame = (DataFrame)ackList.get(i);
int seq = frame.getAck();
g.drawRect(lx-w,ly,w,h);
//g.drawString("ACK"+7,lx-w+2,ly+h-3);
g.drawString("ACK"+seq,lx-w+2,ly+h-3);
g.drawLine(lx-w,mid,lx-w-12,mid);
g.drawLine(lx-w-12,mid,lx-w-7,mid-3);
g.drawLine(lx-w-12,mid,lx-w-7,mid+3);
lx = lx - dis;
}
}
}
synchronized public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(backColor); // 清屏
g.fillRect(0,0,width,height);
g.setColor(Color.black);
//g.drawLine(1,5,20,5); // 上横
//g.drawLine(1,5,width-1,5);
//g.drawLine(1,5,1,height-5); // 左竖
//g.drawLine(1,height-5,width-1,height-5); // 下横
//g.drawLine(width-1,5,width-1,height-5); // 右竖
g.drawString("通信信道",100,20); // 标题
//g.setColor(Color.red);
//g.fillRect(1,5 , 20, 5);
int x0 = width/2; // 管道中心
int y0 = height/3;
int pipWidth = width - 20; // 管道大小
int pipHeight= height/3;
pipeline(x0,y0,pipWidth,pipHeight,g); // 绘制管道
paintData(0,width-pipHeight/2,y0+pipHeight/4,g);// 绘制数据帧
paintACK(width,0+pipHeight/2,y0-pipHeight/4,g); // 绘制确认帧
}
}///:~
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -