Subject: Re: Batch Telnet (Re: diskless and 3Com 509)
To: None <mycroft@ai.mit.edu>
From: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
List: tech-net
Date: 03/17/1995 12:44:38
> The problem is that the client is closing the connection.  If the
> telnet client's stdin gets an EOF, one might expect it should at most
> close the write side of the TCP connection (i.e. use the shutdown(2)
> system call).

Right, that's exactly what I'd hope it would do.  I don't think I've
ever met a telnet client that did, though.

> With a more reasonable protocol, you'd expect the half-close to be
> propagated to the server, and the server would notice it and shut
> down if appropriate.  However, TCP doesn't have a half-close
> mechanism.

It sure did last time I read the RFC; either end can send a FIN packet
and continue to receive data in the other direction "forever", until
the other end sends a FIN or until the local TCP client insists on
closing down the read half of the connection.

Indeed, this is how it works in practice.  In case you still doubt,
here are a pair of programs.  sample-s.c is the server, sample-c.c is
the client.  Run sample-s, then (on the same machine) run sample-c.

Type stuff at sample-c and it will be sent to sample-s; anything
received from sample-s will be printed.  Type your EOF character to
sample-c and it will shutdown half the connection and stop listening to
stdin, but will continue to listen to the read half of the connection.
When it sees the s->c half of the connection drop, it exits.

sample-s reads stuff from sample-c and echoes it back, until it gets
EOF on the c->s half of the connection.  At this point it sleeps for
five seconds, prints the sum of all the bytes received, sleeps for five
more seconds, shuts down the s->c half of the connection, sleeps for
another five seconds, and exits.  sample-s also prints to its own
stdout a trace of the actions it's taking.

These compile and work under gcc 2.6.3 on our SunOS 4.1.2 Suns, and
under cc on NetBSD/sparc 1.0 (not tested under -current).

					der Mouse

			    mouse@collatz.mcrcim.mcgill.edu

#! /bin/sh
#
# Shar: Shell Archiver
#
# This archive created Fri Mar 17 12:42:23 1995
# Run this through sh to create:
#	sample-c.c
#	sample-s.c
echo x - sample-c.c \(1268 characters\)
sed 's/^X//' > sample-c.c << \EOF
X#include <stdio.h>
X#include <errno.h>
X#include <strings.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#define PORT 9876
X
Xvoid main(void);
Xvoid main(void)
X{
X int s;
X struct sockaddr_in sin;
X int n;
X int sending;
X fd_set fds;
X char buf[256];
X
X s = socket(AF_INET,SOCK_STREAM,0);
X if (s < 0)
X  { perror("socket");
X    exit(1);
X  }
X bzero(&sin,sizeof(sin));
X sin.sin_family = AF_INET;
X sin.sin_addr.s_addr = inet_addr("127.0.0.1");
X sin.sin_port = htons(PORT);
X if (connect(s,(struct sockaddr *)&sin,sizeof(sin)) < 0)
X  { perror("connect");
X    exit(1);
X  }
X sending = 1;
X while (1)
X  { FD_ZERO(&fds);
X    if (sending) FD_SET(0,&fds);
X    FD_SET(s,&fds);
X    n = select(FD_SETSIZE,&fds,0,0,0);
X    if (n < 0)
X     { if (errno == EINTR) continue;
X       perror("select");
X       exit(1);
X     }
X    if (FD_ISSET(s,&fds))
X     { n = read(s,&buf[0],sizeof(buf));
X       if (n < 0)
X	{ perror("net read");
X	  exit(1);
X	}
X       if (n == 0) exit(0);
X       write(1,&buf[0],n);
X     }
X    if (sending && FD_ISSET(0,&fds))
X     { n = read(0,&buf[0],sizeof(buf));
X       if (n < 0)
X	{ perror("stdin read");
X	  exit(1);
X	}
X       if (n == 0)
X	{ shutdown(s,1);
X	  sending = 0;
X	}
X       else
X	{ write(s,&buf[0],n);
X	}
X     }
X  }
X}
EOF
if test 1268 -ne "`wc -c sample-c.c`"
then
echo shar: error transmitting sample-c.c \(should have been 1268 characters\)
fi
echo x - sample-s.c \(1316 characters\)
sed 's/^X//' > sample-s.c << \EOF
X#include <stdio.h>
X#include <strings.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X
X#define PORT 9876
X
Xvoid main(void);
Xvoid main(void)
X{
X int acc;
X int s;
X struct sockaddr_in sin;
X int sinlen;
X char buf[256];
X int n;
X int i;
X unsigned int sum;
X
X acc = socket(AF_INET,SOCK_STREAM,0);
X if (acc < 0)
X  { perror("socket");
X    exit(1);
X  }
X bzero(&sin,sizeof(sin));
X sin.sin_family = AF_INET;
X sin.sin_addr.s_addr = INADDR_ANY;
X sin.sin_port = htons(PORT);
X if (bind(acc,(struct sockaddr *)&sin,sizeof(sin)) < 0)
X  { perror("bind");
X    exit(1);
X  }
X listen(acc,1);
X sinlen = sizeof(sin);
X s = accept(acc,(struct sockaddr *)&sin,&sinlen);
X if (s < 0)
X  { perror("accept");
X    exit(1);
X  }
X close(acc);
X sum = 0;
X while (1)
X  { n = read(s,&buf[0],sizeof(buf));
X    if (n < 0)
X     { perror("read");
X       exit(1);
X     }
X    if (n == 0) break;
X    write(s,&buf[0],n);
X    for (i=0;i<n;i++) sum += (unsigned char) buf[i];
X    printf("echoed %d, sum now %u\n",n,sum);
X  }
X printf("got read EOF, sleeping\n");
X sleep(5);
X printf("sending sum=%u\n",sum);
X sprintf(&buf[0],"%u\n",sum);
X write(s,&buf[0],strlen(&buf[0]));
X printf("sleeping again\n");
X sleep(5);
X printf("shutting down s->c connection\n");
X shutdown(s,1);
X printf("sleeping again\n");
X sleep(5);
X printf("exiting\n");
X exit(0);
X}
EOF
if test 1316 -ne "`wc -c sample-s.c`"
then
echo shar: error transmitting sample-s.c \(should have been 1316 characters\)
fi
exit 0
# end of shell archive