summaryrefslogtreecommitdiff
path: root/docs/tutorials/015/combine.shar
blob: 68a509ce351e917f3d6c1200ae16494f92565b8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1999-02-14 13:38 EST by <jcej@chiroptera.tragus.org>.
# Source directory was `/var/home/jcej/projects/ACE_wrappers/docs/tutorials/015'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    414 -rw-rw-r-- hdr
#    419 -rw-rw-r-- bodies
#   4228 -rw-rw-r-- page01.pre
#    194 -rw-rw-r-- page02.pre
#    318 -rw-rw-r-- page03.pre
#    178 -rw-rw-r-- page04.pre
#    304 -rw-rw-r-- page05.pre
#    418 -rw-rw-r-- page06.pre
#    321 -rw-rw-r-- page07.pre
#    501 -rw-rw-r-- page08.pre
#    640 -rw-rw-r-- page09.pre
#    978 -rw-rw-r-- page10.pre
#    334 -rw-rw-r-- page11.pre
#     49 -rw-rw-r-- page12.pre
#    326 -rw-rw-r-- page13.pre
#    540 -rw-rw-r-- page14.pre
#    770 -rw-rw-r-- page15.pre
#    660 -rw-rw-r-- page16.pre
#    213 -rw-rw-r-- page17.pre
#    372 -rw-rw-r-- page18.pre
#    281 -rw-rw-r-- page19.pre
#    356 -rw-rw-r-- page20.pre
#    151 -rw-rw-r-- page21.pre
#   2567 -rw-rw-r-- page22.pre
#    406 -rw-rw-r-- page04.pst
#    617 -rw-rw-r-- page09.pst
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh31334; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= hdr ==============
if test -f 'hdr' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'hdr' '(file already exists)'
else
  $echo 'x -' extracting 'hdr' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'hdr' &&
<HTML>
<HEAD>
X   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
X   <META NAME="Author" CONTENT="James CE Johnson">
X   <TITLE>ACE Tutorial 015</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
X
<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
X
<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
X
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1019173798 'hdr' &&
  chmod 0664 'hdr' ||
  $echo 'restore of' 'hdr' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'hdr:' 'MD5 check failed'
41322d388f7bb6c8eba031c4a6ab53ce  hdr
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`"
    test 414 -eq "$shar_count" ||
    $echo 'hdr:' 'original size' '414,' 'current size' "$shar_count!"
  fi
fi
# ============= bodies ==============
if test -f 'bodies' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'bodies' '(file already exists)'
else
  $echo 'x -' extracting 'bodies' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'bodies' &&
#
# The client application specific files
#
PAGE=2
client.cpp
Client_i.h
Client_i.cpp
#
# The server application specific files
#
server.cpp
Server_i.h
Server_i.cpp
Handler.h
Handler.cpp
#
# The basic protocol stream
#
Protocol_Stream.h
Protocol_Stream.cpp
Protocol_Task.h
Protocol_Task.cpp
#
# Send/Receive objects
#
XXmit.h
XXmit.cpp
Recv.h
Recv.cpp
#
# Protocol objects
#
Compressor.h
Compressor.cpp
Crypt.h
Crypt.cpp
SHAR_EOF
  $shar_touch -am 1022205498 'bodies' &&
  chmod 0664 'bodies' ||
  $echo 'restore of' 'bodies' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'bodies:' 'MD5 check failed'
a6c99d6567b0640ad524b196dc43647e  bodies
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`"
    test 419 -eq "$shar_count" ||
    $echo 'bodies:' 'original size' '419,' 'current size' "$shar_count!"
  fi
fi
# ============= page01.pre ==============
if test -f 'page01.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page01.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page01.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' &&
X
In a typical client/server system you will be sending and receiving
X      data.  That's the whole point after all.
<P>
In the client/server tutorials that we've done so far it was just a
X      matter of sending a buffer of data to the peer.  This was done
X      with the send*() and recv*() methods of the ACE_SOCK* objects.
<P>
In a more robust system, one might want to process the data before
X      sending it to a peer and "unprocess" it after reading from a
X      peer.  These processing steps might include encryption,
X      compression, applying checksums or any number of other actions.
<P>
In this tutorial a Protocol_Stream object is created to encrypt and
X      compress* data being sent between peers.  Both client and server 
X      applications are presented as well.  I present the application
level code first and then go into the details of the protocol stream
and it's helper objects.  If the stream stuff in the application logic 
is confusing then just read on by and come back to it after the later
discussions.
<P>
Disclaimer:
<br>
<ul>
Several folks have reported problems with this tutorial on Win32.
There are a couple of ways to solve this.  I've chosen to solve it by
using the ACE_Select_Reactor on all platforms instead of taking the
OS-default.
</ul>
<P>
Kirthika's abstract:
<UL>
The Protocol Stream model consists of a stream layer which pushes the
data towards the underlying SOCK_Stream thru the stages of encryption
and compression. This data is received at the other end of the
SOCK_Stream and sent up to its Protocol_Stream layer via the stages of
decompression and decryption and an optional Reader task. This is very
similar to the model of the TCP/IP stack (specifically the datalink to
physical layer portion).
<P>
ACE_Message_Blocks are used to communicate between the client and the
server across the Protocol Stream, which abstracts the protocol
conformance details. The underlying SOCK_Stream is used to set up the
connection using the ACE_SOCK_Connector class. Once the connector
completes its job, the SOCK_Stream pointer is passed on to the
Protocol Stream which now takes over. The Client has put() and get()
methods to send and receive data from the server. 
<P>
The server is implemented using the ACE_Acceptor to listen at the port
for connections and a reactor for delegating events to the appropriate
event handler. The handle_input () method of the handler simply allows
the stream to receive the data and hand it over to the Handler_Task (a
derivative of the ACE_Task) which will then process it.
<P>
The implementation of this Protocol Stream model is done using the
ACE_Module class. The module for Xmit/Recv is shoved in first
into the stream, followed by the compression and encryption modules. The
optional Reader if defined is bundled with a dummy task
(ACE_Thru_Task class) into a module. The get() and put() methods do
the job of reading and writing to the Stream. Each module is made up
of a pair of Protocol Tasks. A Protocol Task is a derivative of the
ACE_Task and whose recv() and send() methods need to be filled to
perform the appropriate task.
<P>
The Xmit object derives from the Protocol task and has a send() method
which does the task of transmitting data to the underlying
SOCK_Stream. Keeping the fragmentation and reassembly issues in mind,
block-size is also sent across with the block of data.
The Recv object uses a dummy Message Block to provoke the Protocol
Task object to call the recv() on it. This is done by being
foresighted about the use of mutliple threads instead of a single
thread.
<P>
The compression/decompression is bundled in a single Protocol Task
object with the send () method doing the compression and the recv()
doing the decompression. Similarly, the encrption/decryption is done
using a single Protocol Task object.
<P>
This tutorial provides a glimpse on how to design and implement a
protocol in layers and also revises a lot of what has been learnt until
now from the previous tutorials.
(for instance, Message_Block, Task, Acceptor, Connector, Event_Handler
etc.)
</UL>
<P>
<font size=-1>* Ok, I didn't really implement encryption and
X        compression objects.  I'll leave that as a thought
X        exercise!</font>
SHAR_EOF
  $shar_touch -am 0214124599 'page01.pre' &&
  chmod 0664 'page01.pre' ||
  $echo 'restore of' 'page01.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page01.pre:' 'MD5 check failed'
a09c3ff1f21a90aab991e0f38dc00458  page01.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`"
    test 4228 -eq "$shar_count" ||
    $echo 'page01.pre:' 'original size' '4228,' 'current size' "$shar_count!"
  fi
fi
# ============= page02.pre ==============
if test -f 'page02.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page02.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page02.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' &&
We'll take a look first at the client application.  As usual, our goal
X      is to keep the main() application as simple as possible and
X      delegate the tricky stuff to another object.
X
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page02.pre' &&
  chmod 0664 'page02.pre' ||
  $echo 'restore of' 'page02.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page02.pre:' 'MD5 check failed'
6a2e64962c95b349625f418502c95952  page02.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`"
    test 194 -eq "$shar_count" ||
    $echo 'page02.pre:' 'original size' '194,' 'current size' "$shar_count!"
  fi
fi
# ============= page03.pre ==============
if test -f 'page03.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page03.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page03.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' &&
The Client object is designed to hide all of the messy connection
X      logic from it's users.  It also provides put/get methods for
X      sending data to the server and receiving the server's response.
X      Note the Protocol_Stream member that will take care of
X      converting and sending/receiving the data.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page03.pre' &&
  chmod 0664 'page03.pre' ||
  $echo 'restore of' 'page03.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page03.pre:' 'MD5 check failed'
95326c064b10bbda428d3c967f285760  page03.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`"
    test 318 -eq "$shar_count" ||
    $echo 'page03.pre:' 'original size' '318,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pre ==============
if test -f 'page04.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' &&
The implementation of the Client object.  Only the open() method
X      really does any work.  The other methods simply delegate their
X      function to the Protocol_Stream.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page04.pre' &&
  chmod 0664 'page04.pre' ||
  $echo 'restore of' 'page04.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pre:' 'MD5 check failed'
2955ca8d3b0fc6840f3d371aea528b8d  page04.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`"
    test 178 -eq "$shar_count" ||
    $echo 'page04.pre:' 'original size' '178,' 'current size' "$shar_count!"
  fi
fi
# ============= page05.pre ==============
if test -f 'page05.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page05.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page05.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' &&
Like the client, we want to keep the main() part of our server code as 
X      simple as possible.  This is done by putting most of the work
X      into the Handler object that will deal with client connections.
XFrom the looks of the code below, I think we've been successful in our 
simplification.
<HR>
X
SHAR_EOF
  $shar_touch -am 1019173798 'page05.pre' &&
  chmod 0664 'page05.pre' ||
  $echo 'restore of' 'page05.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page05.pre:' 'MD5 check failed'
8833b0d7f90a4d13f68b8cdb9147c29a  page05.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`"
    test 304 -eq "$shar_count" ||
    $echo 'page05.pre:' 'original size' '304,' 'current size' "$shar_count!"
  fi
fi
# ============= page06.pre ==============
if test -f 'page06.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page06.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page06.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' &&
The Server object exists in order simplify the 
main() application level.  To that end, it hides the details of
creating an acceptor and managing the reactor.
<P>
The static close() method available for a signal handler as you saw on 
the previous page.  Of course the assumption here is that there would
only be one Server instance but since you can't provide a TCP/IP port, 
that's probably a valid assumption!
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page06.pre' &&
  chmod 0664 'page06.pre' ||
  $echo 'restore of' 'page06.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page06.pre:' 'MD5 check failed'
06a0abefdf4e704b42147f433bd27e4d  page06.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`"
    test 418 -eq "$shar_count" ||
    $echo 'page06.pre:' 'original size' '418,' 'current size' "$shar_count!"
  fi
fi
# ============= page07.pre ==============
if test -f 'page07.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page07.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page07.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' &&
And now the implementation of Server.  This is actually just the
main() code from a previous tutorial broken into appropriate method
calls.  It may seem silly to do this rather than keeping the stuff in
main() but you'll find that you have less trouble enhancing an
application when you take this sort of approach.
<HR>
X
SHAR_EOF
  $shar_touch -am 1019173798 'page07.pre' &&
  chmod 0664 'page07.pre' ||
  $echo 'restore of' 'page07.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page07.pre:' 'MD5 check failed'
7dfb75884939c3c05ee1e1000956e9f4  page07.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`"
    test 321 -eq "$shar_count" ||
    $echo 'page07.pre:' 'original size' '321,' 'current size' "$shar_count!"
  fi
fi
# ============= page08.pre ==============
if test -f 'page08.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page08.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page08.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' &&
The Handler object is our event handler.  You can use either
ACE_Event_Handler or ACE_Svc_Handler<> for the baseclass.  I generally 
prefer the latter since it takes care of some housekeeping that I
would otherwise be responsible for.
<P>
The class declaration is taken almost exactly from a previous
tutorial.  A good design will have a simple handler object that will
collect data from the peer and pass it along to another object for
processing.  Again, keep it simple and delegate authority.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page08.pre' &&
  chmod 0664 'page08.pre' ||
  $echo 'restore of' 'page08.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page08.pre:' 'MD5 check failed'
471889eb736a025fc46719549bca160a  page08.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`"
    test 501 -eq "$shar_count" ||
    $echo 'page08.pre:' 'original size' '501,' 'current size' "$shar_count!"
  fi
fi
# ============= page09.pre ==============
if test -f 'page09.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page09.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page09.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page09.pre' &&
Like any other event handler, the handle_input() method will be
responsible for getting data from the peer() and doing something with
it.  In this case, we have a Protocol_Stream to deal with.  We'll use
the stream for the actual I/O but we are ultimately responsible for
processing the data from the peer.  To do that, we've created a
Handler_Task that fits within the Protocol_Stream framework to process 
data that has been received.  Handler::handle_input() will tell the stream that 
it's time to read data and that data will eventually show up at
Handler_Task::recv() where we'll process it as required by our
application logic.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page09.pre' &&
  chmod 0664 'page09.pre' ||
  $echo 'restore of' 'page09.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page09.pre:' 'MD5 check failed'
da5c4b11d356dc9caf3a8e3b6b2527a6  page09.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pre'`"
    test 640 -eq "$shar_count" ||
    $echo 'page09.pre:' 'original size' '640,' 'current size' "$shar_count!"
  fi
fi
# ============= page10.pre ==============
if test -f 'page10.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page10.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page10.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page10.pre' &&
And so finally we come to the Protocol_Stream.  That, after all, is
the focus of the entire tutorial but it took us half of the day to get 
here!
<P>
The Protocol_Stream uses an ACE_Stream to move an ACE_Message_Block
through a series of tasks.  Each task in the stream is responsible for 
performing some operation on the data in the message block.  That is
the nature of a protocol stream (or "stack" if you prefer).  In this
stream, the data is compressed and encrypted* on its way between
peers.  We also allow users of the stream to install a reader task to
handle data that percolates up from the peer.  As you saw a page or
two ago, this is most useful for a server.
X
<P>
<font size=-1>*Again, I just pretend to do these things.  It would
take another day or two to go through any sort of reasonable
encryption or compression!</font>
<P>
Before we get into the code, here's a picture that's shows what's
going on here.
<P><center><img src="stream.gif"></center></p>
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page10.pre' &&
  chmod 0664 'page10.pre' ||
  $echo 'restore of' 'page10.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page10.pre:' 'MD5 check failed'
a74423390fb5217104c6d89d7f202e8b  page10.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page10.pre'`"
    test 978 -eq "$shar_count" ||
    $echo 'page10.pre:' 'original size' '978,' 'current size' "$shar_count!"
  fi
fi
# ============= page11.pre ==============
if test -f 'page11.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page11.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page11.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page11.pre' &&
And now the implementation of the Protocol_Stream.  There are more
lines of code here than we've seen so far but it still isn't
complicated.  The basic idea is to construct the ACE_Stream with our
set of protocol objects that will manipulate the data.  Our primary
concern in this file is to get everything in the correct order!
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page11.pre' &&
  chmod 0664 'page11.pre' ||
  $echo 'restore of' 'page11.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page11.pre:' 'MD5 check failed'
b0e968102fb417b12710e99465f4e387  page11.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page11.pre'`"
    test 334 -eq "$shar_count" ||
    $echo 'page11.pre:' 'original size' '334,' 'current size' "$shar_count!"
  fi
fi
# ============= page12.pre ==============
if test -f 'page12.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page12.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page12.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page12.pre' &&
A quick look at the Protocol_Task header...
<HR>
SHAR_EOF
  $shar_touch -am 0210223499 'page12.pre' &&
  chmod 0664 'page12.pre' ||
  $echo 'restore of' 'page12.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page12.pre:' 'MD5 check failed'
5258df32a7fddcecfe902aec8440f98f  page12.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page12.pre'`"
    test 49 -eq "$shar_count" ||
    $echo 'page12.pre:' 'original size' '49,' 'current size' "$shar_count!"
  fi
fi
# ============= page13.pre ==============
if test -f 'page13.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page13.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page13.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page13.pre' &&
The Protocol_Task implementation takes care of the open(), close(),
put() and svc() methods so that derivatives can concentrate on the
send() and recv() methods.  After a while you find that most
ACE_Task<> derivatives look very similar in the four basic methods and 
only need one or two additional to do any real work.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page13.pre' &&
  chmod 0664 'page13.pre' ||
  $echo 'restore of' 'page13.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page13.pre:' 'MD5 check failed'
8b74a6d79d158222928097a9bb1335db  page13.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page13.pre'`"
    test 326 -eq "$shar_count" ||
    $echo 'page13.pre:' 'original size' '326,' 'current size' "$shar_count!"
  fi
fi
# ============= page14.pre ==============
if test -f 'page14.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page14.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page14.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page14.pre' &&
The Xmit object knows how to send data to the peer.  It sits at the
tail of the stream and gets everything that flows down from the head.
In keeping with the spirit of things, this object does only one thing
and doesn't concern itself with anyone else' details.
<P>
The only thing you might want to do is combine it with Recv.  Why?
As you'll realize in a page or two, the Xmit and Recv objects must
interact if you're going to ensure a safe transit.  By having a single 
object it's easier to coordinate and maintain the interaction.
<HR>
SHAR_EOF
  $shar_touch -am 1019213498 'page14.pre' &&
  chmod 0664 'page14.pre' ||
  $echo 'restore of' 'page14.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page14.pre:' 'MD5 check failed'
bfc300ca2da5b82a5e452713cbf2f544  page14.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page14.pre'`"
    test 540 -eq "$shar_count" ||
    $echo 'page14.pre:' 'original size' '540,' 'current size' "$shar_count!"
  fi
fi
# ============= page15.pre ==============
if test -f 'page15.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page15.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page15.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page15.pre' &&
The implementation of Xmit isn't too complicated.  If we choose to
combine it with the Recv task we simply lift the recv() method from
that object and drop it into this one.
<P>
Note that close() must decide if it's being called when the stream is
shutdown or when it's svc() method exits.  Since we tell the baseclass 
not to use any threads it's a safe bet that flags will always be
non-zero.  Still, it's good practice to plan for the future by
checking the value.
<P>
Note also that when we send the data we prefix it with the data size.
This let's our sibling Recv ensure that an entire block is received
together.  This can be very important for compression and encryption
processes which typically work better with blocks of data instead of
streams of data.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page15.pre' &&
  chmod 0664 'page15.pre' ||
  $echo 'restore of' 'page15.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page15.pre:' 'MD5 check failed'
0d79137eaedd73b820037fcafe6e16b6  page15.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page15.pre'`"
    test 770 -eq "$shar_count" ||
    $echo 'page15.pre:' 'original size' '770,' 'current size' "$shar_count!"
  fi
fi
# ============= page16.pre ==============
if test -f 'page16.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page16.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page16.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page16.pre' &&
Recv is the sibling to Xmit.  Again, they could be combined into a
single object if you want.
<P>
An ACE_Stream is designed to handle downstream traffic very
well.  You put() data into it and it flows along towards the tail.
However, there doesn't seem to be a way to put data in such that it
will travel upstream.  To get around that, I've added a get() method
to Recv that will trigger a read on the socket.  Recv will then put
the data to the next upstream module and we're on our way.  As noted
earlier, that data will eventually show up either in the <i>reader</i> 
(if installed on the stream open()) or the stream head reader task's
message queue.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page16.pre' &&
  chmod 0664 'page16.pre' ||
  $echo 'restore of' 'page16.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page16.pre:' 'MD5 check failed'
2d89b3c894cfcfdfef47ae506913cdad  page16.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page16.pre'`"
    test 660 -eq "$shar_count" ||
    $echo 'page16.pre:' 'original size' '660,' 'current size' "$shar_count!"
  fi
fi
# ============= page17.pre ==============
if test -f 'page17.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page17.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page17.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page17.pre' &&
The Recv implementation is nearly as simple as Xmit.  There's
opportunity for error when we get the message size and we have to
manage the lifetime of the tickler but other than that it's pretty
basic stuff.
<HR>
SHAR_EOF
  $shar_touch -am 1019173798 'page17.pre' &&
  chmod 0664 'page17.pre' ||
  $echo 'restore of' 'page17.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page17.pre:' 'MD5 check failed'
7db337f2c6ec74d75560534dec550b0e  page17.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page17.pre'`"
    test 213 -eq "$shar_count" ||
    $echo 'page17.pre:' 'original size' '213,' 'current size' "$shar_count!"
  fi
fi
# ============= page18.pre ==============
if test -f 'page18.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page18.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page18.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page18.pre' &&
This and the next three pages present the protocol objects that
provide compression and encryption.  If you were hoping to learn the
secrets of compression and encryption then I'm going to disappoint
you.  There are some really good libraries out there that do this
stuff though and if anyone wants to integrate one of them into the
tutorial I'll be glad to take it!
<HR>
SHAR_EOF
  $shar_touch -am 1019213698 'page18.pre' &&
  chmod 0664 'page18.pre' ||
  $echo 'restore of' 'page18.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page18.pre:' 'MD5 check failed'
dc5f706bd5a27009aed167c0b137648e  page18.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page18.pre'`"
    test 372 -eq "$shar_count" ||
    $echo 'page18.pre:' 'original size' '372,' 'current size' "$shar_count!"
  fi
fi
# ============= page19.pre ==============
if test -f 'page19.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page19.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page19.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page19.pre' &&
Here we implement the details of our compression.  By having both
compression and decompression in one object it's easier to keep track
of implementation details.  Splitting Xmit/Recv like I did will make
things more difficult if something has to change in their interaction.
<HR>
SHAR_EOF
  $shar_touch -am 1019205798 'page19.pre' &&
  chmod 0664 'page19.pre' ||
  $echo 'restore of' 'page19.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page19.pre:' 'MD5 check failed'
4eb5dcd181f180d6c460971903efb288  page19.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page19.pre'`"
    test 281 -eq "$shar_count" ||
    $echo 'page19.pre:' 'original size' '281,' 'current size' "$shar_count!"
  fi
fi
# ============= page20.pre ==============
if test -f 'page20.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page20.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page20.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page20.pre' &&
While I might be able to come up with a competitive compressor, I
don't have a snowball's chance to code up encryption.  I'd be better
off piping the data through the standard Unix crypt command.
<P>
So, while I was lazy with Compress, I'm realistic with Crypt.  I'll
show you the hooks and entry points and let someone else contribute an 
encryptor.
<HR>
SHAR_EOF
  $shar_touch -am 1019213798 'page20.pre' &&
  chmod 0664 'page20.pre' ||
  $echo 'restore of' 'page20.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page20.pre:' 'MD5 check failed'
7f75130d385a34b2c421a8d7cae69cc3  page20.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page20.pre'`"
    test 356 -eq "$shar_count" ||
    $echo 'page20.pre:' 'original size' '356,' 'current size' "$shar_count!"
  fi
fi
# ============= page21.pre ==============
if test -f 'page21.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page21.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page21.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page21.pre' &&
The encryption implementation isn't any smarter than that of the
compressor.  Still, the hooks are there for you to insert your
favorite library.
<HR>
SHAR_EOF
  $shar_touch -am 1019213898 'page21.pre' &&
  chmod 0664 'page21.pre' ||
  $echo 'restore of' 'page21.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page21.pre:' 'MD5 check failed'
7f0f64452098cdef38c5496340a4b6c7  page21.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page21.pre'`"
    test 151 -eq "$shar_count" ||
    $echo 'page21.pre:' 'original size' '151,' 'current size' "$shar_count!"
  fi
fi
# ============= page22.pre ==============
if test -f 'page22.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page22.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page22.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page22.pre' &&
Well, this has certainly been one of the more verbose tutorials to
date.  I must say "thanks" to everyone who stuck it out this far!
<P>
A quick review of what we've done:
<UL>
X
<LI>Create a simple client application and Client object that uses a
Protocol Stream without really knowing how they work.  The app (and
object) rely on the public interface of the Protocol Stream to get the 
job done.  At this level the protocol details are irrelevant.
<P>
<LI>Next, we create a simple server application and Server object
similar to the client.  The Protocol Stream is of course used and we
have to know a little more so that we can insert a <i>reader</i> that
will ultimately process the data from the client.
<P>
<LI>We then go into the details of the Protocol_Stream implementation
and it's Protocol_Task object that forms the basis for the stream
tasks.  Each object is kept as small and simple as possible to improve 
reusability and future maintenance.
<P>
<LI>Finally, the individual protocol objects are discused.  Separate
objects for the peer interface were created as well as the bogus
compressor and encryptor.  The protocol can be extended or modified by 
creating new such objects and installing them in the Protocol_Stream's 
open() method.
X
</UL>
<P>
X
It doesn't sound like much but it certainly took a bunch of files to
get there.  It's easy to get lost in the details when there's so much
to cover so you're encouraged to go over things a couple of times.
As always, enhancments of the tutorials is welcome!
<P>
Here's the complete file list:
<UL>
<LI><A HREF="client">Makefile</A>
<P>
<LI><A HREF="Makefile.client">client Makefile</A>
<LI><A HREF="client.cpp">client.cpp</A>
<LI><A HREF="Client_i.h">Client_i.h</A>
<LI><A HREF="Client_i.cpp">Client_i.cpp</A>
<P>
<LI><A HREF="Makefile.server">Server Makefile</A>
<LI><A HREF="server.cpp">server.cpp</A>
<LI><A HREF="Server_i.h">Server_i.h</A>
<LI><A HREF="Server_i.cpp">Server_i.cpp</A>
<LI><A HREF="Handler.h">Handler.h</A>
<LI><A HREF="Handler.cpp">Handler.cpp</A>
<P>
<LI><A HREF="Protocol_Stream.cpp">Protocol_Stream.cpp</A>
<LI><A HREF="Protocol_Stream.h">Protocol_Stream.h</A>
<LI><A HREF="Protocol_Task.cpp">Protocol_Task.cpp</A>
<LI><A HREF="Protocol_Task.h">Protocol_Task.h</A>
<P>
<LI><A HREF="Xmit.cpp">Xmit.cpp</A>
<LI><A HREF="Xmit.h">Xmit.h</A>
<LI><A HREF="Recv.cpp">Recv.cpp</A>
<LI><A HREF="Recv.h">Recv.h</A>
<P>
<LI><A HREF="Compressor.cpp">Compressor.cpp</A>
<LI><A HREF="Compressor.h">Compressor.h</A>
<LI><A HREF="Crypt.cpp">Crypt.cpp</A>
<LI><A HREF="Crypt.h">Crypt.h</A>
</UL>
SHAR_EOF
  $shar_touch -am 1022205898 'page22.pre' &&
  chmod 0664 'page22.pre' ||
  $echo 'restore of' 'page22.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page22.pre:' 'MD5 check failed'
9eae1b08c2e061a68bfc1f3cbc2f59de  page22.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page22.pre'`"
    test 2567 -eq "$shar_count" ||
    $echo 'page22.pre:' 'original size' '2567,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pst ==============
if test -f 'page04.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' &&
<HR>
<P>
Ok, that's it for the client.  We've seen a very simple main()
X    followed by an equally simple Client object.
<P>
For a quick look back:
<UL>
<LI><A HREF="Makefile.client">client Makefile</A>
<LI><A HREF="client.cpp">client.cpp</A>
<LI><A HREF="Client_i.h">Client_i.h</A>
<LI><A HREF="Client_i.cpp">Client_i.cpp</A>
</UL>
<P>
Now we'll move on and examine the server counter-part of our client.
SHAR_EOF
  $shar_touch -am 1022205698 'page04.pst' &&
  chmod 0664 'page04.pst' ||
  $echo 'restore of' 'page04.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pst:' 'MD5 check failed'
00419a8ab9a3ddae3261840b62afdc4a  page04.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`"
    test 406 -eq "$shar_count" ||
    $echo 'page04.pst:' 'original size' '406,' 'current size' "$shar_count!"
  fi
fi
# ============= page09.pst ==============
if test -f 'page09.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page09.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page09.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page09.pst' &&
<HR>
<P>
That's it for the server-specific code.  I think I've been fairly
successful in keeping it simple and to the point.  There are a couple
of places where the as-yet-undescribed Protocol_Stream pops up and may 
cause confusion.  We're going to discuss that mystery now but before
we do here's the list of server files if you want to review:
X
<UL>
<LI><A HREF="Makefile.server">Server Makefile</A>
<LI><A HREF="server.cpp">server.cpp</A>
<LI><A HREF="Server_i.h">Server_i.h</A>
<LI><A HREF="Server_i.cpp">Server_i.cpp</A>
<LI><A HREF="Handler.h">Handler.h</A>
<LI><A HREF="Handler.cpp">Handler.cpp</A>
</UL>
<P>
SHAR_EOF
  $shar_touch -am 1022205698 'page09.pst' &&
  chmod 0664 'page09.pst' ||
  $echo 'restore of' 'page09.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page09.pst:' 'MD5 check failed'
a96009f43a6fe8e6b52ffa923993a3e1  page09.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pst'`"
    test 617 -eq "$shar_count" ||
    $echo 'page09.pst:' 'original size' '617,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh31334
exit 0