_PROGRAMMING WITH COMMUNCIATION PROTOCOL STACKS_ by Gordon Free [LISTING ONE] #ifndef FT_H #define FT_H #define HVC_FIRST 2 #define HVC_LAST 5 #define MAX_VCS HVC_LAST-HVC_FIRST+1 #define HDRSIZE MAX_APPHDRDATA+sizeof(BBHDR) /* Message command values */ #define FT2_SEND 3 #define FT2_RECEIVE 4 /* Bit fields for Blackbird events */ typedef union { struct { unsigned ListenCallback : 1; unsigned ConnectCallback : 1; unsigned ReadBreathe : 1; unsigned ReadCallback : 1; unsigned WriteBreathe : 1; unsigned WriteCallback : 1; unsigned DisconnectCallback : 1; } flags; int all; } EVENT_T; /* Structure for info on currently open file */ typedef struct _FILEDATA { FILE *hStream; /* stream handle */ char *pszName; /* name of file */ char *pszOpenMode; /* mode file is opened in "r", "w", etc. */ long lSize; /* size of file in bytes */ void *pvBuffer; /* ptr to buffer of file contents */ } FILEDATA_T; /* Structure for application service, one per VC */ typedef struct _SVCDATA { FILEDATA_T fdCurFile; /* current file info */ char bbhXmit[HDRSIZE]; /* BB hdr for sending */ char bbhRcv[HDRSIZE]; /* BB hdr for rcving */ char *pszRemoteName; /* name of remote machine */ long lBytesSoFar; /* num bytes xferred */ int fActive; /* TRUE if VC is active */ short sStatus; /* status of last BB event */ EVENT_T fEvent; /* BB event flags */ } SVCDATA_T; /*----- Callback routine prototypes ------*/ void cbRemService ( unsigned usNumSvcs /* Number of remote services */ ); SHORT cbListen ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ); SHORT cbConnect ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ); SHORT cbDisConnect ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ); SHORT cbRead ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Number of bytes read */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ); SHORT cbWrite ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Number of bytes sent */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ); #endif [LISTING TWO] /*==================================================================== (c) Copyright 1991, Gordon Free All Rights Reserved. -------------------------------------------------------------------- Filename: FT.C Project: DDJ Article $Author$ $Revision$ $Date$ Purpose: Sample file transfer program written for Blackbird API. ====================================================================*/ /*----- Includes ------*/ #include #include #include #include #include #include #include #include #include #include "vcapi.h" #include "ft.h" /*----- Local Defines and Typedefs ------ */ #define ESC 0x1B #define BUFF_SIZE (64*1024-1) /*------ Global Variables -----*/ int fUpdateServices=FALSE; /* remote service list has changed */ HANDLE hvcServer=HVC_NONE; /* vc handle of remote server */ SVCDATA_T svc[MAX_VCS]; /* array of app service info */ char acMachineName[MAX_MACHID] = "Unknown"; /*---- DisplayServices: prints a report of all available remote services ----*/ void DisplayServices(void) { int nServices; /* num of services reported */ REMSERVICES remserv; /* service info struct */ static HANDLE ahRS[10]; /* array of remote svc handles */ int i; puts("\nList of Remote Machines ..."); puts("---------------------------"); /* Fill in array of remote service handles */ nServices = vcQuery(ahRS, HBOUND(ahRS)); /* Report service handle and machine name for each remote service */ for (i=0; i 0) { psvc = &svc[vch-HVC_FIRST]; /* Read contents of file into buffer */ size = read(fileOut, psvc->fdCurFile.pvBuffer, BUFF_SIZE); close(fileOut); /* Put name into packet header */ pbbhXmit = (PBBHDR)&(psvc->bbhXmit[0]); strcpy(&(pbbhXmit->bAppData[1]), name); /* Indicate what remote is to do with this data */ pbbhXmit->bAppData[0] = FT2_RECEIVE; pbbhXmit->ubBytesToFollow = strlen(name)+2; vcWrite(vch,pbbhXmit, psvc->fdCurFile.pvBuffer, size , NULL, cbWrite, TWO_SECS); } else { perror("Error opening file"); } return size; } /*----SaveFile: write contents of buffer into file with specified name----*/ int SaveFile ( char *name, long lsize, void far *buffer ) { int size; int fileIn; printf("Saved file = %s\n", name); fileIn = open(name,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY); if (fileIn > 0) { size = (size_t) lsize; write(fileIn, buffer, size); close(fileIn); } else { perror("Error saving file"); } return 0; } /*---CheckEvents: polls each virtual connection looking for events that have been flagged by the various callback routines.---*/ void CheckEvents ( void ) { EVENT_T evt; SVCDATA_T *psvc; PBBHDR pbbhRcv; HANDLE vch; void *buff; int i; /* Loop through all possible virtual connections */ for (i=0, psvc=svc; ifEvent.all) { /* Get copy of event flags and then reset them. This is done with /* interrupts disabled so that we don't miss any events. */ _disable(); evt = psvc->fEvent; psvc->fEvent.all = FALSE; _enable(); /* ------------------- Listen --------------------------- */ /* Somebody connected to us, allocate storage and issue a */ /* read to get file transfer requests. */ if (evt.flags.ListenCallback) { puts("#Listen Callback"); pbbhRcv = (PBBHDR)&(psvc->bbhRcv[0]); if (psvc->fdCurFile.pvBuffer != NULL) { vcRead(HVC_FIRST+i,pbbhRcv, psvc->fdCurFile.pvBuffer , BUFF_SIZE, NULL, cbRead, TWO_SECS); } else { puts("Error allocating memory"); } /* Register a new service for use by other clients */ buff = malloc(BUFF_SIZE); if (buff != NULL) { vch = vcListen ("FXFR","DDJ", cbListen, cbDisConnect); svc[vch-HVC_FIRST].fdCurFile.pvBuffer = buff; } else { puts("Error allocating memory"); } } /* ------------------- Connect -------------------------- */ /* We've successfully connected to a remote server */ if (evt.flags.ConnectCallback) { puts("#Connect Callback"); hvcServer = HVC_FIRST+i; } /* ------------------- Read --------------------------- */ /* We've gotten a request, so process it! */ if ((evt.flags.ReadCallback) && (psvc->sStatus == 0)) { pbbhRcv = (PBBHDR)&(psvc->bbhRcv[0]); /* Is it a request to receive a file? */ if (pbbhRcv->bAppData[0] == FT2_RECEIVE) { SaveFile(&(pbbhRcv->bAppData[1]), psvc->lBytesSoFar , psvc->fdCurFile.pvBuffer); /* How about send a file? */ } else if (pbbhRcv->bAppData[0] == FT2_SEND) { SendFile(i+HVC_FIRST, &(pbbhRcv->bAppData[1])); /* No comprende */ } else { puts("Huh?"); } vcRead(HVC_FIRST+i,pbbhRcv, psvc->fdCurFile.pvBuffer , BUFF_SIZE, NULL, cbRead, TWO_SECS); } /* ------------------- Write --------------------------- */ if ((evt.flags.WriteCallback) && (psvc->sStatus == 0)) { puts("#Write Complete"); } /* ------------------- Disconnect ---------------------- */ /* Remote went byebye */ if (evt.flags.DisconnectCallback) { puts("#Disconnect Callback"); free(psvc->fdCurFile.pvBuffer); if (i+HVC_FIRST == hvcServer) hvcServer = HVC_NONE; } } } if (fUpdateServices) { fUpdateServices = FALSE; DisplayServices(); } } /*---DoUserRequests: check keyboard for activity and process user's command. Returns TRUE if user has not requested to exit.---*/ int DoUserRequests ( void ) { HANDLE vch; SVCDATA_T *psvc; PBBHDR pbbhXmit, pbbhRcv; int fContinue=TRUE; char filename[80]; void *buff; int c; if ( kbhit() ) { switch (c = getch()) { /* ------------------- Exit ----------------------------- */ case ESC: fContinue = FALSE; break; /* ------------------- Connect -------------------------- */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* Disconnect from current server, if any. */ /* Buffer will be released when disconnect completes */ if (hvcServer != HVC_NONE) { vcDisconnect(hvcServer); } /* Allocate buffer for new service */ buff = malloc(BUFF_SIZE); if (buff != NULL) { /* Connect to remote server */ vch = vcConnect(c-'0', cbConnect, cbDisConnect, TWO_SECS); svc[vch-HVC_FIRST].fdCurFile.pvBuffer = buff; } else { puts("Error allocating memory"); } break; /* ------------------- Send File ------------------------ */ case 's': case 'S': if (hvcServer != HVC_NONE) { psvc = &svc[hvcServer-HVC_FIRST]; printf("\nEnter name of file to send >"); scanf("%s", filename); SendFile(hvcServer, filename); } else { puts("You must establish a connection first!"); } break; /* ------------------- Receive File --------------------- */ case 'r': case 'R': if (hvcServer != HVC_NONE) { psvc = &svc[hvcServer-HVC_FIRST]; /* Set up ptrs to BB hdrs for receive and send */ pbbhXmit = (PBBHDR)&(psvc->bbhXmit[0]); pbbhRcv = (PBBHDR)&(psvc->bbhRcv[0]); /* Instruct remote server to send specified file */ printf("\nEnter name of file to receive >"); scanf("%s", &(pbbhXmit->bAppData[1])); pbbhXmit->bAppData[0] = FT2_SEND; pbbhXmit->ubBytesToFollow = strlen(&(pbbhXmit->bAppData[1]))+2; /* Post read before sending request so that we are ready */ /* for response */ vcRead(hvcServer,pbbhRcv, psvc->fdCurFile.pvBuffer , BUFF_SIZE, NULL, cbRead, TWO_SECS); /* Send request for file */ vcWrite(hvcServer,pbbhXmit, NULL, 0, NULL, cbWrite, TWO_SECS); } else { puts("You must establish a connection first!"); } default: break; } } return fContinue; } /*----main: initalize Blackbird and alternate between processing Blackbird events and user requests. ----*/ int main ( int argc, char **argv ) { HANDLE vch; void *buff; puts("Blackbird File Transfer Sample Program ver 1.0"); puts(" (c) Copyright 1991, Gordon Free."); puts(" All Rights Reserved.\n"); puts(vcVersion()); /* Parse machine name off command line */ if (argc >= 2) { strncpy(acMachineName, argv[1], sizeof(acMachineName-1)); acMachineName[sizeof(acMachineName-1)] = '\0'; } /* Initialize Blackbird and request notification of remote services */ vcInit(acMachineName,"DDJ"); vcRemsvcUpdate(cbRemService); /* Register as a server only if user gave a machine name */ if (argc >= 2) { buff = malloc(BUFF_SIZE); if (buff != NULL) { vch = vcListen ("FXFR","DDJ", cbListen, cbDisConnect); svc[vch-HVC_FIRST].fdCurFile.pvBuffer = buff; } else { puts("Error allocating memory"); goto finish; } } /* Alternate between checking for Blackbird events and processing user requests. */ do { CheckEvents(); } while (DoUserRequests()); finish: puts("Exiting ..."); vcEnd(); return 0; } [LISTING THREE] /*-------------------------------------------------------------------- (c) Copyright 1991, Gordon Free All Rights Reserved. ==================================================================== Filename: FT_CB.C Project: DDJ Article $Author$ $Revision$ $Date$ Purpose: Callback routines for Blackbird events ====================================================================*/ /*-----Includes-----*/ #include #include "vcapi.h" #include "ft.h" /*------Global Variables ------*/ extern SVCDATA_T svc[MAX_VCS]; /* service info for each VC */ extern int fUpdateServices; /* set TRUE to display remotes */ /* These routines are called at interrupt time, so no stack checking! */ #pragma check_stack(off) /* Called whenever remote service list changes */ void cbRemService ( unsigned usNumSvcs /* Number of remote services */ ) { fUpdateServices = TRUE; } /* Called when remote machine connects to us as a client */ SHORT cbListen ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ) { svc[hVC-HVC_FIRST].fEvent.flags.ListenCallback = TRUE; svc[hVC-HVC_FIRST].sStatus = SHORT1FROMULONG(ul2); svc[hVC-HVC_FIRST].fActive = TRUE; return (FALSE); } /* Called when remote server acknowledges our connection request */ SHORT cbConnect ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ) { svc[hVC-HVC_FIRST].fEvent.flags.ConnectCallback = TRUE; svc[hVC-HVC_FIRST].sStatus = SHORT1FROMULONG(ul2); svc[hVC-HVC_FIRST].fActive = TRUE; return (FALSE); } /* Called anytime a VC is broken */ SHORT cbDisConnect ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ) { svc[hVC-HVC_FIRST].fEvent.flags.DisconnectCallback = TRUE; svc[hVC-HVC_FIRST].fActive = FALSE; return (FALSE); } /* Called when we've receive a data buffer from remote */ SHORT cbRead ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Number of bytes received */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ) { svc[hVC-HVC_FIRST].fEvent.flags.ReadCallback = TRUE; svc[hVC-HVC_FIRST].lBytesSoFar = ul1; svc[hVC-HVC_FIRST].sStatus = SHORT1FROMULONG(ul2); return (FALSE); } /* Called when data has been successfully sent to remote */ SHORT cbWrite ( HANDLE hVC, /* Virtual connection handle */ ULONG ul1, /* Nothing at this time */ ULONG ul2 /* Status = SHORT1FROMUL( ul2 ) */ ) { svc[hVC-HVC_FIRST].fEvent.flags.WriteCallback = TRUE; svc[hVC-HVC_FIRST].sStatus = SHORT1FROMULONG(ul2); return (FALSE); } #pragma check_stack()