📄 scratchp.c
字号:
/* Scratch Protocol for 'TCP/IP Lean' (c) Iosoft Ltd. 2000
This software is only licensed for distribution with the book 'TCP/IP Lean',
and may only be used for personal experimentation by the purchaser
of that book, on condition that this copyright notice is retained.
For commercial licensing, contact license@iosoft.co.uk
This is experimental software; use it entirely at your own risk. The author
offers no warranties of any kind, including its fitness for purpose. */
/*
** v0.01 JPB 28/10/99
** v0.02 JPB 1/11/99 Removed 'flags' from packet format
** v0.03 JPB 2/11/99 Reintroduced sequencing flags
** v0.04 JPB 3/11/99 Added SLIP support
** v0.05 JPB 4/11/99 Added config-file support
** v0.06 JPB 5/11/99 Revamped packet driver to support multiple classes
** v0.07 JPB 8/1/99 Updated packet format to use command string
** v0.08 JPB 8/1/99 Adapted Tx, Rx so txlen and rxlen include header length
** v0.09 JPB 9/11/99 Added sequencing
** v0.10 JPB 9/11/99 Added data queues
** v0.11 JPB 11/11/99 Improved sequence logic
** v0.12 JPB 12/11/99 Added circular buffer option to packet driver
** v0.13 JPB 15/11/99 Added network Tx and Rx circular buffers
** v0.14 JPB 16/11/99 Improved support for direct-drive of NE2000 card
** v0.15 JPB 16/11/99 Added SCRATCHE for use with DJGPP compiler
** v0.16 JPB 17/11/99 Fixed reentrancy problem on receive
** v0.17 JPB 18/11/99 Removed references to min() and max()
** v0.18 JPB 19/11/99 Completely revamped state machine
** v0.19 JPB 22/11/99 Added timeouts
** v0.20 JPB 23/11/99 Added WIN32 support
** v0.21 JPB 25/11/99 Fixed SLIP interface
** v0.22 JPB 26/11/99 Improved SCRATCHP state-machine
** v0.23 JPB 29/11/99 Added 'dir' command
** v0.24 JPB 30/11/99 Added 'get' and 'put'
** v0.25 JPB 1/12/99 Promoted 'seq' and 'ack' to 32-bit values
** v0.26 JPB 6/12/99 Added 3COM driver
** v0.27 JPB 30/12/99 Improved DJGPP compatibility - removed MAXPATH definition
** v0.28 JPB 6/4/00 Updated to use latest version of net drivers
** v0.29 JPB 13/8/00 Updated copyright notice
*/
#define VERSION "0.29" /* This software version */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>
#include <string.h>
/* Override the default circular buffer size used in TCPFS.H */
#define _CBUFFLEN_ 0x1000 /* Circ buffer size: MUST be power of 2 */
#include "ether.h"
#include "netutil.h"
#include "net.h"
#include "scratchp.h"
#define SCRATCHPVER 1 /* Protocol version number */
#define TESTLEN SCRATCHPDLEN /* Max size of 1 block of test data */
#define TESTBUFFLEN (TESTLEN*2) /* Size of test data buffer */
#define TESTINC 1 /* Increment value for test data */
#define CFGFILE "tcplean.cfg" /* Default config filename */
#define NDIAGS 40 /* Number of diagnostic header entries */
#define DIAG_TX 1 /* Identifiers for diagnostic Tx, Rx hdrs */
#define DIAG_RX 2
#define ERRTIME 2 /* Error timeout in sec */
#define RETRIES 2 /* Number of error retries */
#define BLOCKLEN 255 /* Len of file xfer blk (excl. len byte) */
#define FILENAMELEN 40 /* Max length of get/put filename */
char cfgfile[MAXPATH+5]=CFGFILE; /* Config filename */
BYTE testdata[TESTBUFFLEN]; /* Test data buffer */
LWORD testlen;
int txoff, rxoff; /* Tx, Rx offsets into test data */
GENFRAME genframe; /* Receive/transmit frame */
long txcount; /* Count of Rx bytes */
CBUFF txbuff={_CBUFFLEN_}; /* Circular buffer for transmit */
CBUFF rxbuff={_CBUFFLEN_}; /* Circular buffer for receive */
int statedebug, pktdebug, sigdebug; /* Flags to enable diagnostic printout */
char locid[IDLEN+1]; /* My (local) ident string */
char remid[IDLEN+1]; /* Remote node ident string */
BYTE remaddr[MACLEN]; /* Remote node address */
extern BYTE bcast[MACLEN]; /* Broadcast address */
char netcfg[40]; /* Network config string */
int connstate; /* Network connection state */
int appstate; /* Application state */
char *connstates[]={STATENAMES}; /* Strings for connection states */
char *appstates[]={APPNAMES}; /* Diagnostic strings for states */
char *signames[] = {SIGNAMES};
BYTE apptemp[TESTLEN+1]; /* Temporary storage for app. data */
SCRATCHPHDR diaghdrs[NDIAGS]; /* Diagnostic storage of SCRATCHP headers */
int diagidx; /* Index into dignostic storage */
char kbuff[81]; /* Keyboard buffer */
WORD errtimer, errcount; /* Timer & counter for errors */
char *offon[] = {"off", "on"}; /* Off/on strings */
FILE *fhandle; /* File handle for GET and PUT */
char filename[FILENAMELEN+1]; /* File name for GET and PUT */
long filelen; /* File length for GET and PUT */
int breakflag; /* Flag to indicate ctrl-break pressed */
/* Prototypes */
int get_pkts(GENFRAME *nfp);
void prompt_user(void);
int do_scratchp(GENFRAME *nfp, int rxlen, int signal);
int do_apps(CBUFF *rxb, CBUFF *txb, int sig);
int do_dir(CBUFF *txb);
void newconnstate(int state);
void newappstate(int state);
void disp_sig(int sig);
int make_scratchp(GENFRAME *nfp, BYTE *dest, char *cmd, BYTE flags,
void *data, int dlen);
int make_scratchpds(GENFRAME *nfp, BYTE *dest, char *cmd, BYTE flags, char *str);
int put_scratchp(GENFRAME *nfp, WORD txlen);
int is_scratchp(GENFRAME *nfp, int len);
int swap_scratchp(GENFRAME *nfp);
void disp_scratchphdr(SCRATCHPHDR *sph);
void disp_scratchp(GENFRAME *nfp);
void dump_diags(void);
int read_cfg(char *fname);
int mygets(char *buff, int maxlen);
int main(int argc, char *argv[])
{
char k, cmdkey=0;
int i, keysig, connsig, sstep=0;
WORD frametype, txlen=0;
GENFRAME *nfp;
printf("SCRATCHP v" VERSION " "); /* Sign on */
if (!read_cfg(argc>1 ? argv[1] : 0)) /* Read config file */
{
printf("ERROR: invalid configuration file\n");
exit(1);
}
printf("\n"); /* Make random test data */
for (i=0; i<TESTLEN; i++)
testdata[i] = testdata[i+TESTLEN] = (BYTE)rand()&0xff;
nfp = &genframe; /* Open net driver.. */
nfp->g.dtype = frametype = open_net(netcfg); /* ..get frame type */
if (!frametype)
{
printf("ERROR: can't open network driver\n");
exit(1);
}
else
{
newappstate(STATE_IDLE); /* Set default states */
newconnstate(APP_IDLE);
timeout(&errtimer, 0); /* Refresh timeout timer */
while (!breakflag && cmdkey!='Q') /* Main command loop.. */
{
txlen = keysig = connsig = 0;
prompt_user(); /* Prompt user on state change */
if (sstep || kbhit()) /* If single-step or keypress..*/
{
k = getch(); /* ..get key */
if (sstep)
timeout(&errtimer, 0); /* If single-step, refresh timer */
cmdkey = toupper(k); /* Decode keystrokes.. */
switch (cmdkey) /* ..and generate signals */
{
case 'I': /* 'I': broadcast ident */
if (connstate != STATE_CONNECTED)
printf("Broadcast ident request\n");
keysig = SIG_USER_IDENT;
break;
case 'O': /* 'O': open connection */
printf("Open connection: remote ident (RETURN if any)? ");
mygets(remid, IDLEN);
if (kbuff[0])
printf("Contacting '%s'..\n", remid);
else
printf("Contacting any node..\n");
keysig = SIG_USER_OPEN;
break;
case 'D': /* 'D': directory */
printf("Directory of remote node\n");
if (connstate != STATE_CONNECTED)
printf("Error: not connected\n");
else
keysig = SIG_USER_DIR;
break;
case 'E': /* 'E': echo data test */
if (connstate != STATE_CONNECTED)
printf("Error: not connected\n");
else
{
printf("Echo test\n");
keysig = SIG_USER_ECHO;
}
break;
case 'G': /* 'G': get file from rmeote */
if (connstate != STATE_CONNECTED)
printf("Error: not connected\n");
else
{
printf("Filename to get? ");
if (mygets(filename, FILENAMELEN))
keysig = SIG_USER_GET;
}
break;
case 'P': /* 'P': put file into remote */
if (connstate != STATE_CONNECTED)
printf("Error: not connected\n");
else
{
printf("Filename to put? ");
if (mygets(filename, FILENAMELEN))
keysig = SIG_USER_PUT;
}
break;
case 'C': /* 'C': close connection */
printf("Closing connection..\n");
keysig = SIG_USER_CLOSE;
break;
case 'S': /* 'S': single-step */
sstep = !sstep;
printf("Single-step %s\n", sstep ? "on" : "off");
break;
case '?': /* '?': dump dignostic log */
printf("\n");
dump_diags();
break;
}
} /* Feed kbd signal to application */
connsig = do_apps(&rxbuff, &txbuff, keysig);
if (!connsig && connstate!=STATE_IDLE &&
timeout(&errtimer, ERRTIME))/* If idle and timeout.. */
{ /* ..check error counter.. */
if (errcount++ < RETRIES)
connsig = do_apps(&rxbuff, &txbuff, SIG_TIMEOUT);
else /* ..signal 'timeout' or 'fail' */
connsig = do_apps(&rxbuff, &txbuff, SIG_FAIL);
} /* Keep SCRATCHP alive */
txlen = do_scratchp(nfp, 0, connsig);
put_scratchp(nfp, txlen); /* Transmit packet (if any) */
txlen = get_pkts(nfp); /* Check receive packets */
put_scratchp(nfp, txlen); /* Transmit response (if any) */
poll_net(nfp->g.dtype); /* Keep net drivers alive */
}
if (connstate) /* Shutdown: still connected? */
{
printf("Closing connection..");
while (get_pkts(nfp)) /* Discard all receive packets.. */
putchar(','); /* ..then send 'stop' ..*/
txlen = make_scratchp(nfp, remaddr, 0, FLAG_STOP, 0, 0);
put_scratchp(nfp, txlen);
while (get_pkts(nfp)) /* ..then discard any more.. */
putchar('.'); /* (to ensure Tx buffer flushed) */
putchar('\n');
poll_net(nfp->g.dtype); /* Keep net drivers alive */
}
}
close_net(frametype); /* Close network drver */
return(0);
}
/* Demultiplex incoming packets */
int get_pkts(GENFRAME *nfp)
{
int rxlen, txlen=0;
if ((rxlen=get_frame(nfp)) > 0) /* If any packet received.. */
{
if (is_scratchp(nfp, rxlen)) /* If SCRATCHP.. */
{
swap_scratchp(nfp); /* ..do byte-swaps.. */
txlen = do_scratchp(nfp, rxlen, 0); /* ..action it.. */
}
} /* ..and maybe return a response */
return(txlen); /* (using the same pkt buffer) */
}
/* Prompt user depending on connection & application states, & kbd signal */
void prompt_user(void)
{
static int lastappstate=0, lastconnstate=-1;
static long lastfilelen=0;
if (lastfilelen != filelen)
printf("%lu bytes \r", filelen);
lastfilelen = filelen;
if (appstate != lastappstate)
{
if (appstate == APP_FILE_RECEIVER)
printf("Receiving '%s'..\n", filename);
if (appstate == APP_FILE_SENDER)
printf("Sending '%s'..\n", filename);
else if (lastappstate==APP_FILE_SENDER ||
lastappstate==APP_FILE_RECEIVER)
{
if (!filelen)
printf("ERROR: file not found\n");
else
printf("%lu bytes transferred\n", filelen);
}
if (appstate == APP_ECHO_SERVER)
printf("Echo server running... [C]lose connection?\n");
else if (appstate == APP_ECHO_CLIENT)
printf("Echo client running... [C]lose connection?\n");
lastappstate = appstate;
}
else if (connstate != lastconnstate)
{
if (connstate == STATE_IDLE)
printf("Connection closed: [I]dent, [O]pen, [Q]uit?\n");
if (connstate == STATE_CONNECTED)
{
printf("Connected");
if (*remid)
printf(" to '%s'", remid);
printf(": [D]ir [G]et [P]ut [E]cho [C]lose?\n");
}
lastconnstate = connstate;
}
}
/* SCRATCHP connection state machine; given packet buffer and Rx len
** If Rx len is non-zero, process the incoming SCRATCHP packet,
** Otherwise, only check for state chamges or timeouts,
** Return non-zero packet length (incl. SCRATCHP hdr) if responding */
int do_scratchp(GENFRAME *nfp, int rxlen, int sig)
{
WORD n, trylen, tx=0, txlen=0, crlen=0, dlen=0, txw;
LWORD oldrx, rxw, acked=0;
static LWORD txack;
char *errstr=0, temps[22];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -