简单文件传输协议--主机部分--patr1

来源:互联网 发布:暗黑破坏神3 mac 编辑:程序博客网 时间:2024/05/13 09:36

/*
串口基础--简单文件传输协议--主机部分
从机部分:devtranfile.h
          devtranfile.c
新建工程名称:UartSendFile
时间:2013年5月23日 第一个版本
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;             //使用Thread类来创建和控制线程
using System.Windows.Forms;
namespace UartSendFile
{
    public partial class Form1 : Form
    {
        /*********************************************************************************
        *********************************************************************************/
        #region //窗口的起动 关闭
        public Form1()
        {
            InitializeComponent();
        }
        //窗口起动事件
        private void Form1_Load(object sender, EventArgs e)
        {
            int i = 0;
            comboBox_comx.Items.Clear();
            while(i++<20)
            {
                comboBox_comx.Items.Add("COM" + i.ToString());
            }

            comboBox_baudrate.Items.Clear();
            comboBox_baudrate.Items.Add("1200");
            comboBox_baudrate.Items.Add("2400");
            comboBox_baudrate.Items.Add("4800");
            comboBox_baudrate.Items.Add("9600");
            comboBox_baudrate.Items.Add("19200");
            comboBox_baudrate.Items.Add("38400");
            comboBox_baudrate.Items.Add("57600");
            comboBox_baudrate.Items.Add("115200");
            textBox_path.Text = "请选择文件..";
            textBox_message.Text = "消息...";
            textBox_message.Enabled = false;
        }
        //关闭事件
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (PortIsOpend == true)
            {
                PortIsOpend = false;
                serialPort1.Close();
            }
        }
        #endregion
       
        /*********************************************************************************
        *********************************************************************************/
        #region //串口相关操作
        string PortSelected;        //当前选中的串口名:COMx
        int BaudRate;               //波特率
        bool PortIsOpend = false;   //串口当前状态
        //【按钮】:打开串口,关闭串口
        private void butSirPort_Click(object sender, EventArgs e)
        {
            //获取当前波特率
            string baud = comboBox_baudrate.Text;
            serialPort1.BaudRate = Convert.ToInt32(baud.Trim());     //若baud非纯数组,会抛异常
            BaudRate = serialPort1.BaudRate;
            PortSelected = comboBox_comx.Text;

            if (PortIsOpend == false)
            {
                try
                {
                    serialPort1.PortName = PortSelected;  //PortSelected==""  会异常
                    serialPort1.Open();
                    butSirPort.Text = "关闭串口";
                    label_Port.Text = "串口已经打开";
                    PortIsOpend = true;

                    comboBox_baudrate.Enabled = false;
                    comboBox_comx.Enabled = false;
                }
                catch (Exception ee)
                {
                    ee.GetType();
                    label_Port.Text = "连接失败";
                    PortIsOpend = false;
                }
            }
            else
            {
                backgroundWorker.CancelAsync();
                butSirPort.Text = "打开串口";
                label_Port.Text = "串口已经关闭";
                PortIsOpend = false;
                comboBox_baudrate.Enabled = true;
                comboBox_comx.Enabled = true;
                serialPort1.Close();
            }
        }
        //串口接收数据函数
        public int usartRevData(byte[] revdata, int len)
        {
            int unm = 0;
            if (serialPort1.IsOpen == false)
            {
                return 0;
            }
            serialPort1.ReadTimeout = 1;                    //接收超时1MS
            try
            {
                int cnt = 0;
                unm = 0;
                while (true)
                {   //没有可以读取的则退出
                    cnt = serialPort1.BytesToRead;
                    if (cnt <= 0)
                    {
                        break;
                    }
                    //使用最小空间储存
                    cnt = (len > cnt) ? (cnt) : (len);
                    //revdata存数据 unm存个数
                    cnt = serialPort1.Read(revdata, unm, cnt);
                    unm += cnt;
                    //计算剩余空间长度
                    len = len - cnt;
                    if (len <= 0)
                    {
                        break;
                    }
                    //等待未接收完的数据就绪
                    int t = 4 * 10 * 1000 / BaudRate;   //接收4个数据的时间
                    Thread.Sleep(t + 1);                //多给1毫秒
                }
            }
            catch (Exception e)
            {
                e.GetType();
                unm = 0;
            }
            return unm;
        }
        public int usartRevDatas(byte[] revdata, int len, int timeout)
        {
            int i = 0;
            while (true)
            {
                i = usartRevData(revdata, len);
                if (i > 0 || timeout == 0)
                {
                    break;
                }
                timeout--;
                Thread.Sleep(1);
            }
            return i;
        }
        #endregion
       
        /*********************************************************************************
        *********************************************************************************/
        #region //文件的读取操作
        string sendfilename = "";       //要发送的文件名
        string sendfilepath = "";       //要发送的文件路径+文件名
        //【按钮】 选择文件
        private void but_filepath_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofl = new OpenFileDialog();
            if (ofl.ShowDialog() == DialogResult.OK)            //OK表示按下了“打开”   
            {
                sendfilepath = ofl.FileName;
                sendfilename = ofl.SafeFileName;
                textBox_path.Text = sendfilepath;

                FileStream fs = File.OpenRead(sendfilepath);
                int i = (int)fs.Length;
                label_filelen.Text = "文件大小:" + i.ToString() + "字节";
            }
        }
        private void button_send_Click(object sender, EventArgs e)
        {
            //检测串口
            if (PortIsOpend == false)
            {
                ShowMessage("请先打开串口");
                return;
            }

            //检测文件
            if (sendfilename == "")
            {
                ShowMessage("请选择文件..");
                return;
            }

            //检测状态
            if (issending==true)
            {
                ShowMessage("正在传输文件,请等待...");
                return;
            }
            backgroundWorker.RunWorkerAsync();
        }
        #endregion
       
        /*********************************************************************************
        *********************************************************************************/
        #region //后台处理操作,用于显示消息,进度等
        //在调用backgroundWorker.RunWorkerAsync()后,将执行本函数。
        //本函数可以执行阻塞事件,不影响主线程
        string backgroundWorkermsg = "";            //用于传递消息
        bool issending = false;                     //是否正在传输文件标志
        private void backgroundWorker_ShowMsg(string msg)
        {
            backgroundWorkermsg = msg;
            backgroundWorker.ReportProgress(101);
        }
        //在调用backgroundWorker.ReportProgress()后,将执行本函数。需要在属性ProgressChanged中添加本函数
        //本函数是主线程函数了
        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage > 100)
            {
                ShowMessage(backgroundWorkermsg);
            }
            else
            {
                progressBar.Value = e.ProgressPercentage;
            }
        }

        //在DoWork退出时被调用后,需要在属性RunWorkerCompleted中添加本函数
        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            progressBar.Value = 0;
            issending = false;
        }
        private void ShowMessage(string msg)
        {
            textBox_message.Text = msg;
        }
        #endregion

        /*********************************************************************************
        *********************************************************************************/
        #region //文件传输操作
        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            bool ret;
            //有线程在操作,则提出
            if (issending == true)
            {
                return;
            }
            issending = true;
            backgroundWorker_ShowMsg("正在传输文件:" + sendfilename);
           
            FileStream fs = File.OpenRead(sendfilepath);
            fdata = new byte[fs.Length];
            fs.Read(fdata, 0, fdata.Length);                //将数据读取到fdata

            //第一步:发送文件名
            ret = sf_filename(sendfilename);
            if (ret == false)
            {
                backgroundWorker_ShowMsg("文件名发送超时:" + sendfilename);
                return;
            }
            //第二步:发送文件内容
            if (true == sf_filedata())
            {
                backgroundWorker_ShowMsg("发送文:" + sendfilename + "成功");
            }
            else
            {
                backgroundWorker_ShowMsg("发送文:" + sendfilename + "失败");
            }
            backgroundWorker.ReportProgress(100);
        }
        //发送文件内容
        byte[] fdata;                       //文件内容
        byte[] SendPkg = new byte[256+100]; //每次传输的数据包
        private bool sf_filedata()
        {
            int flen = fdata.Length;
            int fcnt = 1;               //当前发送的包数(从1开始)
            int per = 0;                //百分比。
            int perbe = 0;                //百分比。
            SendPkg[0] = 0xAA;
            SendPkg[1] = 0xff;      //校验值
            SendPkg[2] = 0xff;      //目的地
            SendPkg[3] = 0x01;      //类型,==1文件传输
            int timeout = 0;
            while (true)
            {
                SendPkg[4] = (byte)fcnt;        //!=0,表示传输的是文件
                SendPkg[5] = (byte)(fcnt >> 8);
                SendPkg[6] = (byte)(fcnt >> 16);
                SendPkg[7] = (byte)(fcnt >> 24);

                //数据包装载
                int j;
                if (flen - fcnt * 256 > 0)
                {
                    //剩余发送数据大于256
                    j = 256;
                }
                else
                {
                    //剩余发送数据小于256
                    j = fcnt * 256 - flen;
                    if (j >= 256)
                    {
                        //数据发送完毕
                        return sf_fileEnd();
                    }
                    j = 256 - j;
                }
                int i;
                int starts;                             //起始位置
                starts = (fcnt - 1) * 256;
                for (i = 0; i < j; i++)
                {
                    SendPkg[8 + i] = fdata[starts + i];
                }
                SendPkg[1] = MathXor_2(SendPkg, j + 8);
                //发送数据包
                serialPort1.Write(SendPkg, 0, j + 8);
                //接收返回信号
                int sf_ret;
                sf_ret = sf_file_ret(1000);

                if (sf_ret != 0)
                {
                    timeout = 0;
                }
                switch(sf_ret)
                {
                    case 0:     //接收数据超时
                        timeout++;
                        if (timeout > 10)       //10次后结束传输
                        {
                            return false;
                        }
                        break;
                    case 5:     //发送成功
                                fcnt++;
                        break;
                    case 4:     //请求重新发送
                        break;
                    case 6:     //请求结束发送
                        return true;
                    default:    //其他情况,发送失败
                        break;
                }
                //百分比
                per = flen / 256;           //总报数
                per = fcnt * 100 / per;     //百分比
                if (perbe != per)
                {
                    perbe = per;
                    backgroundWorker.ReportProgress(perbe);
                }
            }
        }

        //传输文件名
        private bool sf_filename(string sendfilename)
        {
            SendPkg[0] = 0xAA;
            SendPkg[1] = 0xff;      //校验值
            SendPkg[2] = 0xff;      //目的地
            SendPkg[3] = 0x01;      //类型,==1文件传输
           
            SendPkg[4] = 0x00;      //==0,表示传输的是文件名
            SendPkg[5] = 0x00;      //
            SendPkg[6] = 0x00;      //
            SendPkg[7] = 0x00;      //
            int len = sendfilename.Length;
            if (len > 256)
            {
                return false;
            }

            //获取文件名
            byte[] tmp;
            tmp = System.Text.Encoding.Default.GetBytes(sendfilename);
            int i;
            for (i = 0; i < len; i++)
            {
                SendPkg[8 + i] = tmp[i];
            }

            len += 8; //整包长度
            SendPkg[1] = MathXor_2(SendPkg, len);

            //尝试30次
            i = 0;
            while (i++ <= 30)
            {
                //发送数据包
                serialPort1.Write(SendPkg, 0, len);

                if (5 == sf_file_ret(1000))
                {
                    //发送成功
                    return true;
                }
                backgroundWorker_ShowMsg("发送文件名:" + sendfilename + "无应答倒计时:" + (30 - i).ToString());
            }
            //发送失败
            return false;
        }

        //传输结束信号
        private bool sf_fileEnd()
        {
            SendPkg[0] = 0xAA;
            SendPkg[1] = 0xff;      //校验值
            SendPkg[2] = 0xff;      //目的地
            SendPkg[3] = 0x01;      //类型,==1文件传输

            SendPkg[4] = 0xff;      //==0,表示传输的是文件名
            SendPkg[5] = 0xff;      //
            SendPkg[6] = 0xff;      //
            SendPkg[7] = 0xff;      //

            SendPkg[1] = MathXor_2(SendPkg, 8);

            //尝试5次
            int i = 0;
            while (i++ <= 5)
            {
                //发送数据包
                serialPort1.Write(SendPkg, 0, 8);

                int fs_ret=sf_file_ret(1000);
                if (5 == fs_ret)
                {
                    //发送成功
                    return true;
                }
            }
            //发送失败
            return false;
        }


        //---------对从机的返回数据处理-------
        //==0 没有收到数据
        //==1 校验错误
        //==2 返回的id不对
        //==3 类型不是文件传输
        //==4 从机 请求重新传数据
        //==5 从机接收OK
        //==6 停止传输
        //>=7 未定义
        int sf_file_ret(int timeout)
        {
            byte[] tmp = new byte[256 + 100];
            int i = usartRevDatas(tmp,256+100,timeout);
            if (i <= 0)
            {
                return 0;
            }
            if (tmp[1] != MathXor_2(tmp, i))
            {
                return 1;
            }
            //if()  rtrurn 2
            if (tmp[3] != 1)
            {
                return 3;
            }
            if (tmp[4] == 0xa5)     //重新传
            {
                return 4;
            }
            if (tmp[4] == 0xaa)     //ok
            {
                return 5;
            }
            if (tmp[4] == 0x55)     //停止传输
            {
                return 6;
            }
            return 7;
        }

        //从第二个地方开始异或值
        byte MathXor_2(byte[] xordata, int len)
        {
            int i = 0;
            byte xor = 0;
            byte tmp;
            i = 2;
            len -= 2;
            while (len-- > 0)
            {
                tmp = xordata[i++];
                xor = (byte)(xor ^ tmp);
            }
            return xor;
        }
        byte MathXor(byte[] xordata, int len)
        {
            int i = 0;
            byte xor = 0;
            byte tmp;
            while (len-- <= 0)
            {
                tmp = xordata[i++];
                xor = (byte)(xor ^ tmp);
            }
            return xor;
        }
        #endregion
    }
}

原创粉丝点击