[comp.protocols.tcp-ip] Sockets vs streams. An attempt to answer the original question

hedrick@athos.rutgers.edu (Charles Hedrick) (08/28/90)

Before we got diverted, the original question was whether code that
needs to access the network should use sockets or streams.  Since
then, I've looked up the streams documentation in SunOS 4.1.  I'm
going to assume that's typical of what streams is like, but of course
that could be wrong.  So take this with a grain of salt.

In my opinion, if you want to support a full range of systems, you're
going to have to deal with both sockets and streams.  So that's not
the basic design choice.  It's also not a big issue anyway.  All
network code that I've seen has subroutines for doing the low-level
network operations such as openning connections.  These are not
complex subroutines.  Maybe half a page each.  They just do the right
combination of socket, bind, connect, etc.  So the streams version is
going to have another version of the subroutine to open a connection,
that uses t_bind and t_connect instead of bind and connect.  Big deal.
Similarly, data transfer subroutines can use send and recv for
Berkeley and t_snd and t_rcv for the streams version.

The real issue seems to be not this, but the problem that streams
doesn't fit the normal Unix view of I/O.  At least in SunOS 4.1, you
can't do read and write on a stream.  Thus the special t_snd and t_rcv
calls for I/O.  Sockets allow you to use either special send and recv
calls, which allow more detailed control over network-level handling,
or normal read and write.  But SunOS provides a streams module you can
push that gives you read and write.  It can't deal with out of band
data, but if you know your application doesn't use OOB, it might be
usable.

It seems clear that you can get streams/sockets compatibility by doing
everything with subroutines and supplying streams and sockets versions
for everything.  But there are two questions that can really only be
answered by people who have experience with using streams:

(1) What is the performance penalty for using the read/write interface
in streams?  With sockets, the send/recv interface is at the same
level as the read/write interface, so there's no reason to expect any
performance penalty for using read and write.  Indeed it's very rare
that you see programs using send and recv.  This allows you to use
things like printf, and to set primary input or output to a socket.
With a stream, if you want to do this, you have to push on the
read/write interface.  I could imagine ways of implementing it that
wouldn't result in any more overhead than doing the low-level I/O, but
there's no way to know whether this will happen in real
implementations other than trying it.  If read/write turns out to be
unacceptable under streams, then you'll need to go to the approach of
using subroutines or macros for your low-level code, so that you can
supply both socket and streams versions.  (By the way, the original
ATT claim was that sockets were a terrible wart on Unix, and streams
were "clean".  I'm not sure what -- if anything -- that meant.  It
seems to me that sockets makes network I/O look a lot more like normal
file I/O than streams do.)

(2) Is it a good idea to use the "sockets library"?  Comments have
been made about both overhead and portability.  Again, this is an
issue that only experience can settle.  Most applications of sockets
that I've seen use read and write.  In this case, all you need the
sockets library for is to open and close the connection.  Once it's
open, you're going to use read and write directly, which will not need
to pass through any sockets emulation.  So this seems to reduce to the
previous question, of whether the read/write interface to streams has
too much overhead.  Whether it makes sense to use the sockets library
for opening and closing seems to reduce to the issue of how good the
sockets libraries are and how compatible the streams implementations
are.  Clearly streams itself is just a framework.  It's only the
actual device drivers and streams modules that determine whether two
implementations look at all alike.  One could imagine a world in which
each streams implementation looks different, but all their socket
emulation libraries are fairly compatible.  One could also imagine a
world in which everyone used the same streams code, but the socket
libraries are very flaky.  In the first case, you'd be better off to
use sockets, in the second you'd be better off to use streams.

Since it's hard to get reliable information on any of these topics, I
think I'd make sure that my code is designed in such a way that you
can handle either case.  That is, I'd run all network operations --
opening, closing, and actual I/O -- through low-level subroutines or
macros that are designed so you can implement them with either sockets
or streams.

henry@zoo.toronto.edu (Henry Spencer) (08/28/90)

In article <Aug.27.17.09.46.1990.14447@athos.rutgers.edu> hedrick@athos.rutgers.edu (Charles Hedrick) writes:
>...  (By the way, the original
>ATT claim was that sockets were a terrible wart on Unix, and streams
>were "clean".  I'm not sure what -- if anything -- that meant.  It
>seems to me that sockets makes network I/O look a lot more like normal
>file I/O than streams do.)

It is important to distinguish "streams" (Dennis Ritchie's term for his
revised non-block-device i/o system) from "STREAMS" (what AT&T put into
System V).  Dennis's streams cleaned up a lot of mess, and improved
performance to boot.  But as Dennis is rumored to have said, "`streams'
means something different when shouted".

The way to do i/o on Dennis's streams was with "read" and "write".
Network i/o, in general, looked *exactly* like local device i/o.  This
is the way it should be, unlike what both Berkeley and AT&T have done
(both have reluctantly conceded that most people want to use "read"
and "write" and have made that work, but their hearts were clearly
elsewhere).
-- 
TCP/IP: handling tomorrow's loads today |Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads tomorrow|  henry@zoo.toronto.edu   utzoo!henry

guy@auspex.auspex.com (Guy Harris) (08/29/90)

>The real issue seems to be not this, but the problem that streams
>doesn't fit the normal Unix view of I/O.  At least in SunOS 4.1, you
>can't do read and write on a stream.

Well, yes, you can, actually.  You have programs that do "read()" and
"write()" on terminals under SunOS 4.x, right?  If so, they're doing
"read()" and "write()" on streams.... 

What you can't do in SunOS 4.1 - nor in the S5R3 systems from which much
of that code came - is "read()" and "write()" on *TLI* streams that
don't have the "tirdwr" module pushed atop them.  That module, which you
mention, actually comes from S5R3.

In S5R[34], streams isn't really the equivalent of sockets, streams
*plus TLI* is.

(I wouldn't be at all surprised to find that in the Research UNIX
streams code, you *can* do "read()" and "write()" on streams used for
network connections.  I don't know if this is the case, but I wouldn't
be surprised if it were....)

stanonik@NPRDC.NAVY.MIL (Ron Stanonik) (08/29/90)

Wollongong's win 3b implementation of tcp/ip (on our 3b2's running
sysVr3) seems to only push tirdwr for the accept socket call.  We've
installed a couple of bsd programs (syslog and lpr), which use
read/write and seem to work just fine.

Anybody know what tirdwr does?  Gather/scatter packets to satisfy
the read/write size argument?  Some oob handling?

Ron Stanonik
stanonik@nprdc.navy.mil

ps. Is there anyway to get a list of all the modules pushed onto
a stream.  I_LOOK only lists the top most module.

jbvb@FTP.COM (James B. Van Bokkelen) (08/29/90)

    The way to do i/o on Dennis's streams was with "read" and "write".
    Network i/o, in general, looked *exactly* like local device i/o.  This
    is the way it should be, unlike what both Berkeley and AT&T have done
    (both have reluctantly conceded that most people want to use "read"
    and "write" and have made that work, but their hearts were clearly
    elsewhere).

I would say rather that using read/write on network connections is
the way most people would *like* it to be.  The reality is that on
most systems the local filesystem is a pretty tame beast compared to
a network connection.  Unless the OS/language combination's
read/write was designed with network connections in mind (which means
boolean flag arguments and wide variations in behaviour depending on
them), use of read/write is likely to result in a cantankerous and
unreliable network application...

James B. VanBokkelen		26 Princess St., Wakefield, MA  01880
FTP Software Inc.		voice: (617) 246-0900  fax: (617) 246-0901

henry@zoo.toronto.edu (Henry Spencer) (08/31/90)

In article <9008291448.AA14069@ftp.com> jbvb@ftp.com writes:
>    The way to do i/o on Dennis's streams was with "read" and "write".
>    Network i/o, in general, looked *exactly* like local device i/o.  This
>    is the way it should be...
>
>I would say rather that using read/write on network connections is
>the way most people would *like* it to be.  The reality is that on
>most systems the local filesystem is a pretty tame beast compared to
>a network connection.  Unless the OS/language combination's
>read/write was designed with network connections in mind (which means
>boolean flag arguments and wide variations in behaviour depending on
>them), use of read/write is likely to result in a cantankerous and
>unreliable network application...

Only if you have a cantankerous and unreliable network. :-)  True, the
network interface is more complex than most device interfaces (although
whether it is more complex than the tty interface, in particular, is a
debatable point!)... but most applications don't care.  They just want
to open a connection to service X on machine Y and reliably send bits
back and forth.  The complexities have to be present for the occasional 
sophisticated customer, but the simple customer shouldn't have to worry
about them.  The Unix tty interface is quite complex, but most programs
can ignore most of it -- if they want to print an error message, they
just say "fprintf(stderr, ..." and it works.  That's the way the network
interface should be too:  some simple way to open a connection (I find
it impossible to comprehend why 4BSD doesn't have an open-connection-to-
service-X-on-machine-Y library function, given how stereotyped and how
messy this job is), read/write for i/o, close for shutdown (and ioctl
for option setting etc. for those who care).  That's all most customers
want.

The networking facilities in Eighth/Ninth/Tenth Edition Unix within
Bell Labs are existence proofs that this approach can and does work.
-- 
TCP/IP: handling tomorrow's loads today| Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads someday|  henry@zoo.toronto.edu   utzoo!henry

jbvb@FTP.COM (James B. Van Bokkelen) (08/31/90)

    ....  The complexities have to be present for the occasional sophisticated
    customer, but the simple customer shouldn't have to worry about them.  The
    Unix tty interface is quite complex, but most programs can ignore most
    of it -- if they want to print an error message, they just say
    "fprintf(stderr, ..." and it works.  That's the way the network interface
    should be too:  some simple way to open a connection (I find it impossible
    to comprehend why 4BSD doesn't have an open-connection-to-service-X-on-
    machine-Y library function, given how stereotyped and how messy this job
    is), read/write for i/o, close for shutdown (and ioctl for option setting
    etc. for those who care).  That's all most customers want.

I see where our viewpoints differ: I am selling applications to end-users,
and I intend to support them.  Most of the end-users who use our Development
Kit for one-off or in-house applications are probably quite satisfied with
open/read/write/close.  However, I am careful to advise any OEMs who develop
for resale to pay close attention to the flags and error codes...

James B. VanBokkelen		26 Princess St., Wakefield, MA  01880
FTP Software Inc.		voice: (617) 246-0900  fax: (617) 246-0901

sklower@ernie.Berkeley.EDU (Keith Sklower) (09/01/90)

In article <1990Aug28.162400.17811@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>The way to do i/o on Dennis's streams was with "read" and "write".
>Network i/o, in general, looked *exactly* like local device i/o.  This
>is the way it should be, unlike what both Berkeley and AT&T have done
>(both have reluctantly conceded that most people want to use "read"
>and "write" and have made that work, but their hearts were clearly
>elsewhere).

I find this inaccurate, partronizing and tiresome.  I have worked around
Berkeley since 1978 and although was not a member of the actual unix group
in 1982 while TCP was being incorporated, attended their meetings and
seminars.

I assure you that it was the design goal then, that only
``sophisticated process'' would need a more elaborate mechanism to establish
a network connection, but that once having established one, that it should
be usable as a completely ordinary file descriptor by ``naive'' processes
like the date command, using read and write, and that the file descriptor
should be inherited by the normal unix means (fork & exec).

It sounds to me like Henry is attempting to rewrite history (for his
own possibly political motives).

pcg@cs.aber.ac.uk (Piercarlo Grandi) (09/03/90)

On 31 Aug 90 19:48:37 GMT, sklower@ernie.Berkeley.EDU (Keith Sklower) said:

sklower> In article <1990Aug28.162400.17811@zoo.toronto.edu>
sklower> henry@zoo.toronto.edu (Henry Spencer) writes:

spencer> The way to do i/o on Dennis's streams was with "read" and "write".
spencer> Network i/o, in general, looked *exactly* like local device i/o.

Note that with the most recent developments of UNIX (FSS) read() and
write() no longer mean much, except 'move data from/to the kernel'; they
have become just a way to add new system calls, using a file like style
of interface. The semantics of write() and read() have become very very
loose. What does read/write mean on /dev/proc? Something highly non
obvious.

spencer> This is the way it should be, unlike what both Berkeley and
spencer> AT&T have done (both have reluctantly conceded that most people
spencer> want to use "read" and "write" and have made that work, but
spencer> their hearts were clearly elsewhere).

And for good reason! read() and write() on file descriptors is not the
most amusing interface. It is much more convenient to have IPC style
access to files, e.g. Accent with its ports, than viceversa, unless we
are so wedded to UNIX that we cannot change (probably true, vide failure
of Accent to get accepted, and then success of Mach, which is a Unix
like imitation of it).

Frankly, the "everything is a file" way of achieving connectability is
not the best abstraction, because you want to connect to active
services, not just to passive storage. Straining a file model to include
"active" files is a bit inelegant.

"everything is a socket" or equivalently "everything is a process" (even
files!) are much better; "everything is a file system", the technology
used in recent research Unixes or PLAN/9 is a way to get as abstraction
a more active thing than a file, works better than "everything is a
file", yes, but requires a large suspension of disbelief, and assumes
that we really want the Unix directory tree model for all name service,
which may not be a terribly good choice either.

	e.g. as in having an internet filesystem type, and doing
	something like

		fd = open("/dev/internet/lcs.mit.edu/echo.udp",...);
	/*or*/	fd = open("/dev/internet/edu/mit/lcs/119.tcp",...);

sklower> I find this inaccurate, partronizing and tiresome.  I have
sklower> worked around Berkeley since 1978 and although was not a member
sklower> of the actual unix group in 1982 while TCP was being
sklower> incorporated, attended their meetings and seminars.

Ahh. Incidentally, I would like to observe that I have *never* seen an
implementation of BSD sockets and TCP. The only ones that circulate are
extremely limited and rigid subsets. Since you have been around UCB for
so long, can you tell us why there is no (full) implementation of BSD
sockets? E.G. where are user domain type filedescriptors?

About compatibility between IPC and normal file I/O:

sklower> I assure you that it was the design goal then, that only
sklower> ``sophisticated process'' would need a more elaborate mechanism
sklower> to establish a network connection, but that once having
sklower> established one, that it should be usable as a completely
sklower> ordinary file descriptor by ``naive'' processes like the date
sklower> command, using read and write, and that the file descriptor
sklower> should be inherited by the normal unix means (fork & exec).

Uhm. Precisely. And 'wrappers', one of the features of BSD sockets, were
designed to make such compatibility absolute. Why nobody has ever
implemented them?

sklower> It sounds to me like Henry is attempting to rewrite history
sklower> (for his own possibly political motives).

I actually think that he may innocent, but he *is* attempting to rewrite
history, in a way different from what you say; on BSD it actually
happens that read/write on sockets is the same as on files, but because
they changed the specification of read/write on files.

The only difference with send/rcv is that sendmsg/rcvmsg allow
out-of-band, and passing file descriptors, which make no sense with
read/write to a file, but the distance from traditional read/write is
larget than that.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

guy@auspex.auspex.com (Guy Harris) (09/03/90)

(Not particularly TCP/IP oriented at this point....)

 >>> ps. Is there anyway to get a list of all the modules pushed onto
 >>> a stream.  I_LOOK only lists the top most module.
 >>
 >>Not in V.3.
 >
 >Actually it is not quite that bad.  You should have an idea of all the
 >possible modules that COULD be on a given stream (not many).  It is then 
 >trivial to write a little program which uses I_FIND to see if the module
 >is on the stream.

That'll tell you *which* modules are on the stream, but it won't tell
you where they are on the stream.  If you want that, you need something
such as S5R4's I_LIST.

lars@spectrum.CMC.COM (Lars Poulsen) (09/05/90)

In article <1990Aug28.162400.17811@zoo.toronto.edu>
   henry@zoo.toronto.edu (Henry Spencer) writes:
>spencer> The way to do i/o on Dennis's streams was with "read" and "write".
>spencer> Network i/o, in general, looked *exactly* like local device i/o.
>
>spencer> This is the way it should be, unlike what both Berkeley and
>spencer> AT&T have done (both have reluctantly conceded that most people
>spencer> want to use "read" and "write" and have made that work, but
>spencer> their hearts were clearly elsewhere).

In article <PCG.90Sep2194524@athene.cs.aber.ac.uk>
   pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>And for good reason! read() and write() on file descriptors is not the
>most amusing interface. It is much more convenient to have ... [features]

The problem is larger than that. As commercial programmers have
often said, unix read() and write() is not a very good interface for
files, either. It is a good interface for SIMPLE TEXT files, and very
little else.

Having a guaranteed subset of compatible functionality has allowed
very productive use of the "building block" philosophy. Having a way to
specify a remote conenctions "as if" it were a filename would allow more
things to be prototyped with simple shell scripts and pipelines.

On the other hand, great flexibility leads to a loss of device
independence, witness VMS' proliferation of pseudo-device drivers as an
example of where the path of customized interfaces may take us.
-- 
/ Lars Poulsen, SMTS Software Engineer
  CMC Rockwell  lars@CMC.COM

henry@zoo.toronto.edu (Henry Spencer) (09/05/90)

In article <38584@ucbvax.BERKELEY.EDU> sklower@ernie.Berkeley.EDU.UUCP (Keith Sklower) writes:
>>...the way it should be, unlike what both Berkeley and AT&T have done
>>(both have reluctantly conceded that most people want to use "read"
>>and "write" and have made that work, but their hearts were clearly
>>elsewhere).
>
>I find this inaccurate, partronizing and tiresome.  I have worked around
>Berkeley since 1978 and although was not a member of the actual unix group
>in 1982 while TCP was being incorporated, attended their meetings and
>seminars.

I wasn't there; all I got to do was read the resulting documents.  Some
of which come over with a very strong air of "well, if you want to do it
right, you will of course use our 57 new system calls, but we grudgingly
admit that read/write will work if you insist on being backward".
-- 
TCP/IP: handling tomorrow's loads today| Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads someday|  henry@zoo.toronto.edu   utzoo!henry

henry@zoo.toronto.edu (Henry Spencer) (09/05/90)

In article <9008311500.AA12356@ftp.com> jbvb@ftp.com writes:
>    ...That's all most customers want.
>
>I see where our viewpoints differ: I am selling applications to end-users,
>and I intend to support them.  Most of the end-users who use our Development
>Kit for one-off or in-house applications are probably quite satisfied with
>open/read/write/close.  However, I am careful to advise any OEMs who develop
>for resale to pay close attention to the flags and error codes...

I was writing from the down-in-the-wires viewpoint, where *any* user process
is a customer.  And on the whole, I remain unrepentant. :-)  It should be
possible to use open/read/write/close, with (say) perror when something
goes wrong, without major problems, assuming that error-message-and-die
is a reasonable thing to do on failure.  A requirement for fault tolerance
does require closer attention to error handling, as in non-networked code.
Also as in non-networked code, a requirement for carefully optimized use
of system resources requires attention to flags and details.  And anyone
building production code should be aware of the grubby details, so that
he can recognize the one case in a hundred where some of them are relevant.

However, I continue to believe that what most applications want to see is
a reliable bidirectional pipe, perhaps set up in slightly odd ways but
thereafter used via read/write/close, with invisible recovery from transient
problems and a suitable errno value on hard failure.

The resemblance to a Unix file is not accidental. :-)
-- 
TCP/IP: handling tomorrow's loads today| Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads someday|  henry@zoo.toronto.edu   utzoo!henry

bzs@WORLD.STD.COM (Barry Shein) (09/06/90)

I think we're all looking at this in far too narrow a context. There
are many issues that are being hand-waved (note, anyone who assumes I
believe one way or the other on this issue please send me my decision
as I haven't received it yet.)

For example, when I address something that reaches out into, say,
network name space, where does my request go? Does it go to a
specialized driver that takes care of things like DNS resolution?
Does it get handed back up to a user-level process? What?

I still think the terminal example (open("/dev/tty/9600/even/noecho",...)
was a good one.

Someone sent me mail saying "oh, that's just ioctl()'s".

What magic! Sockets are bad, ioctls are good, such power in a name.

The real issue is:

	A) Are we trying to simplify/generalize the *user*
	interface so most any network/etc operation can be
	specified in a string, wherever a file name is expected?

or

	B) Are we trying to simplify/generalize the *programmer*
	interface so they don't have to know about those nasty
	socket calls?

Careful, I believe those two goals can be very much in conflict. I
also don't believe that everyone discussing this issue on this list
falls into the same camp. So we're having definite communications
problems, state your objectives!

        -Barry Shein

Software Tool & Die    | {xylogics,uunet}!world!bzs | bzs@world.std.com
Purveyors to the Trade | Voice: 617-739-0202        | Login: 617-739-WRLD

pcg@cs.aber.ac.uk (Piercarlo Grandi) (09/13/90)

On 4 Sep 90 17:29:23 GMT, lars@spectrum.CMC.COM (Lars Poulsen) said:

lars> In article <1990Aug28.162400.17811@zoo.toronto.edu>
lars> henry@zoo.toronto.edu (Henry Spencer) writes:

spencer> The way to do i/o on Dennis's streams was with "read" and
spencer> "write".  Network i/o, in general, looked *exactly* like local
spencer> device i/o.

If you want to keep UNIX like semantics. Unfortunately you really want
to do typed data, if only to include passing file descriptors, and OOB
signaling, and these do not fit well with traditional UNIX style
interfaces.

spencer> This is the way it should be, unlike what both Berkeley and
spencer> AT&T have done (both have reluctantly conceded that most people
spencer> want to use "read" and "write" and have made that work, but
spencer> their hearts were clearly elsewhere).

lars> In article <PCG.90Sep2194524@athene.cs.aber.ac.uk>
lars> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:

pcg> And for good reason! read() and write() on file descriptors is not
pcg> the most amusing interface. It is much more convenient to have ...
pcg> [features]

Hey, I take exception to the [features] summary -- I was describing not a
set of extra features, but a completely different philosophy based on
communication with active entities represented by IPC ports. An
alternative to the UNIX style file descriptors to access passive files.
It can be as simple and terse as the current UNIX style.

lars> The problem is larger than that. As commercial programmers have
lars> often said, unix read() and write() is not a very good interface
lars> for files, either. It is a good interface for SIMPLE TEXT files,
lars> and very little else.

Uhm. Here we differ. The UNIX style of accessing file is excellent for
any type of file, because any type of file can be mapped onto untyped
byte arrays (a.k.a. virtual memory segments), by the use of suitable
library procedures. Unfortunately non storage like entities are not
easily mapped onto untyped byte arrays.

lars> Having a guaranteed subset of compatible functionality has allowed
lars> very productive use of the "building block" philosophy. Having a way to
lars> specify a remote conenctions "as if" it were a filename would allow more
lars> things to be prototyped with simple shell scripts and pipelines.

The problem is that modeling everything as a passive entitity is far less
flexible than modling everything as an active entity.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk