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

📄 zmodem.cpp

📁 DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!
💻 CPP
📖 第 1 页 / 共 3 页
字号:
// ********************* START OF ZMODEM.CPP *********************
//
// This file contains all of the source code needed to support
// Zmodem file transfers.  This code is directly derived from the
// public domain code released by Chuck Forsberg and Omen 
// Technology. The Zmodem enhancements published by Omen 
// Technology including variable headers and run length encoding 
// are not supported here. The Omen Technology code is available 
// with the source code for this book for the curious.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "rs232.h"
#include "crc.h"
#include "ascii.h"
#include "zmodem.h"
#include "_zmodem.h"

// The two notification routines belong to the base class.  There
// isn't any reason to override them in a derived class such as
// Zmodem, but a specific application may want to develop its own
// virtual functions to replace these.

void FileTransfer::error( char *fmt, ... )
{
   va_list argptr;

   va_start( argptr, fmt );
   vprintf( fmt, argptr );
   putc( '\n', stdout );
   va_end( argptr );
}

void FileTransfer::status( char *fmt, ... )
{
   va_list argptr;

   va_start( argptr, fmt );
   vprintf( fmt, argptr );
   putc( '\n', stdout );
   va_end( argptr );
}

// The Zmodem constructor only has to initialize a few variables.

Zmodem::Zmodem( RS232 *rs232_port )
{
    port = rs232_port;
    file_count = 0;
    file_name[ 0 ] = '\0';
    file_length = -1L;
    file = 0;
    receiver_buffer_length = 16384;
    wake_up_sender_header_type = ZRINIT;
}

// The public Send functionsends a batch of files one at a time
// via the SendSingleFile function.  When a normal completion
// occurs, it is flagged with a ZFIN frame.

int Zmodem::Send( char *files[] )
{
    PackLongIntoHeader( 0L );
    SendHexHeader(4, ZRQINIT, transmitted_header);
    GetRinitHeader();
    byte_count = -1;
    while ( *files ) {
        if ( SendSingleFile( *files ) == ERROR )
                 return ERROR;
        files++;
    }
    SendZFIN();
    return OK;
}

// This function is used *everywhere*, and benefits from being
// declared as inline.

inline int Zmodem::ReadChar( long timeout )
{
    int c = port->Read( timeout );
    return ( c < 0 ) ? TIMEOUT : c;
}


// This is the worker routine that transmits a single file.

int Zmodem::SendSingleFile( char *name )
{
    int c;
    unsigned long crc_value;
    long lastcrcrq = -1;
    int length;

    file = fopen( name, "rb" );
    if ( file == NULL) {
        error( "Failed to open %s", name );
        return OK;
    }
    file_at_eof = 0;
    fseek( file, 0, SEEK_END );
    file_length = ftell( file );
    fseek( file, 0, SEEK_SET );
    length = sprintf( buffer, 
                      "%s%c%u 0 0 0 0 0", 
                      name, 
                      0, 
                      file_length );

    for ( ; ; ) {
        PackLongIntoHeader( 0L );
        SendBinaryHeader( 4, ZFILE, transmitted_header );
        SendDataFrame( buffer, length, ZCRCW );
again:
        c = ReadHeader( received_header );
        switch ( c ) {
            case ZRINIT:
                while ( ( c = ReadChar( 5000L ) ) > 0 )
                if ( c == ZPAD )
                    goto again;
            /* **** FALL THRU TO **** */
            default:
                continue;
            case ZCAN:
            case TIMEOUT:
            case ZABORT:
            case ZFIN:
                return ERROR;
            case ZCRC:
                if ( received_file_position != lastcrcrq ) {
                    Crc32 crc( 0xFFFFFFFFL );
                    lastcrcrq = received_file_position;
                    fseek( file, 0L, SEEK_SET );
                    while ( ( ( c = getc( file ) ) != EOF ) 
                                && --lastcrcrq )
                        crc.update(c );
                    crc_value = ~crc.value();
                    fseek( file, 0L, SEEK_SET );
                    lastcrcrq = received_file_position;
                }
                PackLongIntoHeader( crc_value );
                SendBinaryHeader( 4, ZCRC, transmitted_header );
                goto again;
            case ZSKIP:
                fclose( file );
                return OK;
            case ZRPOS:
                if (fseek(file,received_file_position,SEEK_SET))
                   return ERROR;
                last_sync_position = 
                 (byte_count=transmitted_file_position=
                 last_reported_position=
                 received_file_position) - 1;
                return SendFileContents();
        }
    }
}


int Zmodem::SendFileContents( void )
{
    int c;
    int e;
    int n;
    int junkcount;
    int newcnt;

    junkcount = 0;

start_read:
    newcnt = receiver_buffer_length;
    PackLongIntoHeader( transmitted_file_position );
    SendBinaryHeader( 4, ZDATA, transmitted_header );
    do {
        n = fread( buffer, 1, 1024, file );
        if ( n < 1024 )
            file_at_eof = 1;

        if ( file_at_eof )
            e = ZCRCE;
        else if ( junkcount > 3 )
            e = ZCRCW;
        else if ( byte_count == last_sync_position )
            e = ZCRCW;
        else if ( receiver_buffer_length && ( newcnt -= n ) <= 0 )
            e = ZCRCW;
        else
            e = ZCRCG;
        SendDataFrame( buffer, n, e );
        byte_count = transmitted_file_position += n;
        if ( e == ZCRCW )
            goto waitack;
        while ( port->RXSpaceUsed() ) {
            switch ( ReadChar( 100 ) ) {
                case CAN:
                case ZPAD:
                    c = SyncWithReceiver( 1 );
                    if ( c == ZACK )
                        break;
                    SendDataFrame( buffer, 0, ZCRCE );
                    goto gotack;
                case XOFF:
                case XOFF | 0x80 :
                    ReadChar( 10000L );
                default:
                    junkcount++;
            }
        }
    } while ( !file_at_eof );
    for ( ; ; ) {
        PackLongIntoHeader( transmitted_file_position );
        SendBinaryHeader( 4, ZEOF, transmitted_header );
        switch ( SyncWithReceiver( 0 ) ) {
            case ZACK:
                continue;
            case ZRPOS:
                goto start_read;
            case ZRINIT:
                return OK;
            case ZSKIP:
                fclose( file );
                return c;
            default:
                fclose( file );
                return ERROR;
        }
    }

//Backchannel processing

waitack:
    junkcount = 0;
    c = SyncWithReceiver( 0 );
gotack:
    switch ( c ) {
        default:
        case ZCAN:
            fclose( file );
            return ERROR;
        case ZSKIP:
            fclose( file );
            return c;
        case ZACK:
        case ZRPOS:
            break;
        case ZRINIT:
            return OK;
    }
    while ( port->RXSpaceUsed() ) {
        switch ( ReadChar( 100 ) ) {
            case CAN:
            case ZPAD:
                c = SyncWithReceiver( 1 );
                goto gotack;
            case XOFF :
            case XOFF | 0x80 :
                ReadChar( 10000L );
        }
    }
    goto start_read;
}

// If all goes well, the public receive function just calls
// WakeUpSender(), then ReceiveFiles().  If both of those
// do what they are supposed to do, a batch of files will
// have been properly transferred.

int Zmodem::Receive( char * )
{
    static char CancelString[] = {
        CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN,
        BS,  BS,  BS,  BS,  BS,  BS,  BS,  BS,  BS,  BS };

    switch ( WakeUpSender() ) {
        case 0      :
        case ZCOMPL : return OK;
        case ERROR  : break;
        default     : if ( ReceiveFiles() == OK )
                          return OK;
    }
    port->Write( CancelString, sizeof CancelString, 30000L );
    if ( file )
        fclose( file );
    return ERROR;
}

// This is the general purpose receiver function.  It calls the
// ReceiveSingleFile function repeatedly as long as the wakeup
// function keeps receiving ZFILE frames.

int Zmodem::ReceiveFiles( void )
{
    int return_status ;

    for ( ; ; ) {
        switch ( return_status = ReceiveSingleFile() ) {
            case ZEOF:
            case ZSKIP:
                switch ( WakeUpSender() ) {
                    case ZCOMPL:
                        return OK;
                    default:
                        return ERROR;
                    case ZFILE:
                        break;
                }
                continue;
            default:
                return return_status;
            case ERROR:
                return ERROR;
        }
    }
}

// Some data used various places in the class

static char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"};

static char *frametypes[] = {
    "No Response to Error Correction Request",
    "No Carrier Detect",
    "TIMEOUT",
    "ERROR",
    "ZRQINIT",  "ZRINIT",   "ZSINIT",   "ZACK",     "ZFILE",
    "ZSKIP",    "ZNAK",     "ZABORT",   "ZFIN",     "ZRPOS",
    "ZDATA",    "ZEOF",     "ZFERR",    "ZCRC",     "ZCHALLENGE",
    "ZCOMPL",   "ZCAN",     "ZFREECNT", "ZCOMMAND", "ZSTDERR"
};

// This function is used when receiving files.  It sends out the
// initial frame and waits for a response from the sender.  If 
// things go properly it will get the file data subpacket and 
// return ZFILE. If the sender has no more files it will send a 
// ZFIN, which is handled here.

int Zmodem::WakeUpSender( void )
{
    int c;
    int n;

    for ( n = 0 ; n < 16 ; n++ ) {
        PackLongIntoHeader( 0L );
        transmitted_header[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
        SendHexHeader( 4, 
                       wake_up_sender_header_type, 
                       transmitted_header );
        if ( wake_up_sender_header_type == ZSKIP )
            wake_up_sender_header_type = ZRINIT;
        for ( int try_again = 1 ; try_again ;  ) {
            switch ( ReadHeader( received_header ) ) {
                case ZRQINIT :
                case ZEOF    :
                case TIMEOUT :
                default      :
                    try_again = 0;
                    break;
                case ZFILE   :
                    wake_up_sender_header_type = ZRINIT;
                    c = ReadDataFrame( buffer, 1024 );
                    if ( c == GOTCRCW )
                        return ZFILE;
                    SendHexHeader( 4, ZNAK, transmitted_header );
                    break;
                case ZSINIT  :
                    if (ReadDataFrame(attention_string, ZATTNLEN)
                           == GOTCRCW ) {
                        PackLongIntoHeader( 1L );
                        SendHexHeader(4,ZACK,transmitted_header);
                    } else
                        SendHexHeader(4,ZNAK,transmitted_header);
                    break;
                case ZCOMPL  :
                     break;
                case ZFIN    :
                    AckZFIN();
                    return ZCOMPL;
                case ZCAN    :
                    return ERROR;
            }
        }
    }
    return 0;
}

// This is the workhorse routine that reads a single file from the
// sender.  It reads in headers until it gets a ZDATA header, then
// it switches over to reading data subpackets until it gets one
// of the end of supbacket codes.

int Zmodem::ReceiveSingleFile( void )
{
    int c;
    int error_count;
    long rxbytes;

    if ( OpenInputFile( buffer ) == ERROR )
        return wake_up_sender_header_type = ZSKIP;
    error_count = 0;
    rxbytes = 0L;
    for ( ; ; ) {
        PackLongIntoHeader( rxbytes );
        SendHexHeader( 4, ZRPOS, transmitted_header );
nxthdr:
        switch ( c = ReadHeader( received_header ) ) {
            default:
              error( "ReceiveSingleFile: ReadHeader returned %d", 
                     c );
              return ERROR;
            case ZNAK:
            case TIMEOUT:
              if ( ++error_count >= 20 ) {
                error("ReceiveSingleFile: ReadHeader returned %d", 
                      c );
                return ERROR;
              }
            case ZFILE:
                ReadDataFrame( buffer, 1024 );
                continue;
            case ZEOF:
              if (UnpackHeaderIntoLong(received_header)!=rxbytes)
                goto nxthdr;
              if ( fclose( file ) != 0 ) {
                wake_up_sender_header_type = ZFERR;
                error( "ReceiveSingleFile: fclose() "
                       "returned error" );
                return ERROR;
              }
              return c;
            case ERROR:
                if ( ++error_count >= 20 ) {

⌨️ 快捷键说明

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