Buy ebook TCP IP Lean Web Servers for Embedded Systems 2nd Edition Jeremy Bentham cheap price
Buy ebook TCP IP Lean Web Servers for Embedded Systems 2nd Edition Jeremy Bentham cheap price
https://ebookgate.com/product/web-technologies-tcp-ip-web-java-
programming-and-cloud-computing-3rd-edition-achyut-s-godbole/
ebookgate.com
https://ebookgate.com/product/the-abcs-of-tcp-ip-2nd-edition-gilbert-
held/
ebookgate.com
https://ebookgate.com/product/tcp-ip-clearly-explained-pete-loshin/
ebookgate.com
https://ebookgate.com/product/the-correspondence-of-jeremy-bentham-
volume-1-1752-to-1776-1st-edition-jeremy-bentham/
ebookgate.com
The Correspondence of Jeremy Bentham Volume 2 1777 To 1780
1st Edition Jeremy Bentham
https://ebookgate.com/product/the-correspondence-of-jeremy-bentham-
volume-2-1777-to-1780-1st-edition-jeremy-bentham/
ebookgate.com
https://ebookgate.com/product/the-correspondence-of-jeremy-bentham-
volume-5-january-1794-to-december-1797-1st-edition-jeremy-bentham/
ebookgate.com
https://ebookgate.com/product/tcp-ip-network-administration-3rd-
edition-craig-hunt/
ebookgate.com
https://ebookgate.com/product/special-edition-using-tcp-ip-niit-usa-
inc/
ebookgate.com
Jeremy Bentham
CMP Books
Lawrence, Kansas 66046
CMP Books
CMP Media LLC
1601 West 23rd Street, Suite 200
Lawrence, Kansas 66046
USA
www.cmpbooks.com
Designations used by companies to distinguish their products are often claimed as trademarks. In
all instances where CMP Books is aware of a trademark claim, the product name appears in initial
capital letters, in all capital letters, or in accordance with the vendor’s capitalization preference.
Readers should contact the appropriate companies for more complete information on trademarks
and trademark registrations. All trademarks and registered trademarks in this book are the prop-
erty of their respective holders.
Copyright © 2002 by Jeremy Bentham, except where noted otherwise. Published by CMP Books,
CMP Media LLC. All rights reserved. Printed in the United States of America. No part of this pub-
lication may be reproduced or distributed in any form or by any means, or stored in a database or
retrieval system, without the prior written permission of the publisher; with the exception that the
program listings may be entered, stored, and executed in a computer system, but they may not be
reproduced for publication.
The programs in this book are presented for instructional value. The programs have been carefully
tested, but are not guaranteed for any particular purpose. The publisher does not offer any war-
ranties and does not guarantee the accuracy, adequacy, or completeness of any information herein
and is not responsible for any errors or omissions. The publisher assumes no liability for damages
resulting from the use of the information in this book or for any infringement of the intellectual
property rights of third parties that would result from the use of this information.
ISBN: 1-57820-108-X
To Fred, Ilse, and Jane
Table of Contents
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
The Lean Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Embedded Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xii
The Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
The Network . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
The Operating System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv
The Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv
The Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
Chapter 1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 1
The Lean Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2
Software Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
Network Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
Device Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
Configuration File Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
Process Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
State Machines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
Buffering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21
Coding Conventions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29
v
vi Table of Contents
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .547
xi
xii Preface
As time went by and my TCP/IP software matured, the Web became increasingly impor-
tant. My industrial customers would browse the Web at home or work and could see the
advantages of using a Web browser for remote control and to monitor their industrial equip-
ment. TCP became just a vehicle for conveying Web pages. The focus shifted from “I want
TCP/IP on my system” to “I want my system to produce Web pages,” and these pages always
included dynamic real-time data.
History was repeating itself; the software to produce these dynamic Web pages was
designed for large multiuser systems, and I couldn’t find small-scale implementations that
were usable on simple, low-cost embedded systems hardware. I needed:
• a description of the techniques to insert live data into Web pages and
• some simple platform-independent code that I could adapt for specific projects.
Having implemented many small-scale Web servers of my own (generally an 80188 pro-
cessor with 64Kb of ROM), I was delighted to hear of a 256-byte implementation on a
microcontroller, although I was disappointed to discover that it could only produce fixed
pages from its ROM, with no dynamic data. I wanted to know:
• what compromises were associated with implementing TCP and a Web server on a
microcontroller and
• what techniques I could use to insert dynamic data into its Web pages.
Almost by chance, the first edition of this book included a miniature Web server running
on a PICmicro®1. I wasn’t the first to create such a server, but I was the first to publish a full
description of the techniques used, including full source code. The success of the initial
offering prompted me to update this book to broaden the range of networks and protocols
supported on the PICmicro. Despite the “Web servers” in the title of this book, there are many
ways to transfer data across a network, and I wanted to provide working examples of their
use.
Hopefully, you’ll find the answers you want in this book.
Embedded Systems
The term “embedded system” may be new to some of you and require some explanation,
even though you use embedded systems every day of your life. Microwave ovens, TVs, cars,
elevators, and aircraft are all controlled by computers, which don’t necessarily have a screen,
keyboard, and hard disk. A computer could be controlling your car without your knowledge:
an engine management system takes an input signal from the accelerator and provides out-
puts that control the engine.
These computers are embedded in a system, of which they may be only a small compo-
nent. The embedded system designer may have to work within tight constraints of size,
weight, power consumption, vibration, humidity, electrical interference, and above all, cost
and reliability. The PC architecture has been adapted for embedded systems operation, and
rugged single-board computers (SBCs) are available from a wide variety of suppliers, together
with the necessary add-on cards to process real-world signals. The ultimate in miniaturization
The Hardware
At the time of writing, the PC hardware platform, although distinctly showing its age, cannot
be ignored. The second-hand market is awash with perfectly serviceable PCs that don’t con-
tain the latest and fastest technology but are more than adequate for your purposes. There are
low-cost industrial SBCs that have a PC core, standard network interface, and the ability to
accept interface cards for a wide variety of real-world signals.
My software will run on all these PC compatibles, and even on PC incompatibles (such as
the 80188 CPU) with a very small amount of modification, because I have clearly isolated all
hardware and operating-system dependencies.
In addition to the PC code, I have included a miniature TCP/IP stack and Web server for a
Microchip PICmicro® microcontroller, using the Custom Computer Services PCM C com-
piler. A standard PICmicro evaluation board can be hand-modified to include the appropriate
peripherals (a circuit diagram is given), or a complete off-the-shelf board can be purchased
instead. I won’t pretend that it would be easy to adapt this software to another processor, but
there is an in-depth analysis of the difficulties associated with microcontroller implementa-
tions, which would give you a very significant head-start if working with a different CPU.
The Network
Base-level Ethernet (10Mbit) is still widely available; complete kits, including interface cards
and cabling, are available at low cost from computer retailers. My software directly supports
two of the most popular Ethernet cards — Novell NE2000 compatibles and 3COM 3C509
— and can potentially (if using the Borland Compiler) support other cards through the packet
driver interface, though the direct hardware interface approach is preferable because it makes
experimentation and debugging much easier.
xiv Preface
When developing network software, you are very strongly advised to use a separate scratch
network, completely isolated from all other networks in the building. Not only does debug-
ging become much easier, but you also avoid the possibility of disrupting other network traffic.
It is remarkable how a minor change to the software can result in a massive increase in the net-
work traffic and a significant disruption to other network users. You have been warned!
The software also supports serial links through SLIP (serial line Internet protocol), and a
crossover serial cable between two PCs can, to a certain extent, be used as a substitute for a
real network.
The Borland compilers, though ostensibly obsolete, may be found on the CD-ROM of
some C programming tutorial books or may be bundled with their 32-bit cousins. The
The Software xv
high-level software can be compiled using all of these environments, but I have not been so
fortunate with the low-level network interface code.
• The Borland compilers are the easiest to use because they allow the use of interrupts
without the need for machine code inserts and so can support the full range of network
interfaces.
• With the Microsoft compiler, the network card and SLIP interfaces are supported, but the
packet driver interface is not.
• Only the direct network card interface is supported when using the DJGPP compiler.
Because the direct network card interface is the easiest to debug, and hence more suitable
for experimentation, this restriction isn’t as onerous as it might appear.
If your favorite compiler isn’t on the list, I apologize for the omission, but I am very
unlikely to add it. Each compiler represents a very significant amount of testing, and my pref-
erence is to reduce, rather than increase, the number of compilers supported. If your compiler
is similar to the above (for example, an earlier version), then you should have little or no
adaptation work to perform, though I can’t comment on any compiler I haven’t tried.
PICmicro Compilers. The early software used the Custom Computer Services (CCS) PCM
v2.693, but later developments are broadly compatible with the CCS and Hitech compilers
for the PIC16xxx and PIC18xxx series microcontrollers. A detailed discussion of compatibil-
ity issues is beyond the scope of this chapter. See Appendix D and the software release notes
on the CD-ROM for more information.
The Software
The enclosed CD-ROM contains complete source code to everything in this book so that you,
as purchaser of the book, can experiment. However, the author retains full copyright to the
software, and it may only be distributed in conjunction with the book; for example, you may
not post any of the source code on the Internet or misrepresent its authorship by extracting
fragments or altering the copyright notices.
If you want to sell anything that contains this software, a license is required for the
“incorporation” of the software into each commercial product. This normally takes the form
of a one-off payment that allows unlimited incorporation of any executable code derived
from this source. There are no additional development fees (apart from purchase of the
book), and license fees are kept low to encourage commercial usage. Full details and software
updates are on the Iosoft Ltd. Web site at www.iosoft.co.uk.
Acknowledgments
The author owes a profound debt of gratitude to Berney Williams of CMP Books for being so
keen on this project, Anthony Winter for his proofreading skills and advice, Glen Middleton
of Arcom Control Systems Ltd. and Adrian Nicol of Io Ltd. for their help with the hardware,
and, above all, to Jane McSweeney (now Jane Bentham) for her continued enthusiasm, sup-
port, and wonderful cakes.
xvi Preface
1
Chapter 1
Introduction
The Lean Plan
This is a software book, so it contains a lot of code, most of which has been specially written
(or specially adapted) for the book. The software isn’t a museum piece, to be studied in a
glass case, but rather a construction kit, to promote understanding through experimentation.
The text is interspersed with source code fragments that illustrate the points being discussed
and provide working examples of theoretical concepts. All the source code in the book, and
complete project configurations for various compilers, are on the enclosed CD-ROM.
When I started writing this book, I intended to concentrate on the protocol aspects of
embedded Web servers, but I came to realize that the techniques of providing dynamic con-
tent (on-the-fly Web page generation) and client/server data transfers were equally important,
yet relatively unexplored. Here are some reasons for studying this book.
TCP/IP. You want to understand the inner workings of TCP/IP and need some tools and
utilities to experiment with.
Dynamic Web Content. You have an embedded TCP/IP stack and need to insert dynamic
data into the Web pages.
1
2 Chapter 1: Introduction
Miniaturization. You are interested in incorporating a miniature Web server in your sys-
tem but need to understand what resources are required and what compromises will have to
be made.
Prototyping. You want a prebuilt Web server that you can customize to evaluate the con-
cept in a proposed application.
Data transfer. You need to transfer data across a network using standard protocols.
Of course, these areas are not mutually exclusive, but I do understand that you may not
want to read this book in a strict linear order. As far as possible, each chapter stands on its own
and provides a stand-alone utility that allows you to experiment with the concepts discussed.
I won’t assume any prior experience with network protocols, just a working knowledge of
the C programming language. In the Preface, I detailed the hardware and software you would
need to take full advantage of the source code in the book. You don’t have to treat this book
as a hands-on software development exercise, but it would help your understanding if you
did.
Getting Started
On the CD-ROM, you’ll find the directory tcplean with several subdirectories.
You’ll also find the directory chipweb with a two subdirectories containing the files for
Chapters 12–16.
ARCHIVE zip files containing older versions of the ChipWeb source code
P16WEB latest ChipWeb source code
Executable copies of all the utilities, sample configuration files, and a README file with any
late-breaking update information are in tcplean. Preferably, the complete directory tree d:\
tcplean (where d: is the CD-ROM drive) should be copied to c:\tcplean on your hard disk,
1. PICmicro® is the registered trademark of Microchip Technology Inc.; PICDEM.net™ is the trade-
mark of Microchip Technology Inc.
Getting Started 3
and d:\chipweb to c:\chipweb. If a different directory path is used, it will be necessary to edit
the compiler project files.
The utilities read a configuration file to identify the network parameters and hardware
configuration; the default is tcplean.cfg, read from the current working directory. It is
unlikely that this will contain the correct hardware configuration for your system, so it is
important that you change the configuration file before running any of the utilities. See
Appendix A for details. If you attempt to use my default configuration without checking its
suitability, it may conflict with your current operating system settings and cause a lockup.
It is possible to browse the source files on the CD-ROM and execute the utilities on it
without loading them onto your hard disk, though you still need a to adapt the configuration
file and store it in the current working directory.
c:\>cd tcplean
c:\tcplean>d:\tcplean\ping 10.1.1.1
This would execute the utility on the CD-ROM using the configuration file.
c:\tcplean\tcplean.cfg
The default configuration file may be overridden using the -c command-line option.
c:\tcplean>ping -c slip 172.16.1.1
This uses the alternative configuration file slip.cfg, which makes it possible to experiment
with multiple network configurations without having to rebuild the software each time.
If you are in any doubt about the command-line arguments for a utility, use the -? option.
c:\>cd tcplean
c:\tcplean>ping -?
Some of the utilities have the same name as their DOS counterparts (because they do the same
job), so it is important to change to tcplean before attempting to run them.
A final word of warning: I strongly recommend that you create a new “scratch” network
for your experimentation that is completely isolated from all other networks in the building.
It is a very bad idea to experiment on a “live” network.
Network Configuration
The DOS software in this book supports the following network hardware.
Some combinations of network hardware and compiler are not supported. Consult Appendix
A and the README file for full information on the network configuration options.
4 Chapter 1: Introduction
Compiler Configuration
Executable versions of all the DOS projects are included within the tcplean directory, so ini-
tial experimentation can take place without a compiler. The project files for each compiler
reside in a separate directory, as described earlier, and all the compiler configuration informa-
tion resides within the project files. All the source code files reside in a single shared directory.
There are a few instances where compiler-specific code (generally Win32-specific code) must
be generated, in which case automatic conditional compilation is used.
Load specific projects for the following compilers:
Borland C++ v3.1 In a DOS box, change to the BC31 directory and run BC using the
project filename.
c:\>cd \tcplean\bc31
c:\tcplean\bc31>bc ping.prj
Borland C++ v4.52 Launch the Integrated Development Environment (IDE) and select
Project–Open Project and the desired IDE file in the BC45 directory.
DJGPP and RHIDE Launch the RHIDE IDE and select Project–Open Project and the
desired GPR file in the DJGPP directory.
Visual C++ v6 Launch the IDE and select File–Open Workspace and the desired DSW file
in the VC6 directory.
Custom Computer Services PCM The PICmicro cross-compiler uses a completely differ-
ent set of source code that resides in the PCM directory. Open a DOS box and change direc-
tory to \tcplean\pcm. Copy the necessary system files (16C76.h and ctype.h) into this
directory from the standard PCM distribution. Run the PCM compiler, specifying PWEB.C on
the command line.
c:\>cd \tcplean\pcm
c:\tcplean\pcm>copy \picc\examples\16c76.h
c:\tcplean\pcm>copy \picc\examples\ctype.h
c:\tcplean\pcm>\picc\pcm pweb.c
I run the PCM compiler from within the IDE of an emulator; see the emulator documenta-
tion for details on how to do this. When first using such a setup, make a minor but readily
observable change, rebuild, and check that the new executable really has been downloaded
into the emulator. It is all too easy to omit a vital step in the rebuild chain, such that the old
file is still being executed.
Software Introduction
For the rest of this chapter, I’ll look at the low-level hardware and software functions needed
to support software development.
• network hardware characteristics
• network device drivers
• process timing
• state machines
• buffering
• coding conventions
Even if you’re keen to get on with the protocols, I suggest you at least skim this material,
since it forms the groundwork for the later chapters.
Network Hardware
To help in setting up a serial or network link, I’ve included some sample configurations in
Appendix A, together with the relevant software installations. Assuming one or both are
installed, I will examine their characteristics with a view to producing the low-level hardware
device drivers.
Figure 1.1 shows two types of networks (two “topologies”): the older style bus network,
where the computers are connected to a single common cable, and the newer star network,
where the computers are individually connected to a common box (a hub), which electrically
copies the network signals from one computer to all others. Fortunately, the operation of an
Ethernet hub is completely transparent to the software, so you can still treat the network as if
the computers were sharing a common cable.
Both computers have equal access to the serial link. The hardware simply acts as a
“data pipe” between the two computers and does not prioritize one computer above another.
There are only two computers (nodes) on the network. Throughout this book, I’ll use
“node” as shorthand for “a computer on the network.” Insofar as the simple serial link con-
stitutes a network, it is clear that if one node transmits a message, it can only be received by
the other node and no others.
A node can transmit data at any time. This is technically known as a full duplex system;
both computers can transmit and receive simultaneously without any clash of data signals.
Message delivery is reliable. The assumption is that the two nodes are close to each
other, with a short connecting cable, so there will be no corruption of data in transit. The pre-
dominant failure mode is a catastrophic link failure, such as a disconnection of the cable or a
node powering down.
The serial data is a free-format stream of bytes, with little or no integrity checking.
The serial hardware is only designed for short-distance interconnects, so it has a very simple
error-checking scheme (parity bit), which is often disabled. To guarantee message integrity,
error checking must be provided in software.
There is no limit on message size. Because the serial data is simply a stream of bytes
with no predefined start or end, there is no physical restriction on its length.
There is no need for addressing Because there is only one possible recipient for each
message, there is no need to include an address identifying that recipient.
All nodes have a 48-bit address that is unique on the network. Just as a postal address
uniquely identifies a specific location in the world, so a node address (generally known as a
media access and control, or MAC, address) must uniquely identify a node on the network. In
fact, the standardization of Ethernet guarantees each node address to be also unique in the
world; you can mix and match Ethernet adaptors from different manufacturers, secure in the
knowledge that no two will have the same 48-bit address.
Any node may transmit on the network when it is idle. If a node is to communicate
with another, it must wait for all others to be silent before it can transmit. Because all nodes
are equal, they need not ask permission before transmitting on the network; they simply wait
for a suitable gap in the network traffic.
Message delivery is unreliable. “Unreliable? Why don’t you fix it?” Networks are, by
their very nature, an unreliable way of sending data. The failure modes range from the cata-
strophic (the recipient’s computer is powered down or physically disconnected from the net-
work) to the intermittent (a packet has been corrupted by collision or electrical interference).
The network hardware has the ability to detect and compensate for some intermittent faults
(e.g., a retry in the event of a packet collision), but eventually an error will occur that has to
be handled in software, so the software must assume the network is unreliable.
All data on the network is in blocks (frames) with a defined beginning and end and
an integrity check. Nodes that are going to transmit when they want need a defined for-
mat for their transmissions so that others know when they are starting or finishing, assuming
each transmission is a block with start and end markers and some form of checking (usually a
CRC, or cyclic redundancy check) to ensure it hasn’t been damaged in transit. The name
given to this block differs according to the network used; Ethernet blocks are called frames.
The network can send a maximum of 1,500 bytes of data per frame. All networks
have an upper limit on the size of data they can carry in one frame. This is called the maxi-
mum transfer unit, or MTU. Ethernet frames can contain up to 1.5Kb, but TCP/IP software
will work satisfactorily with a lot smaller MTU.
All messages are equipped with a source and destination address. Frames are usu-
ally intended for a single recipient; this is known as unicast transmission. Occasionally, it may
be necessary to send a frame to all nodes on the network, which is a broadcast transmission.
8 Chapter 1: Introduction
Device Drivers
It would be helpful if the driver software presented a common interface to the higher-level
code, but it is clear from the preceding analysis that there are significant differences; these are
summarized in Table 1.1.
SLIP
Fortunately, one of the TCP/IP families of standards, SLIP, provides exactly this functionality. It
uses simple escape codes inserted in the serial data stream to signal block boundaries as follows.
• The end of each block is signaled by a special End byte, with a value of C0h.
• If a data byte equals C0h, two bytes with the values DB, DC are sent instead.
• If a data byte equals DBh, two bytes with the values DB, DD are sent instead.
Additionally, most implementations send the End byte at the beginning of each block to
clear out garbage characters prior to starting the new message (Figure 1.2).
There is effectively no limit to the size of the data block, but you have to decide on some
value in order to dimension the data buffers. With old slow serial links, a maximum size of
256 bytes was generally used, but you’ll be using faster links, and a larger size is better for
minimizing protocol overhead. By convention, 1,006 bytes is often used.
The encoding method can best be illustrated by an example (Figure 1.3). Assume a six-
byte block of data with the hex values BF C0 C1 DB DC is sent; it is expanded to C0 BF DB DC C1
DB DD DC C0.
Device Drivers 9
Figure 1.3 SLIP example.
END END
BFh DBh DCh C1h DBh DDh DCh
C0h C0h
The original data has nearly doubled in size, due to my deliberately awkward choice of
data values. In normal data streams, the overhead is much lower.
Modem Emulation
An additional problem with serial networking is that most PCs are configured to use a
modem (Figure 1.4) to an Internet Service Provider (ISP).
I’ll create a Web server, but instead of two modems, I’ll use a serial (null modem) cable to
link it to the browser. The problem is that my Web server will then receive the browser’s com-
mands to its modem. If these go unanswered, the browser will assume its modem is faulty and
report this to the user.
The easiest solution is to include a simple modem emulator in your serial driver so that the
browser is fooled into thinking it is talking to a modem. Because modem commands are text
based, you can easily distinguish between them and the SLIP message blocks prefixed by the
delimiter character (C0h); when the latter appears, disengage the modem emulation.
Modem commands begin with the uppercase letters AT, followed by zero or more alpha-
betic command letters, with alphabetic or numeric arguments, terminated by an ASCII car-
riage return (<CR>) character. The usual reply are the uppercase letters OK, followed by a
carriage return and line feed (<CR><LF>). Table 1.2 shows a few typical command–response
10 Chapter 1: Introduction
sequences for a simple modem. This emulation would respond OK to all commands; this is
normally sufficient.
Table 1.2 Modem command–response sequences.
/* Ethernet (DIX) frame; data size is frame size minus header & CRC */
#define ETHERMTU (MAXFRAME-sizeof(ETHERHDR))
typedef struct {
ETHERHDR h; /* Header */
BYTE data[ETHERMTU]; /* Data */
LWORD crc; /* CRC */
} ETHERFRAME;
This is the basic Ethernet frame, also known as Ethernet 2 (Ethernet 1 is obsolete), or DIX
Ethernet (after its creators, DEC, Intel, and Xerox).
Type/Length Field
Unfortunately, there are several Ethernet standards, and they make different use of this two-
byte field. One standard uses it as a length, giving the total count of bytes in the data field.
Others use it as a protocol type, indicating the protocol that is being used in the data field.
Mercifully there are simple ways of detecting and handling these standards, which are dis-
cussed in Chapter 3.
Data
This area contains user data in any format; the only restrictions are that its minimum size is
46 bytes and its maximum is 1,500 bytes. The minimum is necessary to ensure that the over-
all frame is at least 64 bytes. If it were smaller, there would be a danger that frame collisions
wouldn’t be detected on large networks.
12 Chapter 1: Introduction
A starting CRC value of FFFFFFFFh is sent to this function, together with the first byte
value. A new CRC value is returned, which is sent to this function together with the next byte
value, and so on. When all bytes have been processed, the final CRC value is inverted (one’s
complement) to produce the four-byte Ethernet CRC, which would be transmitted least sig-
nificant byte first.
This specifies an Ethernet interface using an NE2000-compatible card at I/O address 280h.
See Appendix A for details on the cards and networks supported.
This string passed to a network initialization function, to open the required interface.
WORD open_net(char *cfgstr);
Device Drivers 13
This function opens up the network driver, given a string specifying the type of driver and
configuration parameters, and returns a driver type, which must be used in all subsequent
accesses, or a 0 on error (e.g., when the hardware is in use by other software).
void close_net(WORD dtype);
This function shuts down the network driver. The returned value for the driver type serves
two purposes: it provides a unique handle for the interface, and its flags inform you of the
type of interface in use. This allows you to create software that can handle multiple network
interfaces, each with different hardware characteristics.
You need a generic frame that can accommodate any one of the different frame types. Its
header includes the driver type.
/* General-purpose frame header, and frame including header */
typedef struct {
WORD len; /* Length of data in genframe buffer */
WORD dtype; /* Driver type */
WORD fragoff; /* Offset of fragment within buffer */
} GENHDR;
typedef struct {
GENHDR g; /* General-pupose frame header */
BYTE buff[MAXGEN]; /* Frame itself (2 frames if fragmented) */
} GENFRAME;
The header also has a length word to assist in low-level buffering (e.g., polygonal buffer-
ing, described later) and support for fragmentation. This is where a frame that exceeds the
MTU size is broken up, sent as two smaller frames, and reassembled at the far end. This will
be discussed further in Chapter 3; for now, you need to be aware that the maximum frame
size (MAXGEN in the above definitions) need not be constrained to the maximum Ethernet frame
size. You’ll use a MAXGEN of just over 3Kb, so two complete Ethernet frames can be stored in
the one GENFRAME.
Having standardized on a generic frame, you can create the driver functions to read and
write these frames.
WORD get_net(GENFRAME *gfp); Checks for an incoming frame. If present, it copies it into
the given buffer and returns the data length. If there is no frame, it returns 0.
WORD put_net(GENFRAME *gfp, WORD len); Sends a frame, given its length, and returns the
total transmitted length or 0 if error.
You don’t need to specify which network interface is used because the function can exam-
ine the driver-type field to determine this. Sample device drivers have been included on the
CD-ROM, but they will not be discussed here because they are highly specific to the hard-
ware (and operating system).
14 Chapter 1: Introduction
# EOF
Blank lines, or lines beginning with #, are treated as comments. At the start of each line is
a single lowercase configuration parameter name delimited by white space and followed by a
string giving the required parameter value(s).
The content of the file is specific to the software being run; if any configuration parameter
is unrecognized, it is ignored. In the above example, the net entry defines the network driver
to be used and its base I/O address. The node name is identified as node1, with IP address
10.1.1.1 and gateway address 10.1.1.111 given. Appendix A gives guidance on how to cus-
tomize the configuration file for the network hardware you are using.
Process Timer
When implementing a protocol, an event for a future time is often scheduled. Whenever you
send a packet on the network, you must assume that it, or the response to it, might go astray.
After a suitable time has elapsed, you may want to attempt a retry or alert the user.
Most modern operating systems have a built-in provision for scheduling such events, but I
am very keen to keep the code Operating System (OS) independent and to be able to run it on
the bare metal of small embedded systems. To this end, my software includes a minimal event
scheduler of its own, which requires a minimum of OS support and can be adapted to use the
specific features of your favorite OS.
The simplest scheduling algorithm is to delay between one event and another.
putpacket(...); /* Packet Tx */
delay(2000); /* Wait 2 seconds */
if (getpacket(...)) /* Check for packet Rx */
{
/* Handle response packet */
}
else
{
/* Handle error condition */
}
Process Timer 15
The dead time between transmission and reception is highly inefficient. If the response arrives
within 100 milliseconds (ms), the system would wait a further 900ms before processing it.
With a multitasking OS, you could use sleep instead of delay, which would wake up on
time-out or when the packet arrived (a method called blocking, since it blocks execution until
an event occurs). An alternative pseudo-multitasking method is to use timer interrupts to
keep track of elapsed time and to initiate corrective action as necessary, but this approach
would be highly specific to the OS.
A simple compromise, not entirely unfamiliar to old-style Windows programmers, is to
have the software check for its own events and handle them appropriately.
putpacket(...); /* Packet Tx */
timeout(&txtimer, 0); /* Start timer */
while (1)
{
/* Check for packet Rx */
if (getpacket(...))
{
/* Handle response packet */
}
/* Check for timeout on response */
else if (timeout(&txtimer, 2))
{
/* Handle error condition */
}
/* Check for other events */
else if ...
The timeout() function takes two arguments: the first is a pointer to a variable that will
hold the starting time (tick count), and the second is the required time-out in seconds. When
the time-out is exceeded, the function triggers an event by reloading the starting time with the
current time and returning a non-zero value. For example, the following code fragment prints
a seconds count every second.
WORD sectimer, secs=0;
timeout(§imer, 0);
while (1)
{
if (timeout(§imer, 1))
printf("%u sec\n", ++secs);
}
16 Chapter 1: Introduction
Before a timer is used, a timeout() call must be made using time value 0. This forces an
immediate time-out, which loads the current (starting) time into the timer variable. The tim-
eout() function is easy to implement, providing you take care with the data types.
tim = (WORD)time(0);
diff = tim - *timep;
if (sec==0 || diff>=sec)
{
*timep = tim;
tout = 1;
}
return(tout);
}
If the use of unsigned arithmetic appears counterintuitive, consider the following code.
WORD a, b, diff;
a = <any starting value>;
b = a + 10;
diff = b - a;
What is the value of diff? It must be 10, whatever the starting value.
There is a hidden trap that is due to timer granularity. The if statement in the code
timeout(§imer, 0);
if (timeout(§imer, 1))
…
will sometimes return TRUE, even though much less than a second has elapsed. This is because
the two statements happen to bracket a timer tick, so it appears that one second has elapsed
when it has not.
A cure for this problem is to change the unit of measurement to milliseconds, although the
nonstandard millisecond timer, mstime(), must be coded for each operating system.
/* Check for timeout on a given msec counter, return non-zero if true */
int mstimeout(LWORD *timep, int msec)
{
State Machines 17
LWORD tim;
long diff;
int tout=0;
tim = mstime();
diff = tim - *timep;
if (msec==0 || diff>=msec)
{
*timep = tim;
tout = 1;
}
return(tout);
}
Alternatively, you can just document this feature by saying that there is a tolerance of –1/+0
seconds on the time measurement. Given this timing tolerance, you might be surprised that my
trivial example of printing seconds works as suggested.
WORD sectimer, secs=0;
timeout(§imer, 0);
while (1)
{
if (timeout(§imer, 1))
printf("%u sec\n", ++secs);
}
It works because the state changes in the main loop are locked to the timer tick changes.
The whole operation has become synchronous with the timer, so after a random delay of up
to one second, the one-second ticks are displayed correctly.
When working with protocols, you will frequently see software processes synchronizing
with external events, such as the arrival of data frames, to form a pseudo-synchronous sys-
tem. When testing your software, you must be sure that this rhythm is regularly disrupted
(e.g., by interleaving accesses to another system) to ensure adequate test coverage.
State Machines
When learning to program, I always avoided state machines and skipped the examples (which
always seemed to be based on traffic lights) because I couldn’t see the point. Why go to all the
effort of drawing those awkward diagrams when a simple bit of procedural code would do
the job very effectively?
Tackling network protocols finally convinced me of the error of my ways. You may think a
network transaction is a tightly specified sequence of events that can be handled by simple
procedural code, but that is to deny the unpredictability (or unreliability, as I discussed earlier)
18 Chapter 1: Introduction
of any network. In the middle of an orderly transaction, your software might see some
strangely inconsistent data, perhaps caused by a bug in the someone else’s software or your
own. Either way, your software must make a sensible response to this situation, and it can’t do
that if you didn’t plan for this possibility. True, you can’t foresee every problem that may
occur, but with proper analysis you can foresee every type of problem and write in a strategy
to handle it.
Only the simplest of network transactions are stateless; that is, neither side needs to keep
any state information about the other. Usually, each side keeps track of the other and uses the
network to
• signal a change of state,
• signal the other machine to change its state, or
• check whether the other machine has signaled a change of state.
The key word is signal. Signals are sent and received over the network to ensure that two
machines remain in sync; that is, they track each other’s state changes. The signals may be
explicit (an indicator variable set to a specific value) or implicit (a quantity exceeding a given
threshold). Either way, the signals must be detected and tracked by the recipient.
Any error in this tracking will usually lead to a rapid breakdown in communications.
When such problems occur, inexperienced network programmers tend to concentrate on the
data, rather than the states. If a file transfer fails, they might seek deep meaning in the actual
number of bytes transferred, whereas an older hand would try to establish whether a state
change had occurred and what caused it at the moment of failure. This process is made much
easier if the protocol software has specifically defined states and has the ability to display or
log the state information while it is running.
At the risk of creating a chapter that you will skip, I’d like to present a simple, worked
example of state machine design, showing the relationship between state diagram, state table,
and software for a simple communications device, the telephone.
The last two states are debatable, since a telephone can send and receive simultaneously.
However, most human beings possess a half-duplex audio system (they seemingly can’t speak
and listen at the same time), so the separation into transmission and reception is logical.
A telephone changes state by a combination of electrical messages down the phone cable
and by user actions. From the point of view of a hypothetical microcontroller in the tele-
phone, these might all be considered signals.
State Machines 19
Line ring ring signal from another phone
Line idle no signal on phone line
Pick up user picks up handset
Mic. speech user speaks into microphone
Line speech speech signal from other phone
Hang up user replaces handset
It is now necessary to define which signals cause transitions between states; for example,
to change state from idle to ringing, a ring signal is required.
It is traditional to document these state changes using a state diagram such as Figure 1.6,
which is a form of flowchart with special symbols. Each circle represents a defined state, and
the arrows between circles are the state transitions, labeled with the signal that causes the
transition. So line speech causes a transition from the connected state to the receiving state,
and line idle causes the transition back to connected.
Because of the inherent limitations of the drawing method, these diagrams tend to over-
simplify the state transitions; for example, Figure 1.6 doesn’t show a state change if the user
hangs up while receiving.
A more rigorous approach is to list all the states as rows of a table and all the signals as col-
umns (Table 1.3). The table entries give a new state or are blank if there is no change of state.
IDLE
Line ring
Line idle
Hang up Ringing
Pick up
Connected
Mic idle
Line speech
Mic speech
Line idle
Sending Receiving
20 Chapter 1: Introduction
Line speech
Mic. speech
Line Ring
Line idle
Hang up
Mic. idle
Pick up
Idle Ringing
Ringing Idle Connected Idle
Connected Sending Receiving Idle
Sending Connected Idle
Receiving Connected Idle
Once the table has been created, it isn’t difficult to generate the corresponding code. You
could use a two-dimensional lookup table, although a series of conditional statements are
generally more appropriate.
switch(state)
{
case STATE_IDLE:
if (signal == SIG_LINE_RING)
newstate(STATE_RINGING);
break;
case STATE_RINGING:
if (signal == SIG_PICKUP)
newstate(STATE_CONNECTED);
else if (signal == SIG_LINE_IDLE)
newstate(STATE_IDLE);
break;
case STATE_CONNECTED:
// ..and so on
}
Buffering 21
I have created an explicit state machine where the states, signals, and relationship between
them are clearly and explicitly identified. Contrast this with an implicit state machine, where
the current state is buried in function calls.
void idle(void)
{
while (1)
{
if (signal == SIG_LINE_RING)
ringing();
}
}
void ringing(void)
{
while (signal != SIG_HANGUP)
{
if (signal == SIG_PICKUP)
connected();
}
}
void connected(void)
{
// ... and so on
Here, the current state is indicated implicitly by the current position in the code, and it is
far harder to keep control of all the possible state transitions, particularly under error condi-
tions. The stack-based call return mechanism imposes a hierarchical structure that is ill suited
to the arbitrary state transitions required. It is important that the state machine is explicitly
created, rather than being an accidental by-product of the way the software has been struc-
tured. The requirements of the state machine must dictate the software structure, not (as is
often the case) the other way around.
Buffering
To support the protocols, three special buffer types will be used. The first is a modified ver-
sion of the standard first in, first out (FIFO) to accommodate an extra trial pointer; the sec-
ond is a fixed-data-length variant of this, and the third is a FIFO specifically designed for bit-
wide, rather than byte-wide, transfers.
FITO Buffer
The FITO (first in, trial out) is a variant of the standard FIFO, or circular buffer (Figure 1.7).
A normal FIFO has one input and one output pointer; data is added to the buffer using the
input pointer and removed using the output pointer. For example, assume that a 10-character
FIFO has the letters “ABCDEFG” added, then “ABCDE” removed, then “HIJKL” added.
22 Chapter 1: Introduction
in
Start
out
in
'ABCDEFG'
A B C D E F G
added
out
in
'ABCDE'
A B C D E F G
removed
out
in
'HIJKL'
K L C D E F G H I J
added
out
The circularity of the buffer is demonstrated in Figure 1.7 by the second addition; instead
of running off the end, the input pointer wraps around to the start, providing there is suffi-
cient space (i.e., the pointers do not collide). Note that after removal, the characters
“ABCDE” are shown as still present in the buffer; only the output pointer has changed posi-
tion. This reflects standard practice, in that there is little point in clearing out unused loca-
tions, so the old characters remain until overwritten.
Now imagine this FIFO is being used in a Web server; the input text is a Web page stored
on disk, and the output is being transmitted on the network. Due to network unreliability,
you don’t actually know whether the transmitted data has been received or has been lost in
transit. If the latter, then the data will have to be retransmitted, but it is no longer in the
FIFO, so it must be refetched from disk.
It would be better if the FIFO had the ability to retain transmitted data until an acknowl-
edgment was received; that is, it keeps a marker for output data that may still be needed,
which I will call trial data, in contrast to untried data, which is data in the buffer that hasn’t
been transmitted yet; hence, the FITO buffer has one input and two output pointers, as
shown in Figure 1.8.
Having loaded “ABCDEFG” in the buffer, data fragments “ABC” and “DE” are sent out
on the network, and the trial pointer is moved up to mark the end of the trail data. “ABC” is
then acknowledged, so the output pointer can be moved up, but the rest of the data is not, so
the unacknowledged data between the output and trial pointers is retransmitted on the net-
work, followed by the remaining untried data. Finally that is all acknowledged, so the output
pointer can be moved up to join the input pointer.
Buffering 23
Figure 1.8 FITO example.
in
Start
trial
out in
'ABCDEFG'
A B C D E F G
added
trial
out
in
Trial data Untried data
'ABC'
A B C D E F G
sent
out trial
in
'DE'
A B C D E F G
sent
out trial
in
'ABC'
A B C D E F G
acknowledged
out trial
in
Timeout A B C D E F G
trial
out in
'DE'
A B C D E F G
resent
out trial
in
'FG'
A B C D E F G
sent
out trial
in
'DEFG'
A B C D E F G
acknowledged
trial
out
24 Chapter 1: Introduction
A structure stores the data and its pointers (as index values into the data array). The first
word indicates the buffer length, which allows for a variety of buffer sizes. For speed, the
buffer size is constrained to be a power of two.
#ifndef _CBUFFLEN_
#define _CBUFFLEN_ 0x800
#endif
/* Circular buffer structure */
typedef struct
{
WORD len; /* Length of data (must be first) */
LWORD in; /* Incoming data */
LWORD out; /* Outgoing data */
LWORD trial; /* Outgoing data 'on trial' */
BYTE data[_CBUFFLEN_]; /* Buffer */
} CBUFF;
A default buffer size of 2Kb is provided, which may be overridden if required. This permits a
buffer to be declared as a simple static structure.
#include "netutil.h"
CBUFF rxpkts = {_CBUFFLEN_};
In both cases, the length value is set when the buffer is created; this is very important if
strange bugs are to be avoided.
The use of LWORD (unsigned 32-bit) buffer pointers with WORD (unsigned 16-bit) data
length may seem strange. The former is part of a Cunning Plan to map the TCP 32-bit
sequencing values directly onto these pointers, whereas the latter permits the code to be com-
piled into a 16-bit memory space (e.g., small model), if necessary. All should become clear in
subsequent chapters.
Buffering 25
In creating the buffer-handling software, it is important to retain a clear idea of what is
meant by untried data (not yet sent), and trial data (sent but not acknowledged).
/* Return total length of data in buffer */
WORD buff_dlen(CBUFF *bp)
{
return((WORD)((bp->in - bp->out) & (bp->len - 1)));
}
/* Return length of untried (i.e. unsent) data in buffer */
WORD buff_untriedlen(CBUFF *bp)
{
return((WORD)((bp->in - bp->trial) & (bp->len - 1)));
}
/* Return length of trial data in buffer (i.e. data sent but unacked) */
WORD buff_trylen(CBUFF *bp)
{
return((WORD)((bp->trial - bp->out) & (bp->len - 1)));
}
/* Return length of free space in buffer */
WORD buff_freelen(CBUFF *bp)
{
return(bp->len ? bp->len - 1 - buff_dlen(bp) : 0);
}
When loading data into the buffer, the simple but slow method is to copy it byte-by-byte.
Instead, I’ll use either one or two calls to a fast block-copy function, depending on whether
the new data wraps around the end of the buffer. If the data is too big for the buffer, it is trun-
cated, because I’m assuming the programmer has checked the free space before calling this
function. The free space is always reported as one byte less than the actual space, so there is
no danger of the input pointer catching up with the output pointer.
/* Load data into buffer, return num of bytes that could be accepted
** If data pointer is null, adjust pointers but don't transfer data */
WORD buff_in(CBUFF *bp, BYTE *data, WORD len)
{
WORD in, n, n1, n2;
26 Chapter 1: Introduction
/* Load string into buffer, return num of chars that could be accepted */
WORD buff_instr(CBUFF *bp, char *str)
{
return(buff_in(bp, (BYTE *)str, (WORD)strlen(str)));
}
Removal of untried data from the buffer, so that it becomes trial data, is essentially the
inverse of the above.
As a useful extra feature, a null data pointer can be given to the function, in which case it
goes through the same motions, but without copying any actual data. This is handy for dis-
carding unwanted data (e.g., trial data that has been acknowledged).
I’ve made extensive use of minw(), which returns the lower of two word values and so is
similar to the standard function min().
WORD minw(WORD a, WORD b)
{
return(a<b ? a : b);
}
and any function arguments may be executed twice, which is a major problem in interrupt-
driven (reentrant) code. Take a line from buff_out().
n = minw(maxlen, buff_dlen(bp)); /* Get max allowable length */
Imagine that the first time buff_dlen() is executed, the source buffer is almost empty, so
all its data can be transferred into the destination. However, before the function is executed a
second time, an interrupt occurs that fills the buffer with data, so the actual data length cop-
ied exceeds the maximum the destination can accept, with disastrous results. The easiest way
to avoid this problem is to buffer the comparison values in a function’s local variables; hence,
the usage of minw().
Polygonal Buffer
A circular buffer is useful for handling unterminated streams of data, but sometimes you’ll
need to store blocks of known length. The classic case is a packet buffer, in which you can
queue packets prior to transmission or on reception. The standard technique is to have a
buffer pool, from which the storage for individual packets can be allocated. A simpler tech-
nique is to use a circular buffer as before but to prefix each addition to it with a length word,
to show how much data is being added.
if (len>0 && buff_freelen(&rxpkts) >= len+2)/* If space in circ buffer..*/
{
buff_in(&rxpkts, (BYTE *)&len, 2); /* Store data len.. */
buff_in(&rxpkts, buff, len); /* ..and data */
}
The smooth circle of data has been replaced by indivisible straight-line segments; when
recovering the data, check that the whole block is available (if there is a risk that part of the
block may be in transit). The trial system comes in handy because you can retry (i.e., push
back) the length if the entire data block isn’t available yet.
if ((dlen=buff_dlen(&txpkts)) >= 2)
{
buff_try(&txpkts, (BYTE *)&len, 2); /* Get length */
if (dlen >= len+2) /* If all there.. */
{
buff_out(&txpkts, 0, 2); /* ..remove len */
buff_out(&txpkts, buff, len); /* ..and data */
}
else
buff_retry(&txpkts, 2); /* Else push back len */
}
Coding Conventions 29
This explains the length parameter on the front of the generic frame. It allows you to store
and retrieve GENFRAME structures from circular buffers without having to understand the con-
tents of the frame.
Coding Conventions
It isn’t essential that you use the same coding conventions (source code formatting) as I do,
though it may help if I describe the rules I’ve used, so you can choose whether to follow them
or not.
Data Types
When defining protocol structures, it is really important to use the correct data width. You
may be used to assuming that an int is 16 bits wide, but that isn’t true in a 32-bit system. I’ve
made the following assumptions for all the DOS compilers.
• char is an 8-bit signed value
• short is 16 bits
• long is 32 bits
From these, I have derived the following width-specific definitions.
#define BYTE unsigned char
#define WORD unsigned short
#define LWORD unsigned long
I have used #define in preference to typedef because compilers use better optimization strat-
egies for their native data types. A notable omission is a Boolean (TRUE/FALSE) data type; I use
an integer value and assume TRUE is any non-zero value.
Keeping compatibility with both 16- and 32-bit compilers also necessitates the addition of
some redundant-looking typecasts.
WORD a, b;
a = (WORD)(b + 1);
If the typecast is omitted, the Visual C++ compiler issues a warning message because b is pro-
moted to a 32-bit value for the addition, which must be truncated when assigned to a.
Another tendency of 32-bit compilers is, by default, to pad data elements out to four- or
eight-byte boundaries, which blows gaping holes in the structures.
typedef struct {
BYTE a;
BYTE b;
LWORD c;
WORD d;
} MYSTRUCT;
If the mix of bytes, words, and long words is to be transmitted on the network, it is vital that
the compiler is set so that it does not introduce any padding between these structure elements;
that is, the structure member alignment is one byte, not four or eight bytes.
Random documents with unrelated
content Scribd suggests to you:
IV.
Joyous and Free.
Ned Franks took down his cap from its peg, as soon as his merry
young scholars, like a swarm of bees from the hive, had poured out
from the low-browed porch of the school-house. But before he had
time to start for the mill, Persis, baby in arms, was at his side, with a
sandwich neatly put up in paper for her husband to eat on his way.
"No fear of my being put on half rations while wifie has charge of
the stores," said Ned Franks.
He only lingered to kiss the soft little face of his babe, fragrant and
sweet as a rosebud, and then set off for his visit to Bat Bell, though
not very hopeful as to its result. The sun was shining brightly, the
trees bursting into leaf; the lark in the blue sky, the thrush from its
bough, were pouring forth songs of joy. Every sight, scent, and
sound was a source of pleasure to Ned Franks.
"Those merry little fellows are piping aloft," thought he, "to cheer
their mates in their nests. Well may my heart sing, too, for who has
such a home, and such a mate, and such a nestling as mine? The
birds carol merrily, for they cannot look forward, the pleasure of the
day is enough for them; but far more cause have I to sing, for I can
look forward and think,—the spring-time is bright, but the harvest
will be brighter; there is joy now, but the fulness of joy is to come!
Ay, I can look forward and upward, too, and see what the birds
cannot see,—the hand that scatters the blessings over my path, the
Father's hand that filleth all things with plenteousness! And even like
his free bounty should be that of his children; freely ye have
received, freely give!"
A thin, weary wayfarer was sitting on the side of the path; his
patched coat, his half-worn-out shoes, and sunken cheek told of
need, although the man was no beggar. Following simply the
impulse of his heart, Franks pulled out his sandwich and courteously
offered it to the stranger. The smile and hearty blessing with which it
was received sent the one-armed school-master on his way with a
heart even more joyous than it had been a few minutes before. To
give is a godlike pleasure, and he who does not know what it is to
do so with delight has missed one of the richest luxuries which man
can enjoy below.
As Ned Franks passed along the high road, he could see in a
neighboring field a man engaged in sowing.
"To bury seed is not to lose seed," thought Ned, "though it seem for
a while to disappear, like money which is given to the Lord, or to the
poor for his sake. A man who spends all that he has on himself or
his family alone seems to me like one who grinds and bakes and
eats all his seed-corn. He gains some present advantage, no doubt,
but he will find want and dearth in the end, for he has not sown for
the future. And the man who lays by and hoards what ought to be
given in charity is like one who locks up his seed-corn in a chest until
it grows mouldy and worthless. It neither feeds him nor grows for
him; it is worse than good for nothing. While he that gives to the
poor lends to the Lord, and the Lord will give him rich increase, not
because of the man's deserts, but because of our heavenly Father's
own free bounty towards those who seek to please him."
Ned, walking on with quick, active step, overtook Ben Stone, who,
carrying his basket of carpenter's tools, was proceeding at a more
leisurely pace in the same direction.
"Whither bound, messmate?" cried Franks, as he came up with the
burly carpenter.
"I've a job at the Hall," replied Stone; "the new baronet will be
coming down to the old house one of these days, and will want to
find everything right there. Where are you going, Ned Franks?"
"I'm going to see if Bat Bell won't add something to the collection for
the tumble-down cottages in Wild Rose Hollow. He was not at
church yesterday."
Ben Stone burst out laughing, as he had a habit of doing upon the
slightest occasion. "Going to ask Bat Bell for money! Going to try
how much meal you can scrape off an old knife-board! ha! ha! ha! I
put my shilling in the plate yesterday;"—the carpenter said this with
a self-satisfied air, as one who felt conscious of having done the
handsome thing;—"but I don't mind promising to double whatever
you manage to squeeze out of Bat Bell; only, of course he mustn't
know that I've said so."
"Don't make a rash engagement, messmate," said Ned Franks, with
a smile; "I may come down upon you for some ready rhino."
"Well, and if you do," answered the good-humored carpenter, "I'll
not flinch from my word. I've enough and to spare, and what one
gives away, as we all know, goes to our good account in the end."
"That depends on the spirit in which we give," said Franks, more
gravely, for he had good reason for suspecting that his companion
held very mistaken views on the subject. "One can't keep a debtor
and creditor account in heaven. We know from the Bible that a man
might give all his goods to feed the poor, and yet that it might profit
him nothing to do so."
"That's one of the texts as I never can make out the meaning of,"
said the carpenter. "To give is to give, and money is money; and
why, when two men do exactly the same thing, one should have a
blessing, and another none, quite passes my poor understanding."
"If one could suppose that all money given in charity could be put to
a test, that only what is really offered for the Lord's sake should
remain money, and all the rest be turned into withered leaves, don't
you think we should have heaps of dry leaves, as in autumn, to be
scattered about by the wind? Consider all that's given for mere
show, all that's given from natural pity, all that's given because it
would be thought strange and mean to do less than others; none of
that money is given to God, so we must not expect that God will
accept it."
"Well, I grant ye this," said the carpenter, "if every man's almsgiving
could be known only to himself and to God, there's many a one as
gives now would keep his money snug in his pocket. But I'm not one
of those, my good friend. I know, as we can carry nothing out of the
world, that it's best to have something laid up in the bank above.
But here your way divides from my way,—you go down the dell, I
keep to the road. Good-day to you, Ned Franks, let me know what
you get from Bat Bell; I'll be bound 'twill be nothing to ruin me. I've
not much to do at the Hall to-day, but measuring and fitting, so
maybe I'll be back before you return; just drop in at my shop and
tell me what's your success;" and with a friendly nod and
complacent smile, the carpenter went along the high road, while the
school-master turned down the little wooded lane which led to the
mill.
"I should have liked to have had a little longer talk with Ben Stone,"
thought Franks. "I'm afraid that he thinks that he is actually buying
God's favor, and earning heaven, by the little kind acts that he does!
That's a kind of error which so many people run foul of. The sunken
rock of self-righteousness is, maybe, just as dangerous as the
sandbank of love of money. I must have a care that I don't take to
judging others, and so split on it myself. I spoke very hardly yester-
evening of Bat Bell the miller, yet, when I consider what a poor
wretched sinner I am, receiving so much from God, and showing my
gratitude in such a poor way, I'm scarce likely to run on that rock.
When one measures one's little drop of charity, and even that not
pure, with the great unfathomable ocean of love of Him who gave
his life-blood for us, one is far more inclined to ask forgiveness for
doing so little, than to expect reward for doing so much. There's
nothing that can give the best of us any claim to the least of God's
mercies, but the merits of Christ. That is a truth that I see the more
plainly the longer I live. To attempt to hold by one's own merits
would be like trying to go to sea in a bark made of gossamer
threads. The gossamer web looks goodly enough when the
sunbeams are glinting upon it, and the dew-drops are nestling in it,
but no man in his senses would trust his life to its power to bear up
his weight. It would be a madder thing still for him to trust his soul's
salvation to his own merits. If any mortal had anything in himself to
boast of or to trust to, that mortal was St. Paul, who was ready to
spend and to be spent; who had suffered the loss of all things for
God,—a very different kind of self-denial from what we dare to call
by that name,—and yet what was the feeling of St. Paul? Did he
think thus he had earned heaven? Did he not say, God forbid that I
should glory save in the cross of our Lord Jesus Christ? If we were to
strip ourselves of all that we have, if we were to give away health
and time and life itself for God's service, we should never get
beyond that verse, we should have nothing whereof to boast,
nothing (out of Christ) whereon to rest."
Ned had now descended to the bottom of a beautiful little dell,
through which gushed a rapid stream of water, turning the large
wheel of Bell's mill. The wheel was, however, at this time still, and its
monotonous clack did not mingle with the gurgle of the brook and
the song of the birds. Franks had many delightful associations
connected with that wooded dell; for there stood the cottage in
which Persis, as a maiden, had dwelt with her aged grandfather; it
was there that he had wooed and won her; from that little ivy-
mantled nest he had, three years before, taken his bride to church.
The cottage had now other inhabitants, but Franks could not pass
the spot without stooping to pluck a violet to carry back to his wife.
"I'll give this to Persis," he said to himself; "she'll like a flower from
the old home, though, thank God, I believe that she has never
regretted leaving it for the new one. This much I can answer for,
leastways, that every day since that happy one on which God gave
her to me has made me prize his gift more dearly."
V.
An Appeal.
Bat Bell was a particular man, regular and precise in all his ways,
who had, as it were, stiffened into his own mould, especially since
the death of his wife, and who did not choose, as he often said, "to
be put out for nobody." Bell hated a visitor at work-time, and he was
so keen after making money that his work-time began early and
ended late. He hated a visitor at meal-time, probably because he did
not wish any one to share his meal. Franks was aware of this, and
tried so to time his visit to the mill that he should catch Bell in that
half-hour of rest which usually followed his early dinner.
"He'll be playing with his Bessy," thought Franks, "and there's
nothing on earth that softens and opens a man's heart like hearing
the voice of his own little child, or dancing it on his knee." Such was
the conclusion to which the school-master came after his four weeks'
experience of the feelings of a father.
Franks, however, found little Bessy, not with her parent, but amusing
herself in the lane close by the mill. She ran up to him with open
arms, and held up her little face for a kiss, for Ned was a prime
favorite with every child who knew him, and, during her mother's
last illness, Bessy had spent a week at a time under the care of the
Franks. She was a plump, rosy-cheeked, merry little girl, of about
five years of age.
"Is father at home, my little lass?" asked Ned.
"Yes; father's in there," replied Bessy, nodding in the direction of the
door of the cottage attached to the mill; "but he lets me be here to
look for the flowers."
"Mind you don't go near the water, little one," said Franks; "keep to
the primroses under the hedge;" and, smiling a good-by to the child,
he proceeded to the dwelling of her father.
Bat Bell was alone in his parlor, seated on his high-backed wooden
chair before the solid deal table, on which appeared the remains of
some bread and cheese, and the empty pewter pot which had held
his beer. Bell was a tall, bony man, naturally of rather a dark
complexion, but skin, hair, and dress were all powdered with the
flour which showed what was his daily occupation, his shaggy black
brows especially having formed a resting-place for the white dust, as
the thatched eaves of a dwelling for snow.
"Good-day to you, Ned Franks, glad to see you; what brings you this
way?" asked the miller, holding out his bony, whitened hand to his
visitor, with as much of a smile on his face as the stiffness of the
leathery skin would allow.
Franks was not one to approach any subject in a round about way; if
there was any difficulty before him, he usually took what he called "a
header" into the very middle of it. He did not say he had just looked
in to see an old friend, or to ask if little Bessy would come and look
at his baby, or utter any remark about the weather, or express any
hope that business was brisk; he said what he had come to say the
moment after he had taken a seat.
"I've called, neighbor, to talk to you about the almshouses yonder in
Wild Rose Hollow;" through the window towards which Ned glanced
as he spoke, the chimney of the nearest one could be seen. "I was
up at Sarah Mason's early this morning, to try what I could do for
her wall; but no patching of mine can make the place fit for a human
being to live in, let alone a rheumatic old woman. You know well the
state of the cottages; something must be done for them without
much delay, or the old hulks will soon fall to pieces."
Every symptom of a smile had disappeared from the hard face of Bat
Bell as soon as his visitor had mentioned Wild Rose Hollow; and
when Ned paused, the miller's only reply was a "humph," uttered in
a very discouraging tone.
"Don't you think that it would be a shame and disgrace to Colme, if
dwellings that have afforded shelter for two hundred and fifty years
to the aged respectable poor of our village were all suffered to go to
utter decay from neglect, as one of them already has done?"
"Why don't young Sir Lacy mend 'em? He has money enough," said
the miller, "and flings it away right and left, they say, in ways that
are little to his credit."
"If he does not come forward, is his backwardness an example to be
followed?" asked Franks.
"Let the clergy see to it; it's their business," said Bell, with a little
disagreeable twitch of the nostril, which with him was always a sign
that something was "putting him out."
"Mr. Leyton preached twice yesterday in aid of the work, but the
collections made were wretched,—not one tenth of what is
absolutely required."
"The parish overseers must do something."
"They refuse to stir a finger," said Franks; "they say it's no business
of theirs."
"Then I'm sure that it's no business of mine," interrupted the miller.
"Is it no business of ours," said the school-master, earnestly, "that
they whom we have known for years, they who have lived amongst
us, and hoped to die amongst us, should be deprived of the comfort,
the quiet, the independence which they so dearly prize?"
"I'm sorry for them," said the miller, carelessly; "the founder should
have left something to keep the wheel going."
"What is wanted is the full stream of Christian love," observed
Franks. "There are scores of charities in London kept constantly
working by nothing but that stream."
The miller did not look as if he had a drop of such love within him.
"It is clear," thought Franks, "that I'm not in the right tack yet. Let
me try him on that of conscience. Why," he continued, aloud,
"there's no plainer command in the Bible than To do good and to
distribute, forget not; let us do good unto all men, and specially unto
them that are of the household of faith."
"I know something of the Bible, too," replied Bat Bell, coldly, and the
twitch was more unpleasant than before. "I'm a father, and I don't
forget that it's written that He who provideth not for his own is
worse than an infidel."
"Never let that text be repeated to justify hoarding!" exclaimed
Franks, with some warmth, for it flashed across his mind how the
devil himself can quote Scripture. "If we are to be content with food
and raiment for ourselves, shall we not be content with them also
for our children, without gathering up for them gold and silver, which
may only prove a snare, as has happened in thousands of cases? I
am a father, too," Ned added more mildly, for he saw on the
countenance of Bell that he had spoken too warmly; "I am a father,
and love my little one as much as a father can love; but if thoughts
of saving for him made me close my hand and heart against the
claims of God's poor, I should feel, that whatever else I might leave
him, I dared not expect to leave him that blessing which alone
giveth true riches. I should feel that my babe was coming between
my soul and my God, and that I must look for God to punish me in
him. Of whatever we make our idol, the Lord is wont to make his
rod."
"I've no such superstitious fears!" cried Bat Bell, rising from his seat
with a gesture of impatience. Had his visitor been any one but Ned
Franks, from whom he had received kindness in time of sorrow, he
would have given his guest a broader hint to depart.
"Let us not talk of fear, then, neighbor," said Franks, also rising, but
with no intention of yet giving up his attempt to move that cold,
hard heart. "Have patience with me a few moments more, while I
speak of a nobler motive,—the love of God. Look around you, Bat
Bell, look at this comfortable home, where want is unknown; you
were ill last winter; look at the health and strength restored you
now; listen to the merry voice of your child,"—a joyous carol was
heard from without,—"and then ask yourself from whom came all
these blessings, the loss of any one of which would throw you into
sore distress. The goods that you have you owe"—
"To hard work, the labor of my hands in the sweat of my brow,"
interrupted the miller.
"Who gave the hand strength and the mind reason? whose power
made the stream which turns your mill? whose sunshine ripens the
corn on your fields? But why speak only of earthly blessings,—we
have more, far more to thank God for! We have not only bodies but
souls to care for; we have not only time but eternity to live for. Can
we be content to sit still and do nothing for others, when we know
what God's Son hath done for us; when we think at what a price he
bought our salvation; how, though he was rich, yet for our sakes he
became poor? He calls us to make no sacrifice for him that he has
not first made a thousand-fold for us; and when he would teach us
what charity should be, the Lord sums up all in the words, Love one
another as I have loved you."
Ned Franks's appeal was interrupted by the door being thrown
suddenly open, and little Bessy's running into the room. The white
pinafore of the child, held up by one chubby hand, formed a
receptacle for a number of wild flowers which she had been
gathering in the lane. With her blue eyes sparkling with pleasure,
the child ran up to her father.
"See, I've plucked 'em for you, every one!" she cried, emptying her
pinafore on the chair from which Bat Bell had lately risen; "no,—all
but this dead primrose,—it's withered and bad, it's not fit to give
father!" Bessy threw the faded flower away. "I've brought you the
first I could find; now, I'll run and get more for myself."
Bell caught up his girl, lifted her up high, and then kissed her again
and again before he set her again on the floor. Bessy nodded merrily
at Ned.
"You shall have some, too," she said; "but the first are always for
father;" and away ran the happy child, leaving her spring flowers
behind her.
And Bessy left something besides. The visit of the little one had
seemed to bring sunshine with it. The hard lines on the parent's face
were softened, every feature relaxed, the cold, money-making man
was a parent, and a fond parent still. Franks felt that the
unconscious Bessy had acted the part of a little ally; that she was
helping to stir the deeply-imbedded vessel which he had been trying
to move.
"Will that dear little girl enjoy her flowers less because the first are
always for her father?" said Franks, as soon as the sound of the
pattering feet was heard no longer. "Would that God's children were
more like her, bringing their gifts with readiness, with joy, and not
like too many of us, offering only the withered thing, the dead thing,
that which we will not miss, to him whose goodness towards us has
been greater than that of any father on earth!"
Bat Bell's hand approached his pocket, though he did not actually
put it in. "Ned Franks," said the miller, "I tell you honestly, that I
wouldn't stand this kind of talk from any man but yourself; but I
know that your practice is better than your preaching; so, as you've
set your heart on getting something for these cottages, just as a
matter of favor to you"—Bell stopped short; he could not make up
his mind either to finish his sentence, or to draw out his purse.
"I do not want you to give as a matter of favor to me," cried Franks,
"nor is the state of the cottages what is uppermost now in my mind.
I came here, indeed, anxious to get something for them, but I am a
hundred-fold more anxious to get something for you!" The miller
raised his dusty eyebrows with surprise, but Franks went on, without
giving him time to interrupt the earnest flow of his speech. "If we
knew that our Lord and Master had come down again to this earth,
that he was in our land, our country, our village, nay, that he was
deigning to dwell in one of these cottages, which, wretched as they
are, are better than the Bethlehem stable, would we not deem it the
first of honors to be allowed to bring gifts to him? Would not you
and I be ready to pull down our own dwellings to get beams and
rafters for his, and think the best that we have, yea, all that we
have, too little to offer to our King? And it is all the same, Bat Bell:
what we give to the poor for his sake, Christ receives as given to
himself. Inasmuch as ye did it unto the least of these, my brethren,
ye did it unto me. Yes, my friend, I want help for the cottages, but I
much more want something for you,—the joy of hearing at the last
day the Saviour's welcome, Come, ye blessed of my Father."
VI.
The Return.
The fervent appeal, coming as it did from the very heart of the
pleader, had stirred the stubborn hearer a little, though but a little
way from his first position. Bat Bell could not help remembering that
there was a reverse to the blessing, a "Depart ye cursed," for those
of whom Christ would witness, "Ye did it not unto me." Bell feared
that he might have lived all his life under the shadow of that curse;
so, anxious to justify himself to his own conscience even more than
to Franks, he took refuge in the remembrance of what he deemed a
good deed.
"I can give,—I have given, and largely, too," said the miller, leaning
his head against the wall. "There's my nephew, Rob Gates; did I not
pay fifty pounds to 'prentice him out,—fifty pounds," repeated the
miller emphatically, "of which I have not had one penny back,
though the ungrateful dog has been in business these three years?"
Upon this one act of generosity Bell always fell back when any call
on his charity was made, as if he considered that the lent fifty
pounds covered every claim which could be made on his purse by
religion or by humanity. It always gave him an opportunity of
declaiming against the ingratitude of mankind; because his nephew
had not repaid his loan, all who needed aid from the miller became
in his eyes covetous and thankless, if not dishonest. Bat Bell tried to
believe that in hazarding fifty pounds he had already given enough
to God; it would have startled him to have been told that not one
farthing of the money could be reckoned as real charity. Bell had
helped his nephew from natural affection, and from family pride. The
miller had acted exactly as he would have acted if he had been a
Turk or an Infidel,—exactly as he would have acted had he never
heard the name of the Lord. Tried by this test, of how small a part of
our alms, alas! will the Master be able to say, Ye did it unto me!
"That miserable fifty pounds," thought Ned, who had heard of it
often before, and who knew too well that the miller used its loss as
a perpetual argument to silence conscience, and excuse his neglect
of the poor.
"You see, Ned Franks," continued the miller, "a man who has once
made sacrifices for others, and has only met with ingratitude; who
has spent upon a good-for-nothing scapegrace of a nephew—"
The miller suddenly stopped and started. Ned, whose back was
towards the open door, only knew by the change on the face of Bell,
the look of surprise that flashed across it, that a third party had
unexpectedly joined them. Turning round he saw a stout young man,
in a shaggy coat, with a knapsack on his shoulders, and a broad grin
on his good-humored face, who advanced with both hands extended
to the miller, exclaiming in a loud, hearty tone, "Here's the good-for-
nothing scapegrace to answer for himself."
"Here's the good-for-nothing scapegrace to
answer for himself." p. 58.
Bell gave his nephew a cordial welcome both with hand and voice,
and Franks was so glad to see the hearty greeting, that he did not
ask himself whether it were possible that the uncle's pleasure at
seeing the young man might partly be owing to the hope of his now
having the old debt cleared off.
"So you were giving me a pretty character, uncle," cried Rob Gates,
after he had thrown himself on a chair; "well, I can't grumble at
that, as you've neither seen nor heard from me for many a long
year; but I never was much of a scribe, and don't trouble the
postman from January to December. I don't care to write till I've
something to say, so I waited till I could play the postman myself,
and bring a kind of notes that are easily read, and will tell more of
gratitude, duty, and that sort of thing, than reams of foolscap
scribbled all over."
With a look of honest satisfaction, the young man pulled a large
leathern pocket-book from his breast-pocket. His movements were
watched with keen interest by the miller, as Rob opened the clasp,
and then slowly drew out, one after another, unfolding and
smoothing out each as he did so, ten five-pound notes of the Bank
of England. He spread them with his broad, rough hands over the
table, as if he took a boyish pleasure in making the greatest possible
show of his wealth.
"Uncle, here's the fifty pounds which I owe you," said Rob; "you're
not sorry now, I hope, that you lent a helping hand to your
scapegrace of a nephew? I can't believe that a fellow ever has cause
to be sorry for doing a kindness; it always in one way or other
comes back."
Franks glanced at the miller, and fancied that he saw his thin lip
quiver a little, and that something like moisture rose in the usually
dark, cold eye. Ned could not tell what was passing in the mind of
that man, as he laid his hand on one of the notes. Slowly, half
reluctantly, the miller raised it, and then, as if moved by an impulse,
which even his selfish nature could not withstand, Bell handed that
note to the sailor, saying, "You came at a lucky time; take that,—it's
for Wild Rose Hollow."
Ned stood amazed at success so far beyond all his hopes. He had
indeed been led to that dwelling in a happy moment, when Bell's
hard heart had been softened and touched, or, to use his own simile,
"when a spring-tide had set in so strongly as to help the stranded
craft off the shoal." His words of thanks were hearty, and while the
miller set about preparing a meal for his hungry guest, the one-
armed sailor joyously started on his homeward way.
Merrily Franks sped up the glen, his blithe whistle mingling with the
chord of the lark that hung quivering over his head. Ben Stone, the
carpenter, who had just returned from the hall, was standing at the
door of his shop, on the lookout for Ned Franks.
"Why, he looks as gladsome as if he'd just come in for a fortune
himself," muttered the carpenter, "and he's whistling away like a
bird! But all that jollity must be put on to cover disappointment, for
if he got more than a crooked four-penny bit from that miserly miller,
I'm a Dutchman, that's all! Well, Ned Franks," cried the jovial
carpenter aloud, "how many brass farthings has Bat Bell pulled out
of his hoard to prop up the houses in Wild Rose Hollow?"
Franks waved the crisp, fluttering bank-note in reply.
"You don't mean—what—no—not a bank-note—a five-pound note!"
exclaimed the astonished Stone, scarcely able to credit his eyes. His
exclamation was echoed by his wife and two neighbors who joined
him at the moment.
"It must be a toy-note," suggested Mrs. Stone.
"No," laughed Ned, "it is a good honest note of the Bank of England,
worth five sovereigns of any man's money. Bat Bell was
unexpectedly repaid a large loan at the very time when I was with
him to ask help for Wild Rose Hollow; the first note which he
touched he gave to God, and I trust that God will bless him for it,"
added Franks, with fervor; "it was more from him than a much
larger sum would have been from another man."
"The sum's pretty large from anybody," said the carpenter, with
rather a rueful face, for Bat Bell's generosity had taken him by
surprise in an inconvenient way. "I hope that I'm not expected to
hold to my unlucky offer; where Bell gives once, I give a hundred
times; he may plump out a five-pound note and not miss it, but I've
not the knack of turning deal shavings into gold."
"No, no, neighbor," cried Franks, "no one would think of holding you
to such a bargain. You have not suddenly come into money like Bell,
or, I've not a doubt, you'd give to the full as large as he."
"But I'm not the man to flinch from my word," said Stone; "if I can't
give the money, I'll give the money's worth in work when I've time
to spare; so you may count that five pounds as fairly doubled, my
friend."
"That will be a lucky bank-note to you, Mr. Franks," observed the
carpenter's wife; "for now that you've brought in such a sum to help
the collection, I'm sure and certain that you'll be the man fixed upon
to be clerk at our church, instead of John Sands."
"Instead of Sands! why, he's not going to resign the place, surely?"
cried Franks.
"He'll have to give it up; he could not for shame stand up just below
the reading-desk, give out the psalms, and lead the singing, as if
nothing had happened," observed Ben Stone, with a shake of his
head.
"Why, Sands is the most quiet, steady, sober—"
"But his wife, she's the mischief, she's the ruin of him; a man is what
a woman makes him," quoth the jovial carpenter, giving a self-
complacent nod towards his own partner. "Parsons, we're told, must
have their own houses in subjection, and I guess the same rule
holds with their clerks. All Colme is talking about it. We'll have you,
our one-armed sailor, clerk as well as school-master; there's no one
so fit for the place!"
VII.
Brightness and Gloom.
"Mr. Franks, you have a happy home," said the clerk, after a little
pause; and then he added, with a sigh, "so had I once."
Ned knew not what to reply; he thought that all England held no two
women more unlike each other than Nancy Sands and his own sweet
Persis.
"You see, Mr. Franks," continued the clerk, drooping his head, and
looking on the carpet, "it was all sorrow that did it. There was not a
better manager in Colme than Mrs. Sands, till—till we buried our
only boy;" the poor man's voice faltered as he spoke; "and then she
fancied that there was comfort in a drop. I don't mean to say she
was right, but it's too common a mistake; I—I think the world's hard
upon her, Mr. Franks,—she has been tempted, grievously tempted;
but there's very good metal in her yet."
There was something touching to the sailor in the effort of the poor
injured husband to throw a veil of indulgence over the glaring fault
of his wife. Though her intemperance was ruining his comfort, and
disgracing his name, and might seriously injure his worldly position,
Sands's anxiety was to find some excuse for his wretched partner.
For the affections of the quiet, stiff, formal man still clung to the
choice of his youth.
John Sands had loved Nancy almost from his boyhood; often had he
been jested about his fancy for the boisterous black-eyed girl, who
cared so little for him. When Nancy had grown into a bold, self-
willed woman, ready enough to receive his attentions, but trifling
with his feelings, and not returning his love, Sands had seen, time
after time, some rival preferred to himself, and had heard, with silent
anguish, that the only girl that he had ever cared for was to be
married to some one else. Yet, somehow or other, every
engagement of Nancy's was broken off; perhaps few men, when it
came to the point of decision, would have wished to be linked for life
to Bangham's termagant daughter. So, after many long years of
patient, sorrowful waiting, John at length had the wish of his heart
granted, and found, as too many find, that he had chosen ill for his
own peace of mind. Nancy might have made a good, hard-working
wife to a man who would have ruled as well as loved her,—one who
would have taught her to obey; but where she should have had a
master, she found a servant; she despised Sands for his very anxiety
to please her, and readiness to yield to her wishes. There was no
open rupture between them; the wife ruled and the husband obeyed
and never complained, till at length Nancy's indulgence in the vice of
intemperance made John's misery a thing which no longer could be
concealed from the world.
The clerk seemed to expect some reply. The sailor was puzzled what
to say; he feared to hurt Sands by expressing any pity, and he was
too sincere to express any hope. But as the dead silence became
very painful, Ned broke it by saying, "I wish with all my heart I could
help you."
"That's it, that's just it," said John Sands, raising his drooping head a
little; "you're the only man I could have asked. You see," he
continued, uneasily, "Mrs. Sands is always right, as she should be,
when I'm by; she has the best of hearts; the metal is good, very
good; but I can't be always beside her, and I'm called up to London
to-morrow on business, which I cannot put off. I thought that
perhaps, somehow, you'd look in a little, or—or take a sort of kind of
care,"—the poor man looked wistfully into the face of Ned Franks;
he knew not how to finish his sentence.
"Really, Mr. Sands," said the embarrassed sailor, "I do not see what I
could possibly do. I'm not in high favor with your wife; any
interference on my part she would certainly take amiss."
All the village knew that Nancy had done all in her power, by trying
to blacken Ned's character, to prevent his being appointed school-
master at Colme, and that she cordially disliked him.
"It was your wife's influence I was thinking of," said the clerk. "I
know that Mrs. Sands has a high opinion of Mrs. Franks. I have
myself heard her say"—He stopped short, for he could hardly have
repeated the compliment to the wife in the presence of the husband,
as it was that "Persis Meade was fifty times too good for that canting
fellow with the wooden arm."
"I am afraid that even my wife would be unable to do anything,"
replied Ned.
"Oh! don't say that, Mr. Franks," cried the clerk piteously, as if his
last hope were being cut away. "It's wonderful what the influence of
a woman can do. Do we not all know that Mrs. Franks, and you
helping her, were able to convert even a hard-hearted, unbelieving
Jew! Is not the baptism of Benjamin Isaacs, and of Benoni his son,
down in the register there, and was it not all from the speaking of
you and your wife? If she could do so much for a Jew, don't say she
can do nothing for a Christian."
Franks was touched by the earnest appeal, but could not help
thinking in his heart that Benjamin Isaacs, with all his Jewish
prejudices, had been a more hopeful subject than Nancy Sands. He
did not, however, speak; he only shook his head to express a doubt.
"Mrs. Franks could make her way with Mrs. Sands, I feel certain of
it," said the clerk, after another painful silence. "Women know how
to speak to women. Could she not take the babe with her? Nancy is
fond of babies." Sands's voice dropped almost to a whisper, as he
added, "She'd have gone through fire and water for our boy; there
was never a better mother; it was sorrow that set her wrong."
Ned could hold out no longer. "I'll ask my wife to call upon Mrs.
Sands to-morrow, and to take the baby, and maybe she'll get her to
return the visit," said the sailor, cheerily. "Keep up a good heart,
neighbor; there may be better days in store for you yet."
There was a little sound in the clerk's throat, something between a
cough and sob, and he pulled his handkerchief hastily out of his
pocket, for his eyes were brimming over with tears. Franks, who
hated to see a man cry, made his departure rather abruptly. " It is
getting very late," he observed; "I must wish Mr. Sands good-day."
"I could not help it; I could not help striking my flag when he
boarded me like that," muttered the sailor to himself, as with long
strides he hurried towards his school-house. "But to think of my
engaging my poor Persis to tackle a tigress, who's too much for the
parson himself! But how could I say him nay? He's nigh broken-
hearted, poor fellow! Certainly, if any one in the world is likely to say
a word to Nancy that will help her to sheer off from the whirlpool
that's drawing her in, that one is my sweet cherub of a wife."
Franks found that he was even later than he had supposed himself
to be; the pupils were already thronging to school; and heated,
hungry, and tired as he was, the master had almost directly to set to
work. He had not even time to snatch a hasty meal. The benches
were half filled with their noisy young occupants before Ned Franks
took his usual place behind his high desk. He fancied that he heard a
little tittering amongst the boys, for at their very last meeting he had
given them a lecture upon punctuality.
"So, my lads, you think that you have caught me napping for once,"
cried Ned Franks, in his cheerful tone; "but I'll not be hard on any
one who is a minute and three-quarters beyond time," Franks
glanced at the large clock on the wall, "if he brings as good an
excuse for delay as I do now. Here," he cried, waving his bank-note
triumphantly, "here are five pounds given to the collection for Wild
Rose Hollow, by our friend, Bat Bell, the miller."
A deafening shout arose from the boys. The miller had so long been
regarded as a money-making, money-saving screw, that they
cheered him at the top of their voices in his new character of a
money-giving man.
"I can match your piece of good news with another," said Persis
Franks, who had come into the school-room on purpose to tell her
tidings to her husband. "Mr. Leyton called while you were out, to let
us know that his aunt had this morning received a letter from Mrs.
Lane, enclosing for the same purpose a check for ten pounds."
There was a cheer for Mrs. Lane, but not quite so uproarious,
because the announcement excited less surprise.
"I'll top your story," said the smiling sailor, speaking so that all the
boys might hear. "Ben Stone, the carpenter, has kindly promised to
give five pounds' worth of his labor to repair the tumble-down
almshouses in Wild Rose Hollow."
A very loud hurrah followed this announcement, mingled with
clapping of hands. The young curate, who chanced to be passing the
school at that time, paused in some surprise on hearing such a
shout, and thought that the naval school-master must have a novel
and curious way of educating his pupils. But Ned Franks was
teaching his boys a lesson quite as important as even the
multiplication-table.
"Now you see, my lads," said the sailor, raising his hands to enforce
attention, "that he who cannot give much money to a charity, may
give his own honest hard work. Now, I've lately read in a capital
book [A] of school-boys, who, when shown how to go about it,
actually built a house for themselves, that the purses of generous
friends might be spared as much as possible. Now, I think that
there's no one here present, but myself, that has not two hands, and
on those hands ten fingers and thumbs. If any one here present
wants to help to set the almshouses to rights, and is willing to give
an odd hour of labor every week-day till the job is done, let him now
hold up his right hand."
Instantly, above the dark cluster of boys, a number of hands—white,
red, clean, and soiled—were held up.
"Or," continued Franks, "as the days are now long, if there be any
one who could and would give two hours daily to serving God, by
thus helping his poor, let that one now hold up both hands."
Up went all the left hands, to the sound of a cheer louder and more
joyous than the first; and then all the hands were employed in
clapping, as if, instead of an invitation to labor, the boys had
received an invitation to a feast. [B]
"Blessings on the noble-hearted little fellows!" thought the school-
master, as he looked down on that mass of bright young faces. And
Persis, as she fixed her proud eyes on her husband, thought, "Ned
can lead these boys wherever he will; for he never asks them to do
a brave, or kind, or generous thing, without first showing them how
to do it by his example!"
[A] Liefde's "Six Months amongst the Charities of Europe."
[B] I wish that the united energies of the children of every school
in Britain, whether for the rich or the poor, could thus be enlisted
in some good work. Masters and mistresses would find the
beneficial effect in the minds of their pupils. Even Ragged Schools
might have a collecting box for farthings; or children's sympathies
might be enlisted in behalf of some charity near them. Working
together for God promotes union, and it is a blessed thing for the
young to learn to delight in such work.
IX.
The Invitation.
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
ebookgate.com