+struct sockaddr_ns sns, to;
+int s, on = 1;
+struct databuf {
+ struct sphdr proto_spp; /* prototype header */
+ char buf[534]; /* max. possible data by Xerox std. */
+} buf;
+ ...
+s = socket(AF_NS, SOCK_SEQPACKET, 0);
+ ...
+bind(s, (struct sockaddr *) &sns, sizeof (sns));
+setsockopt(s, NSPROTO_SPP, SO_HEADERS_ON_OUTPUT, &on, sizeof(on));
+ ...
+buf.proto_spp.sp_dt = 1; /* bulk data */
+buf.proto_spp.sp_cc = SP_EM; /* end-of-message */
+strcpy(buf.buf, "hello world\en");
+sendto(s, (char *) &buf, sizeof(struct sphdr) + strlen("hello world\en"),
+ (struct sockaddr *) &to, sizeof(to));
+ ...
+.DE
+Note that one must be careful when writing headers; if the prototype
+header is not written with the data with which it is to be associated,
+the kernel will treat the first few bytes of the data as the
+header, with unpredictable results.
+To turn off the above association, and to indicate that packet
+headers received by the system should be passed up to the user,
+one might use:
+.DS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netns/ns.h>
+#include <netns/sp.h>
+ ...
+struct sockaddr sns;
+int s, on = 1, off = 0;
+ ...
+s = socket(AF_NS, SOCK_SEQPACKET, 0);
+ ...
+bind(s, (struct sockaddr *) &sns, sizeof (sns));
+setsockopt(s, NSPROTO_SPP, SO_HEADERS_ON_OUTPUT, &off, sizeof(off));
+setsockopt(s, NSPROTO_SPP, SO_HEADERS_ON_INPUT, &on, sizeof(on));
+ ...
+.DE
+To indicate a default prototype header to be associated with
+the outgoing packets on an IDP datagram socket, one would use:
+.DS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netns/ns.h>
+#include <netns/idp.h>
+ ...
+struct sockaddr sns;
+struct idp proto_idp; /* prototype header */
+int s, on = 1;
+ ...
+s = socket(AF_NS, SOCK_DGRAM, 0);
+ ...
+bind(s, (struct sockaddr *) &sns, sizeof (sns));
+proto_idp.idp_pt = NSPROTO_PE; /* packet exchange */
+setsockopt(s, NSPROTO_IDP, SO_DEFAULT_HEADERS, (char *) &proto_idp,
+ sizeof(proto_idp));
+ ...
+.DE
+.NH 2
+Three-way Handshake
+.PP
+The semantics of SPP connections indicates that a three-way
+handshake, involving changes in the datastream type, should \(em
+but is not absolutely required to \(em take place before a SPP
+connection is closed. Almost all SPP connections are
+``well-behaved'' in this manner; when communicating with
+any process, it is best to assume that the three-way handshake
+is required unless it is known for certain that it is not
+required. In a three-way close, the closing process
+indicates that it wishes to close the connection by sending
+a zero-length packet with end-of-message set and with
+datastream type 254. The other side of the connection
+indicates that it is OK to close by sending a zero-length
+packet with end-of-message set and datastream type 255. Finally,
+the closing process replies with a zero-length packet with
+substream type 255; at this point, the connection is considered
+closed. The following code fragments are simplified examples
+of how one might handle this three-way handshake at the user
+level; in the future, support for this type of close will
+probably be provided as part of the C library or as part of
+the kernel. The first code fragment below illustrates how a process
+might handle three-way handshake if it sees that the process it
+is communicating with wants to close the connection:
+.DS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netns/ns.h>
+#include <netns/sp.h>
+ ...
+#ifndef SPPSST_END
+#define SPPSST_END 254
+#define SPPSST_ENDREPLY 255
+#endif
+struct sphdr proto_sp;
+int s;
+ ...
+read(s, buf, BUFSIZE);
+if (((struct sphdr *)buf)->sp_dt == SPPSST_END) {
+ /*
+ * SPPSST_END indicates that the other side wants to
+ * close.
+ */
+ proto_sp.sp_dt = SPPSST_ENDREPLY;
+ proto_sp.sp_cc = SP_EM;
+ setsockopt(s, NSPROTO_SPP, SO_DEFAULT_HEADERS, (char *)&proto_sp,
+ sizeof(proto_sp));
+ write(s, buf, 0);
+ /*
+ * Write a zero-length packet with datastream type = SPPSST_ENDREPLY
+ * to indicate that the close is OK with us. The packet that we
+ * don't see (because we don't look for it) is another packet
+ * from the other side of the connection, with SPPSST_ENDREPLY
+ * on it it, too. Once that packet is sent, the connection is
+ * considered closed; note that we really ought to retransmit
+ * the close for some time if we do not get a reply.
+ */
+ close(s);
+}
+ ...
+.DE
+To indicate to another process that we would like to close the
+connection, the following code would suffice:
+.DS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netns/ns.h>
+#include <netns/sp.h>
+ ...
+#ifndef SPPSST_END
+#define SPPSST_END 254
+#define SPPSST_ENDREPLY 255
+#endif
+struct sphdr proto_sp;
+int s;
+ ...
+proto_sp.sp_dt = SPPSST_END;
+proto_sp.sp_cc = SP_EM;
+setsockopt(s, NSPROTO_SPP, SO_DEFAULT_HEADERS, (char *)&proto_sp,
+ sizeof(proto_sp));
+write(s, buf, 0); /* send the end request */
+proto_sp.sp_dt = SPPSST_ENDREPLY;
+setsockopt(s, NSPROTO_SPP, SO_DEFAULT_HEADERS, (char *)&proto_sp,
+ sizeof(proto_sp));
+/*
+ * We assume (perhaps unwisely)
+ * that the other side will send the
+ * ENDREPLY, so we'll just send our final ENDREPLY
+ * as if we'd seen theirs already.
+ */
+write(s, buf, 0);
+close(s);
+ ...
+.DE
+.NH 2
+Packet Exchange
+.PP
+The Xerox standard protocols include a protocol that is both
+reliable and datagram-oriented. This protocol is known as
+Packet Exchange (PEX or PE) and, like SPP, is layered on top
+of IDP. PEX is important for a number of things: Courier
+remote procedure calls may be expedited through the use
+of PEX, and many Xerox servers are located by doing a PEX
+``BroadcastForServers'' operation. Although there is no
+implementation of PEX in the kernel,
+it may be simulated at the user level with some clever coding
+and the use of one peculiar \fIgetsockopt\fP. A PEX packet
+looks like:
+.DS
+.if t .ta \w'struct 'u +\w" struct idp"u +2.0i
+/*
+ * The packet-exchange header shown here is not defined
+ * as part of any of the system include files.
+ */
+struct pex {
+ struct idp p_idp; /* idp header */
+ u_short ph_id[2]; /* unique transaction ID for pex */
+ u_short ph_client; /* client type field for pex */
+};
+.DE
+The \fIph_id\fP field is used to hold a ``unique id'' that
+is used in duplicate suppression; the \fIph_client\fP
+field indicates the PEX client type (similar to the packet
+type field in the IDP header). PEX reliability stems from the
+fact that it is an idempotent (``I send a packet to you, you
+send a packet to me'') protocol. Processes on each side of
+the connection may use the unique id to determine if they have
+seen a given packet before (the unique id field differs on each
+packet sent) so that duplicates may be detected, and to indicate
+which message a given packet is in response to. If a packet with
+a given unique id is sent and no response is received in a given
+amount of time, the packet is retransmitted until it is decided
+that no response will ever be received. To simulate PEX, one
+must be able to generate unique ids -- something that is hard to
+do at the user level with any real guarantee that the id is really
+unique. Therefore, a means (via \fIgetsockopt\fP) has been provided
+for getting unique ids from the kernel. The following code fragment
+indicates how to get a unique id:
+.DS
+long uniqueid;
+int s, idsize = sizeof(uniqueid);
+ ...
+s = socket(AF_NS, SOCK_DGRAM, 0);
+ ...
+/* get id from the kernel -- only on IDP sockets */
+getsockopt(s, NSPROTO_PE, SO_SEQNO, (char *)&uniqueid, &idsize);
+ ...
+.DE
+The retransmission and duplicate suppression code required to
+simulate PEX fully is left as an exercise for the reader.
+.NH 2
+Non-Blocking Sockets
+.PP
+It is occasionally convenient to make use of sockets
+which do not block; that is, i/o requests which
+would take time and
+would cause the process to wait for their completion are
+not executed, and an error code is returned.
+Once a socket has been created via
+the \fIsocket\fP call, it may be marked as non-blocking
+by \fIfcntl\fP as follows:
+.DS
+#include <fcntl.h>
+ ...
+int s;
+ ...
+s = socket(AF_INET, SOCK_STREAM, 0);
+ ...
+if (fcntl(s, F_SETFL, FNDELAY) < 0)
+ perror("fcntl F_SETFL, FNDELAY");
+ exit(1);