⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stopwaitcommdlg.cpp

📁 MFC模拟计算机网络停等协议发送和接收数据过程
💻 CPP
字号:
// StopWaitCommDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "StopWaitComm.h"
#include "StopWaitCommDlg.h"
#include ".\stopwaitcommdlg.h"
#include "afxmt.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CStopWaitCommDlg 对话框
#define ENQ	0x05
#define	ACK	0x06
#define	STX	0x02
#define	ETX	0x03
#define	EOT	0x04

#define	IDLESTATE	0
#define	SENDSTATE	1
#define	RECVSTATE	2

#define BUFFER_SIZE	3100
#define	TIMEOUT		5000

CFile		SendFile,RecvFile;	//发送和接收文件

HANDLE		m_hCommPort;		//保存打开的串行口设备句柄
char		RecvBuf[BUFFER_SIZE], SendBuf[BUFFER_SIZE];
UINT		SendLen,RecvPTR;

UINT		CommState;	//保存通信状态:IdleState、SendState和RecvState
BYTE		CRC;		//保存校验和
BYTE		Sequence;	//在等停协议中仅使用0和1
bool		ACKFlag;	//接收到ACK:true
bool		LastPacket;	//是否最后一包
UINT		STXFlag;	//0:没收到STX;1:收到STX但未收到序列号;2:收到STX而且收到序列号
UINT_PTR	m_nTimer;	//定时器
UINT		SendCount;	//重发计数器
time_t		TimeStart,TimeFinish;
ULONG		TextLen;	//发送文件的总长度

CString		CommPort;//需要打开的串行口
UINT		PacketLen,Speed,DelayTime;//数据包大小,串行口的速率,为模拟长线路而增加的线路延迟

CStopWaitCommDlg* pDlg;

UINT ReadChar(PVOID hWnd);	//读取字符线程
void FormPacket();	//打包
void SendMsg(char *DisplayStr); //向串行口发送信息
bool SetupCommPort();	//设置串行口



CStopWaitCommDlg::CStopWaitCommDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CStopWaitCommDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CStopWaitCommDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LOG, m_ListLog);
	DDX_Control(pDX, IDOK, m_Send);
	DDX_Control(pDX, IDC_SETUP, m_Setup);
}

BEGIN_MESSAGE_MAP(CStopWaitCommDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDOK, OnBnClickedOk)
	ON_WM_TIMER()
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_SETUP, OnBnClickedSetup)
END_MESSAGE_MAP()


// CStopWaitCommDlg 消息处理程序

BOOL CStopWaitCommDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	CStopWaitCommApp* pApp=(CStopWaitCommApp*)AfxGetApp();
	pDlg=(CStopWaitCommDlg*)pApp->m_pMainWnd;

	m_Send.EnableWindow(false);

	m_hCommPort=INVALID_HANDLE_VALUE;
	CommPort="COM1";Speed=9600;PacketLen=1500;DelayTime=0;

	if (m_SetupComm.DoModal()==IDOK)
	{
        CommPort=m_SetupComm.m_sCommPort;
		Speed=m_SetupComm.m_iSpeed;
		PacketLen=m_SetupComm.m_iPacketLen;
		DelayTime=m_SetupComm.m_iDelayTime;
		if (SetupCommPort())
		{
			m_Send.EnableWindow(true);

			char str[100];
			sprintf(str,"StopWaitComm 串行口=%s,速率=%i,包长度=%i,线路延迟+%i",CommPort,Speed,PacketLen,DelayTime);
			pDlg->SetWindowText(str);
		}
		else
			m_Send.EnableWindow(false);
	}

	return TRUE;  // 除非设置了控件的焦点,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CStopWaitCommDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标显示。
HCURSOR CStopWaitCommDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CStopWaitCommDlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知处理程序代码

	//获取需要发送的文件名
	CString FileName;
	CFileDialog GetFileName(TRUE,NULL,NULL,OFN_HIDEREADONLY,"文本文件(*.txt)|*.txt||",NULL,0);
	if (GetFileName.DoModal()!=IDOK) return;
	FileName=GetFileName.GetPathName();
	if ( !SendFile.Open(FileName,CFile::modeRead|CFile::typeBinary,NULL))	//打开文件
	{
		MessageBox("发送文件打开错误!");
		return;
	}
	//进入发送状态
	m_Send.EnableWindow(false);
	m_Setup.EnableWindow(false);

	m_ListLog.InsertString(-1,"------进入发送状态------");
	time(&TimeStart);	//开始计时
	CommState=SENDSTATE;ACKFlag=false;Sequence=0;LastPacket=false;SendCount=0;TextLen=0;
	SendBuf[0]=ENQ;SendLen=1;	//通知接收方开始发送
	SendMsg("发送:ENQ");
	m_nTimer=SetTimer(1,TIMEOUT,NULL);	//设置定时器
}

UINT ReadChar(PVOID hWnd)
{
	CEvent		RecvEvent(0,TRUE,0,0);//发送和接收事件
	OVERLAPPED	RecvOV;
	char		DisplayStr[256],tempStr[50];
	ULONG		ReadLen;
	double		DiffTime,Ratio;

	memset(&RecvOV,0,sizeof(RecvOV));	//初始化Overlapped变量
	RecvOV.hEvent=RecvEvent;

	while (true)		//只要线程运行,就监视端口是否收到数据
	{
		if (CommState==RECVSTATE)	m_nTimer=pDlg->SetTimer(1,3*TIMEOUT,NULL);	//设置定时器

		ReadFile(m_hCommPort,&RecvBuf[RecvPTR],1,&ReadLen,&RecvOV); //读字符
		GetOverlappedResult(m_hCommPort,&RecvOV,&ReadLen,TRUE);		//等待读状态完成
		RecvEvent.ResetEvent();		//复位事件变量

		if (ReadLen>0)
		{
			switch (CommState)
			{
			case IDLESTATE:
				if (RecvBuf[RecvPTR]==ENQ)	//收到ENQ,进入接收状态
				{
					pDlg->m_ListLog.InsertString(-1,"------进入接收状态------");
					pDlg->m_ListLog.InsertString(-1,"接收:ENQ");
					if ( !RecvFile.Open("receive.txt",CFile::modeCreate|CFile::modeWrite|CFile::typeBinary,NULL))	//打开文件
					{
                            AfxMessageBox("接收文件打开错误!");
							pDlg->m_ListLog.InsertString(-1,"------返回空闲状态------");
							break;
					}
					pDlg->m_Send.EnableWindow(false);
					pDlg->m_Setup.EnableWindow(false);
					CommState=RECVSTATE;Sequence=0;RecvPTR=0;STXFlag=0;TextLen=0;	//进入接收状态
					time(&TimeStart);
					SendBuf[0]=ACK; SendBuf[1]=Sequence; SendLen=2;	//发送确认信息
					SendMsg("发送:ACK 0");
				}
				break;
			case SENDSTATE:
				if (ACKFlag==true)
				{
					if (RecvBuf[RecvPTR]==Sequence)
					{
						sprintf(DisplayStr,"接收:ACK %i",Sequence);pDlg->m_ListLog.InsertString(-1,DisplayStr);
						SendCount=0;
						pDlg->KillTimer(m_nTimer);
						if (LastPacket==true)	//是否最后一包
						{
							CommState=IDLESTATE;
							SendFile.Close();	//最后一包,关闭文件

							SendBuf[0]=EOT;SendBuf[1]=EOT;SendLen=2;	//通知接收方开始发送
							SendMsg("发送:EOT");
							pDlg->m_ListLog.InsertString(-1,"------返回空闲状态------");
						
							time(&TimeFinish);	//显示统计信息
							DiffTime=difftime(TimeFinish,TimeStart);
							if ((ULONG (DiffTime))==0) sprintf(tempStr,"xxxxxx");	//该程序只能精确到秒
							else
							{
								Ratio=(((TextLen*9.0)/Speed)/DiffTime)*100;
								sprintf(tempStr,"%f%%",Ratio);
							}
							pDlg->m_ListLog.InsertString(-1,"统计信息(注意本程序的传输时间只能精确到秒):");
							sprintf(DisplayStr,"数据速率:%i字节/秒;数据包长:%i字节;线路延迟:+%i微秒;",Speed,PacketLen,DelayTime);
							pDlg->m_ListLog.InsertString(-1,DisplayStr);
							sprintf(DisplayStr,"数据长度:%li字节;传输耗时:%li秒;传输效率:%s。",TextLen,(ULONG) DiffTime,tempStr);
							pDlg->m_ListLog.InsertString(-1,DisplayStr);
							pDlg->m_Send.EnableWindow(true);
							pDlg->m_Setup.EnableWindow(true);
						}
						else
						{
							FormPacket();
							SendMsg("发送:信息......");
							Sequence = (Sequence==0) ? 1:0;	//变换Sequence.
							m_nTimer=pDlg->SetTimer(1,TIMEOUT,0);	//设置定时器
						}
					}
					ACKFlag=false;
				}
				else
					if (RecvBuf[RecvPTR]==ACK) ACKFlag=true;
				break;
			case RECVSTATE:
				switch (STXFlag)
				{
				case 0:
					switch (RecvBuf[RecvPTR])
					{
					case EOT:
						CommState=IDLESTATE;RecvPTR=0;	//接收完成,连接断开
						pDlg->m_ListLog.InsertString(-1,"接收:完成");
						RecvFile.Close ();
						pDlg->m_ListLog.InsertString(-1,"------返回空闲状态------");
						pDlg->KillTimer(m_nTimer);

						time(&TimeFinish);	//显示统计信息
						DiffTime=difftime(TimeFinish,TimeStart);
						if ((ULONG (DiffTime))==0) sprintf(tempStr,"xxxxxx");	//该程序只能精确到秒
						else
						{
							Ratio=(((TextLen*9.0)/Speed)/DiffTime)*100;
							sprintf(tempStr,"%f%%",Ratio);
						}
						pDlg->m_ListLog.InsertString(-1,"统计信息(注意本程序的传输时间只能精确到秒):");
						sprintf(DisplayStr,"数据速率:%i字节/秒;数据包长:%i字节;线路延迟:+%i微秒;",Speed,PacketLen,DelayTime);
						pDlg->m_ListLog.InsertString(-1,DisplayStr);
						sprintf(DisplayStr,"数据长度:%li字节;传输耗时:%li秒;传输效率:%s。",TextLen,(ULONG) DiffTime,tempStr);
						pDlg->m_ListLog.InsertString(-1,DisplayStr);
						pDlg->m_Send.EnableWindow(true);
						pDlg->m_Setup.EnableWindow(true);
						break;
					case ENQ:
						pDlg->m_ListLog.InsertString(-1,"接收:ENQ");
						SendBuf[0]=ACK; SendBuf[1]=Sequence; SendLen=2;	//发送确认信息
						sprintf(DisplayStr,"发送:ACK %i",Sequence);
						SendMsg(DisplayStr);
						break;
					case STX:
						STXFlag=1;
						break;
					}
					break;
				case 1:
					if (RecvBuf[RecvPTR]==Sequence)
					{
						STXFlag=2;
						CRC=0;
					}
					else STXFlag=0;
					break;
				case 2:
					switch (RecvBuf[RecvPTR])
					{
					case ETX:
						if (CRC==0)
						{
							pDlg->m_ListLog.InsertString(-1,"接收:信息......");
							RecvFile.Write(RecvBuf,RecvPTR-1);	//将接收信息存入文件
							TextLen=TextLen+RecvPTR-1;
							Sequence=(Sequence==0)?1:0;
							SendBuf[0]=ACK; SendBuf[1]=Sequence; SendLen=2;
							sprintf(DisplayStr,"发送:ACK %i",Sequence);
							SendMsg(DisplayStr);
						}
						RecvPTR=0;STXFlag=0;	//开始新的一包
						break;
					default:
						if (RecvPTR < BUFFER_SIZE)	CRC=CRC^RecvBuf[RecvPTR++];
						else {RecvPTR=0;STXFlag=0;}
						break;
					}
					break;
				}
				break;
			} 
		}
	}
	return 0;
}
void FormPacket()	//打包
{
	int	len,i;	

	SendBuf[0]=STX;SendBuf[1]=Sequence;
	len=SendFile.Read(&SendBuf[2],PacketLen);	//读取数据
	
	CRC=0;	//计算校验和
	for (i=0;i<len;i++)
		CRC=CRC^SendBuf[i+2];
	
	SendBuf[2+len]=CRC;
	SendBuf[2+len+1]=ETX;

	SendLen=2+1+len+1;
	LastPacket=(len==PacketLen)?false:true;
	TextLen=TextLen+len;
}
void SendMsg(char *DisplayStr)	//向串行口发送信息
{
	CEvent		SendEvent(0,TRUE,0,0);	//发送和接收事件
	OVERLAPPED	SendOV;
	ULONG		SentLen;

	memset(&SendOV,0,sizeof(SendOV));	//初始化Overlapped变量
	SendOV.hEvent=SendEvent;

	Sleep(DelayTime);		//模拟长线路延迟

	WriteFile(m_hCommPort,SendBuf,SendLen,&SentLen,&SendOV);
	GetOverlappedResult(m_hCommPort,&SendOV,&SentLen,TRUE);		//等待发送完成
	SendEvent.ResetEvent();
	pDlg->m_ListLog.InsertString(-1,DisplayStr);	//显示信息
}

void CStopWaitCommDlg::OnTimer(UINT nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (CommState)
	{
	case SENDSTATE:
		if (SendCount<3)
		{
			SendCount++;
			KillTimer(m_nTimer);SendMsg("超时重传......");m_nTimer=SetTimer(1,TIMEOUT,NULL);
		}
		else
		{
			pDlg->KillTimer(m_nTimer);
			m_ListLog.InsertString(-1,"发送超时......");
			CommState=IDLESTATE;
			SendFile.Close();	//最后一包,关闭文件
			SendBuf[0]=EOT;SendBuf[1]=EOT;SendLen=2;	//通知接收方开始发送
			SendMsg("发送:EOT");
			m_ListLog.InsertString(-1,"------返回空闲状态------");
			m_Send.EnableWindow(true);
			m_Setup.EnableWindow(true);
		}
		break;
	case RECVSTATE:
		m_ListLog.InsertString(-1,"接收超时......");
		CommState=IDLESTATE;RecvPTR=0;	//接收完成,连接断开
		pDlg->m_ListLog.InsertString(-1,"接收:完成");
		RecvFile.Close ();
		m_ListLog.InsertString(-1,"------返回空闲状态------");
		KillTimer(m_nTimer);
		m_Send.EnableWindow(true);
		m_Setup.EnableWindow(true);
		break;
	}
	CDialog::OnTimer(nIDEvent);
}

bool SetupCommPort()	//设置串行口
{

	DCB	CommDCB;	//串行口的设备控制块

	if (m_hCommPort==INVALID_HANDLE_VALUE)
		//CloseHandle(m_hCommPort);	//关闭已打开的串行口
	{
		m_hCommPort=CreateFile (CommPort,		//打开串行口
			GENERIC_READ|GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
			NULL);
		if(m_hCommPort==INVALID_HANDLE_VALUE)
			{
				AfxMessageBox("系统无法打开此串口设备(CreateFile)!");
				return false;
			}

		AfxBeginThread(ReadChar,NULL,THREAD_PRIORITY_NORMAL);
	}
	
	if(!GetCommState(m_hCommPort,&CommDCB)) //得到原来的串口参数
	{
		AfxMessageBox("无法得到串口状态(GetCommState)!");
		CloseHandle(m_hCommPort);
		return false;
	}
  
	CommDCB.BaudRate =Speed;	//设置新的串口参数
	CommDCB.ByteSize =8; 
	CommDCB.Parity = NOPARITY; 
    CommDCB.StopBits = ONESTOPBIT ;
	CommDCB.fBinary = TRUE ;
	CommDCB.fParity = FALSE; 

   	if(!SetCommState(m_hCommPort, &CommDCB))
	{
		AfxMessageBox("无法设置串口状态(SetupCommState)!");
		CloseHandle(m_hCommPort);
		return false;
	} 

	if (!SetupComm(m_hCommPort,BUFFER_SIZE,BUFFER_SIZE))		//设置缓冲区
	{
		AfxMessageBox("设置缓冲区错误(SetupComm)!");
		CloseHandle(m_hCommPort);
		return false;
	}
	if (!PurgeComm(m_hCommPort, PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT)) //清除所有接收和发送缓冲区中的数据
	{
		AfxMessageBox("清空缓冲区错误(PurgeComm)!");
		CloseHandle(m_hCommPort);
		return false;
	}
	RecvPTR=0;
	return true;
}

void CStopWaitCommDlg::OnClose()
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CloseHandle(m_hCommPort);
	CDialog::OnClose();
}

void CStopWaitCommDlg::OnBnClickedSetup()
{
	// TODO: 在此添加控件通知处理程序代码
	char str[100];

	if (m_SetupComm.DoModal()==IDOK)
	{
        CommPort=m_SetupComm.m_sCommPort;
		Speed=m_SetupComm.m_iSpeed;
		PacketLen=m_SetupComm.m_iPacketLen;
		DelayTime=m_SetupComm.m_iDelayTime;

		if (SetupCommPort())
		{
			m_Send.EnableWindow(true);
			sprintf(str,"StopWaitComm 串行口=%s,速率=%i,包长度=%i,线路延迟+%i",CommPort,Speed,PacketLen,DelayTime);
			pDlg->SetWindowText(str);
		}
		else
			m_Send.EnableWindow(false);
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -