This chapter introduces TCP/IP network programming. It describes TCP/IP programming generally and outlines areas where TCPware provides added functions or has specific requirements. This chapter includes sample client and server programs and describes the following:
· TCP/IP programming concepts
· Data representation and exchange
· Programming services options
· Network programming with sockets
· Multicasting
· Sample application programs
For details on TCP/IP network programming, see the list of reference texts in the User's Guide and the following books:
· Comer, Douglas E. & David L. Stevens [1993], Internetworking with TCP/IP, Volume III: Client-Server Programming and Applications (BSD Socket Version), Prentice-Hall
· Stevens, W. Richard [1990], UNIX Network Programming, Prentice-Hall
This chapter is designed for an audience of experienced programmers who need information specific to TCPware programming.
TCP/IP programming requires determining the following:
· Whether to use a connection-oriented (TCP) or connectionless (UDP) networking service
· Creating sockets
· Naming the communication endpoints (internet addresses and port numbers)
Connection-oriented services and protocols support applications that send multiple messages between peer applications. These services and protocols require that applications establish a logical connection (virtual circuit) between them before they can exchange data. They provide data transfer that is reliable, ordered, full duplex, and flow controlled. They:
· Verify receipt of data.
· Compute checksums.
· Provide sequence numbers to ensure correct segment order.
· Retransmit lost segments.
· Inform users of dropped network connections. TCP is such a connection-oriented protocol, designed for applications that need reliable data delivery between similar and dissimilar systems. Examples of applications that use TCP are:
o FTP
o TELNET
o SMTP
Since these services and protocols are more complex than connectionless services, they have higher overhead. In a connection-oriented service:
· The source and destination uniquely identify each connection.
· You can multiplex connections across the network to paired host processes.
· Data streams break into portions encapsulated with control information, such as addresses. Encapsulated pieces pass through the network to the peer host.
With TCP, the number of blocks and size (in number of bytes) of a send operation need not equal that of a receive operation. The protocol may bundle several sends into one receive: a receive operation can receive more or fewer bytes than in a single send operation.
For example, if you do five send operations, you may not need five receive operations to get all the data. If a client application sends five 10-byte blocks of data (five send operations) over the network and the server program initiates a 100-byte read operation, the program can receive all 50 bytes at one time.
TCP (unlike DECnet) does not support the concept of messages. TCP is a byte-stream protocol that does not distinguish record or read/write boundaries. The individual applications have to perform messaging as needed. The client and server have to agree on a message protocol and implement it. Here are three possible strategies:
Messages of fixed length |
Data acquisition applications are examples of this type of strategy. When you use this strategy, make sure the application reads a complete message-length of data from the network. After the read, the application must go back and process any residual data left over from the last complete message. For example, if you issue a read for 100 bytes and do not get 100 bytes, you need to update the pointers and the length, and go back until you read the full 100 bytes. |
Messages preceded by byte count length |
Other protocols prefix each message with a byte count. This could be a 16- or 32-bit quantity. RPC is an example of an application protocol that uses this strategy. If you use this technique, use the conversion routines for native- and network-byte order described in the Native Byte Order and Network Byte Order. This guarantees that the client and server agree on the byte order sequences for that number. |
Messages separated by a <CR><LF> sequence |
FTP is an application that uses this type of messaging strategy. A program receiving data searches for <CR><LF> characters and processes the data preceding these characters as one record. If there is more data in the buffer, the program looks for the next <CR><LF> sequence and processes the data preceding it as a separate message. |
Connectionless services present data with a destination address, and the network delivers it on a best-effort basis. This is independent of other data exchanged between the same pair of hosts. Connectionless services are unreliable but have lower overhead than connection-oriented services. In a connectionless service:
· The client or server must perform data tracking and adaptive retransmission strategies.
· Applications cannot depend on the underlying transport for reliable delivery.
· Each message or portion of a message contains all delivery data.
· Operation is best in LANs since WANs can introduce more errors.
UDP is a full-duplex, connectionless datagram delivery protocol with low overhead. UDP is an excellent choice for applications that need the highest performance and can tolerate this level of service. Examples of applications that use UDP are:
· NFS
· DNS
· SNMP
UDP can lose, delay, or duplicate requests, or deliver them out of order. The application requesting the service must detect and correct transmission errors. For example, an application may retransmit a request if it does not receive a reply. Applications typically do this by enabling a short timeout period and retransmitting the request if the application does not get a response within the timeout period.
Under UDP, a server could receive multiple requests since the reply might simply be lost. This case can cause problems if some operations are not repeatable. Use UDP on LANs where transmission errors are less frequent and round-trip times more predictable. UDP is also well-suited for broadcast and multicast applications, and applications that cannot tolerate the overhead of virtual circuits.
NFS often uses UDP. Here is an example of what could happen during an NFS file deletion request:
1. The client issues a delete request.
2. The server receives and processes the request.
3. The server deletes the file and returns a reply that it deleted the file.
4. The reply gets lost.
5. Not having received a confirmation, the client reissues the original delete request.
6. The server gets the request and replies that the file no longer exists.
7. The server's reply confuses the client.
Note that you might need to add application design features if some operations are not repeatable. Remote Procedure Calls (RPCs) that NFS uses handle this situation by keeping a cache of recent requests and replies. In this case, if NFS receives a duplicate request, it sends the cached reply that it successfully deleted the file.
For DNS and SNMP, these issues are not as severe. For example, DNS resolves host name and internet addresses. It does not matter how many times you reissue a request; the response is the same and you get the information you need.
Most TCP/IP programming uses a Berkeley System Distribution (BSD) UNIX abstraction called sockets. Network programs use sockets to exchange data across the network. Socket programming is synchronous: each socket deals with only one connection at a time.
Socket programming requires a socket descriptor that is analogous to the file descriptor used in file programming. In socket programming (as in file programming), you create the socket descriptor, use it to read or write, then destroy the descriptor by closing the connection. The operation of creating a socket descriptor involves naming the communication endpoints.
Naming communication endpoints involves assigning:
Internet addresses |
Internet addresses uniquely identify the source and destination host interfaces. Any exchange of information involves two addresses, one for the source and one for the destination. |
Port numbers |
Port numbers uniquely identify a source and destination port. Like DECnet object numbers, port numbers identify the particular application or service used. The TCP protocol specifies sockets that have a protocol port number and an IP address. This protocol uses the AF_INET address family type.
TCP/IP programming uses well-known port numbers to contact a known service. For example, to do a file transfer you need to open a connection to port 21. Port 21 is the FTP server port number. Other services have specific port numbers as defined by the current Assigned Numbers RFC. If you write an application, you need to assign a port number. Any client that wants to use the service you create connects to that port number. |
The Assigned Numbers RFC also designates a clearinghouse for assigning port numbers. Get a unique port number from this center when you develop a network service. However, if you are developing a private application, you can create your own port number so long as another service does not use it.
Sockets define endpoint addresses in data structures coded in C. Most application programs use predefined address structures, such as sockaddr_in, as defined in the BSD Socket Data Structures.
This section discusses concepts about how data is formatted and exchanged between systems: data encoding schemes and native as opposed to network byte order.
Different hardware and operating system platforms represent data with different encoding schemes. For example, representing floating point and integer values differ on various hardware platforms.
Your programs must decide on a compatible encoding scheme and perform any required conversion between network format and local hardware representation format. Another approach is to exchange data in ASCII instead of binary representation.
The sequence for storing binary data on a given machine is native byte order. The sequence for transmitting binary data over the network is network byte order.
Some machines are little-endian; others are big-endian. Little-endian machines (VAX systems, for example) store binary data with the least significant byte first.
Big-endian machines (Sun systems, for example) store and transmit binary data with the most significant byte first. Network byte order is always in the big-endian format. Socket libraries require network addresses in network byte order.
The below diagram shows the format for storing the decimal value 512 as a 16-bit (2 byte) quantity on little-endian and big-endian systems.
When communicating between a client and server, both need to agree to the byte sequence for transmitting data. Depending on what type of system you are on, the host's native byte order may not be the same as network byte order. For example, the byte order for VAX systems is little-endian, the opposite of network byte order.
Several routines convert between network and native byte order. Use these routines to make sure that the data is in the right format. Always use these conversion routines, even if the local host byte order is the same as network byte order. Doing so guarantees that the order of information is correct, and you can port the source code.
The function htons converts a short integer (16 bits) from host native to network byte order. The function ntohs converts a short integer from network to host native byte order. The htonl and ntohl functions convert long integers (32 bits) between the two.
For example, if you use a messaging strategy that uses a byte count length (as described in the "Messages preceded by byte count length" bulleted item), use these routines to encode the byte count length field. This way the order of information is correct, whether you are communicating between a VAX and Sun system or between two VAX systems.
This section describes the interface and services options you can use when writing TCP/IP networking applications. It includes information on the following:
· Device drivers
· VAX C and DEC C socket libraries and UCX Compatibility
· TCPware Socket Library routines
· System Queue Input/Output (QIO) interface calls
· FTP Library routines
· TELNET Library routines
· ONC RPC Services
Use the DEC C or VAX C socket libraries with C-based applications. Note that any high-level language that passes C-like arguments can call these routines.
Programming with sockets (rather than using the QIO interface discussed in the System Queue Input/Output (QIO) Calls subsection) makes network programs more portable across UNIX environments and more compatible with other socket-based TCP/IP applications. Sockets are easy to program since they look like standard subroutine calls and hide some of the complexity of the QIO interface system calls.
However, writing event-driven programs is much easier with the QIO interface than with sockets. This is true since sockets do not fit the standard OpenVMS event-driven IO model. The QIO interface provides access to the full range of TCPware functions; the socket library provides access to a subset of the available TCPware functions.
TCPware uses the standard OpenVMS network interface device drivers that operate the hardware controller. These device drivers include DEC's Ethernet device drivers and third-party device drivers (PNDRIVER for Proteon's proNET controller; and NADRIVER, NBDRIVER, and NCDRIVER for the HYPERchannel driver).
Because TCPware uses these standard OpenVMS device drivers, other applications using these drivers (such as DECnet, but not other TCP/IP implementations) can continue to run at the same time and use the same hardware.
The VAX C and DEC C socket routines are the preferred methods of network programming in that they offer the greatest flexibility. These socket routines are available because of the TCPware UCX Compatibility Services.
See VAX C Run Time Library Manual or DEC C Language Reference Manual for information on these socket library routines.
The TCPware UCX Compatibility Services provides the QIO interface that the TCP/IP Services for OpenVMS BGDRIVER support. The UCX Compatibility Services provide support for:
· Any application the UCX BGDRIVER supports.
· VMS 5.5 (or later) VAX C Run-Time Library (VAXCRTL) Socket Routines.
· OpenVMS VAX, OpenVMS Alpha and OpenVMS I64 DEC C Run-Time Library (DEC/CRTL) Socket Routines.
If you developed an application for UCX, there is no need to modify it to make it work with TCPware. You can take your image (compiled and linked against UCX) and run it as is.
See the TCP/IP Services for VMS Programming Manual for programming information. Then see Chapter 2, UCX Compatibility Services, for programming information specific to TCPware's UCX compatibility.
Programmers can create applications that use the system Queue Input/Output calls (QIOs) for an interface. These calls use standard OpenVMS system services and support any high-level programming language. QIOs support all OpenVMS asynchronous features such as Asynchronous System Traps (ASTs) and event flags.
BGDRIVER, TCPDRIVER, UDPDRIVER, IPDRIVER, and INETDRIVER are TCPware's proprietary QIO programming interfaces, as discussed in the following subsections.
TCPware provides BGDRIVER . The BGDRIVER is the preferred programming interface.
See Chapter 2, UCX Compatibility Services.
These drivers are proprietary to Process Software and are described as follows:
IPDRIVER implements IP and ICMP |
It uses the network device drivers to send and receive datagrams. The Address Resolution Protocol (ARP) and Reverse ARP (RARP), which map internet addresses and physical addresses, are also implemented within this driver. Functions are provided to open and close a port and to transmit and receive datagrams. (See the IPDRIVER Services chapter for programming information.)
IPDRIVER uses ports to demultiplex received datagrams. When an IP datagram is received, IPDRIVER validates the header and searches for a port opened on the protocol number in the datagram's internet header. IPDRIVER discards the datagram if no port is open for that protocol or if that port has no outstanding receive. |
TCPDRIVER implements TCP |
It uses the IP device driver to send and receive TCP segments. Functions are provided to open and close, receive and send data over, and perform special control functions on a connection. (See the TCPDRIVER Services chapter for programming information.) |
UDPDRIVER implements UDP |
It uses the IP device driver to send and receive UDP datagrams. Functions are provided to open and close a receive port, and to receive and send data. (See the UDPDRIVER Services chapter for programming information.) |
TCPware provides INETDRIVER, the Stanford Research Institute (SRI) QIO interface. Some vendors of TCP/IP products (such as Process Software's MultiNet) use the SRI QIO interface as a direct socket type interface. The INET device driver maps UNIX socket calls to OpenVMS QIO requests. It supports stream (TCP) and datagram (UDP) services.
INETDRIVER Services provide an asynchronous I/O implementation of the UNIX socket calls within the OpenVMS $QIO and $QIOW system services. These system services allow Asynchronous System Trap (AST) routines and event flags to be associated with I/O requests. This allows for efficient socket operations.
The INETDRIVER interfaces directly with the TCP and UDP protocols in the transport layers. It does not replace the TCPDRIVER or UDPDRIVER services but provides another way to communicate with them.
See Chapter 6, INETDRIVER Services, for programming information.
The FTP library routines provide a programming interface to the FTP protocol. Network programmers use the FTP library routines in applications to provide FTP capabilities.
See Chapter 7, FTP Library, for programming information.
The TELNET library routines provide a programming interface to the TELNET protocol. Network programmers use the TELNET library routines in applications to provide TELNET capabilities.
See Chapter 9, TELNET Library, for programming information.
TCPware provides Open Network Computing (ONC) Remote Procedure Call (RPC) Services. ONC RPC Services are a set of software programming tools with which you can develop distributed applications. These tools implement the RPC and XDR (External Data Representation) protocols.
A distributed application executes different parts of its programs on different hosts in a network. Computers on the network share the processing workload, with each computer performing the tasks for which it is best equipped. For example, a distributed database application might consist of a central database running on a server and numerous client workstations. The workstations send requests to the server. The server carries out the requests and sends the results back to the workstations. The workstations use the results in other modules of the application.
Remote procedure calls (RPCs) allow programs to invoke procedures on remote hosts as if the procedures were local. ONC RPC Services hide the networking details from the application. This facilitates distributed processing because it relieves the application programmer from performing low-level network tasks such as establishing connections, addressing sockets, and converting data from one machine's format to another.
The XDR protocol provides a means for the local and remote host to agree on a way of representing data. XDR is a standard that resolves differences of data representation between different operating systems and hardware architectures.
ONC RPC Services consist of the following components:
· Shareable run-time libraries (RTLs)
· RPCGEN compiler
· Port Mapper
· RPCINFO command
The Port Mapper maps well-known RPC program numbers to UDP/TCP port numbers. The Port Mapper helps ONC RPC client programs connect to ports the ONC RPC server uses. A Port Mapper runs on each host that implements ONC RPC Services. The Port Mapper is part of TCPware's Network Control Process (NETCP).
Note: You must use the TCPware Socket Library if you are using ONC RPC Services.
|
This section provides a quick overview of socket programming. It includes information on the following:
· Using socket calls in network programming
· Socket system calls
· Messaging over stream (TCP) connections
· BSD socket data structures
The below diagram is an example of calls made by a client and server that use TCP to communicate. In this example, the server starts and waits for new connections on a well-known port. It accepts each new connection, processes the client's requests, and closes the connection.
In the example, the client creates a socket and uses connect to connect to the server. The client then uses write to send requests to the server and read to receive replies from the server. The client calls the close routine when it is finished using the connection.
The server uses a socket call to create a socket, then uses bind to specify the local well-known port for the application. Next, the server calls listen (which prepares the socket for incoming connections) and enters a loop. In the loop, the server calls the accept routine and waits for the next connection request to arrive. The server uses read and write to interact with and close to end communication with the client. The server then returns to accept and waits for the next connection request.
Servers typically use passive sockets to wait for incoming network connections. Applications (typically clients) use active sockets to initiate a connection.
When creating a socket, you create either a stream socket or a datagram socket. Stream sockets (created by specifying SOCK_STREAM) correspond to the TCP protocol. Datagram sockets (created by specifying SOCK_DGRAM) correspond to the UDP protocol.
SOCK_RAW corresponds to the IP layer directly. Note that SOCK_RAW is not described in this chapter because programming is not usually done at this level. Using TCP or UDP is far superior.
The IP layer only allows you to have 256 connections or users at the same time. TCP and UDP allow you to have about 232 possible connections between any two hosts. A connection is uniquely identified by the source internet address and port number, and the destination internet address and port number.
Many operating systems provide the BSD socket call interface, including OpenVMS. Most non-socket implementations have comparable networking services. In OpenVMS, most implementations include a BSD socket library interface as well as a QIO interface. The QIO interface can be almost a one-to-one correspondence to the socket calls, and usually provides a greater range of functions than available through the socket library. While the QIO interface can be a bit different from the BSD interface, it still provides the same level of services. The below table describes typically-used socket calls.
Call |
Description |
accept |
Accepts an incoming connection. accept actually blocks and waits for the connection to come in. Typically, in the case of a server, it sits in block mode until someone actually does a connect. Valid only for stream TCP/IP sockets. |
bind |
Used primarily by servers to name their local endpoint of the connection. Servers are contacted at well-known port numbers: they "bind" to that port number. In a bind operation, you specify the internet address as well as the port number.
Most servers specify the internet address as zero (or INADDR_ANY). The servers do not want to bind to any particular internet address, just to a particular port. If a host has multiple internet addresses, it does not matter where the request comes from so long as it goes to the specified port. |
close |
Closes communication over a socket. close is similar to closing a file. close makes sure that the data has been sent over to the peer and then does the actual close operation.
For the Socket Library, close is called socket_close. |
connect |
Names the remote endpoint and, in the case of TCP, establishes a connection. When you specify connect, you also specify an internet address and port number. This connects you actively to the remote peer, provided someone there is willing to accept the connection. For example, when an FTP client issues an open, it does a connect operation on the specified internet address and port 21. This establishes the connection to the remote server. |
getpeername |
Obtain the names of the communication endpoints. These calls return the internet address and port number of either the local end of the connection or the remote end of the connection.
The local end of the connection is the port number and internet address on which you have done a bind operation. The remote endpoint is the system to which you connected or that connected to you. |
getsockopt |
Obtain or set socket options. For example, you can set one option that determines if KEEPALIVE operations are done on TCP connections. |
listen |
Used by servers and prepares the socket for incoming connections. listen declares that the server is willing to accept connections on this socket. Any connection going to that bound port is eligible to be processed by that process. |
read |
read, recv, and recvfrom are different forms of the same request. Use these calls to read data. recvfrom is primarily used by UDP and returns the internet address and port number of the sender of the datagram. Typically, you use this information to send back a reply in a subsequent sendto.
For the Socket Library, read is called socket_read and recv is called socket_recv. |
select |
Performs asynchronous I/Os. The select call allows you to service many connections from within a particular process.
There is also a timeout parameter with the select call that limits the amount of time you can wait before it comes back to you. In this way, select allows you to wait indefinitely, wait until timeout, or poll on a specific socket or set of sockets. |
socket |
Comparable to opening or creating a file. Creates a socket to which you can send I/O requests. |
write |
write, send, and sendto are different forms of the same request. Use these calls to send data out over the network. sendto is used primarily by UDP and allows you to specify the destination internet address and the port number. When using UDP, the sendto request allows you to send requests to any number of hosts.
For the TCPware Socket Library, write is called socket_write and send is called socket_send. |
The BSD sockets support a group of data structures and subroutines for the socket interface that typically are used to communicate with the socket layer. These include the sockaddr_in structure, hostent structure, and servent structure. The following include files are used:
Include Files |
Description |
in.h |
defines the sockaddr_in structure. Almost all socket operations require use of this file. |
inet.h |
defines address conversion subroutines, such as inet_addr(), inet_ntoa(), and so on. |
netdb.h |
defines network database structures including the hostent and servent structures. |
socket.h |
defines the sockaddr structure, SOCK_STREAM, SOCK_DGRAM, AF_INET, and other symbols used when calling the Socket Library subroutines. All socket operations require use of this file. |
See Chapter 8, Socket Library, for details on the files and the following sections.
One of the more important socket data structures is the sockaddr_in structure. Use the sockaddr_in structure in calls to name a communication endpoint. The sockaddr_in structure communicates the internet address in the sin_addr field. It also communicates the port number in the sin_port field.
The in.h include file defines the sockaddr_in structure as shown in the below example. In this example:
1. sin_family is the address family (AF_INET for example)
2. sin_port is the port number (in network byte order)
3. sin_addr is the internet address (in network byte order)
4. sin_zero is the internet address where the remainder of the eight bytes are unused and should be set to zero
Specify both the port and address in network byte order. For example, when doing a bind operation or a connect operation, specify the sockaddr_in structure with the information filled in. When using getsockname or getpeername, you provide the address of the structure and the subroutine then would fill it in for you.
struct in_addr {
unsigned long addr;
};
struct sockaddr_in {
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
The gethostbyname and gethostbyaddr routines use the hostent structure for doing host by name or host by internet address lookups. For example, if you want to do an
FTP> open hostname
the underlying socket interface does not support use of a host name. In this case, use the gethostbyname routine to take that name and return the corresponding internet address (among other information).
The netdb.h include file defines the hostent structure as shown below.
struct hostent {
char *h_name; /* official host name */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* address length */
char **h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0]; /* first address */
};
The hostent structure has:
· A host name field that points to the ASCII host name.
· A pointer to a list of alias names if the host has alias names.
· A pointer to a list of internet addresses. In a more complex environment, a host might have multiple internet addresses.
· Other information, such as the address format and address family. For TCP/IP, the address is four bytes long and the address family is AF_INET (decimal value of 2).
The servent structure is used when looking up services by name or port. Although you can hard-code the name or port (such as port 21), you can use the following routines to obtain the service name and port:
getservbyname |
to obtain the service port when given its name |
getservbyport |
to obtain the service name when given its port number |
Use these routines to obtain port numbers and service names. Doing so makes changing port numbers easy; you simply edit the services definition file, TCPWARE:SERVICES.
The netdb.h include file defines the servent structure as shown below.
struct servent {
char *s_name; /* official service */
char **s_aliases; /* alias list */
int s_port; /* port number */
char *s_proto; /* protocol to use */
};
Multicasting, as specified in RFC 1112, Host Extensions for IP Multicasting, is the transmission of an IP datagram to a multicast host group. A multicast host group is a set of zero or more hosts identified by a single class D IP destination address. Using a multicast host group allows applications running on your host to receive multicast IP datagrams destined for that host group.
TCPware implements the highest level of conformance specified in RFC 1112 – level 2 "full support for multicasting."
The following TCPware programming interfaces support IP multicasting for network interfaces that support multicasting (such as Ethernet, FDDI, and Token Ring):
· UDPDRIVER
· IPDRIVER
· BGDRIVER (SOCK_DGRAM sockets)
· INETDRIVER (SOCK_DGRAM sockets)
· Socket Library
To send a multicast datagram, specify an IP multicast address in the range 224.0.0.0 to 239.255.255.255 as the destination address in a send request. This range covers the class D IP addresses that identify multicast host groups. Note that 224.0.0.0 is unassigned and 224.0.0.1 is assigned to the permanent group of all IP hosts, including gateways, used to address all multicast hosts on the directly connected network.
Each multicast transmission is sent from a single network interface, even if the host has more than one multicast-capable interface. The system manager establishes the default interface to use for multicasting by defining the appropriate routes. If no route is defined for the multicast address, TCPware uses the default gateway's interface. If you do not specify a default gateway, TCPware uses the first available interface. An application can issue a request to explicitly set the interface to use for subsequently transmitted multicast datagrams.
When you send a multicast datagram, TCPware by default delivers a local copy of it if the multicast address is joined by one or more receivers. An application can issue a request to disable the local loopback of multicast datagrams.
TCPware sends IP multicast datagrams with a time-to-live (TTL) of 1 by default, which prevents them from being forwarded beyond a single subnetwork. An application can issue a request to specify the TTL for subsequent multicast datagrams to be set to any value from 0 to 255, in order to control the scope of the multicasts.
Before an application can receive IP multicast datagrams, it must become a member of one or more IP multicast groups. The system manager can explicitly have the host join multicast groups by issuing the NETCU ADD MULTICAST command (and the NETCU REMOVE MULTICAST command to leave a group). Or, an application can ask the host to join (or leave) a multicast group by issuing a request.
See Multicasting Commands in the NETCU Command Reference for the multicasting commands.
The memberships requested by an application do not necessarily determine which datagrams that application receives. The IP layer accepts incoming multicast datagrams if anyone claimed a membership in the destination group of the datagram. However, delivery of a multicast datagram to a particular application is based on the destination port (for UDP) or protocol type, just as with unicast and broadcast datagrams. To receive multicast datagrams sent to a particular port/protocol, you must bind to that local port, leaving the local address unspecified.
Every membership is associated with a single interface, and it is possible to join the same group on more than one interface. If you do not specify a local interface's address when joining a group, TCPware uses the default multicast interface. At present, you can have a maximum of 32 memberships per socket. TCPware drops the memberships an application adds when you close the socket or port or the application exits. However, more than one socket or port can claim membership in a particular group, and the host remains a member of that group until the last claim is dropped.
This section includes information on the following:
· Writing a stream client
· Writing a stream server
· Writing a datagram client
· Writing a datagram server
· Writing servers
When writing a stream client, use the following sequence, with socket library (and system QIO equivalent) routines given:
1. Create a socket by calling the socket routine and requesting a SOCK_STREAM socket (SYS$ASSGN).
2. Open the connection to the server by calling the connect routine (IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP).
You need to specify a sockaddr_in structure with the destination internet address and destination port number filled in where the:
Internet address is the address of the server and can be obtained by calling gethostbyname or inet_addr (inet_addr converts an ASCII dotted internet address to binary representation).
Port number is the well-known port for the server and can be obtained by calling getservbyname.
3. Send and receive data as needed by calling the standard send/recv and read/write routines (IO$_WRITEVBLK or IO$_READVBLK). When you use send/recv, you can specify flag options, some of which allow you to:
· Send urgent data, or
· Peek at incoming data at the head of the queue to determine what to do before the data is actually read.
4. Close the connection by calling the close routine (IO$_SETMODE | IO$M_CTRL | IO$M_SHUTDOWN and then SYS$DASSGN).
The programs in the below table that demonstrate stream client applications are included in the TCPWARE_ROOT:[TCPWARE.EXAMPLES] directory.
Program |
Description |
DAYTIMED.C |
DAYTIME client that uses the TCPware Socket Library |
DISCARD.C |
DISCARD client that uses the INETDRIVER |
FINGER.C |
FINGER client that uses the TCPDRIVER |
TCPSAMPLE.FOR |
DISCARD FORTRAN client that uses the TCPDRIVER |
WHOIS.C |
WHOIS client that use the TCPware Socket Library |
When writing a stream server, use the following sequence, with socket library (and system QIO equivalent) routines given:
1. Create a socket by calling the socket routine and requesting a SOCK_STREAM socket (SYS$ASSGN).
2. Bind the socket to the well-known port for the service by calling the bind routine (IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP). Specify zero for the internet address. Use the getservbyname routine to determine the required port.
3. Set the socket to accept incoming connections by calling the listen routine, which has two parameters: the socket that you want to listen on, and a backlog parameter, a value that indicates how many connections you can have at a given time (the range is 1 to 5).
For example, if you have a single-threaded server that processes connections serially, the server listens, accepts a connection, and processes that connection. Meanwhile other connections can come in on the well-known port. In this case, the connection is established and placed in a queue waiting to be accepted. However, you can do nothing with the connection until you perform the next step.
4. Wait for a connection (call accept), where:
· accept returns a new socket for the connection (you do the I/O on this socket).
· The original socket is still listening for more connections.
5. Receive and send data as needed to provide the service by calling read or write and send or recv (IO$_READVBLK or IO$_WRITEVBLK).
6. Close the connection socket by calling the close routine (IO$_SETMODE | IO$M_CTRL | IO$M_SHUTDOWN).
7. Go to step 4.
The programs in the below table that demonstrate stream server applications are included in the TCPWARE_ROOT:[TCPWARE.EXAMPLES] directory.
Program |
Description |
DISCARDD.C |
DISCARD server that uses the TCPDRIVER or Socket Library |
FINGERD.C |
FINGER server that uses the TCPDRIVER |
TCPSAMPLE.FOR |
DISCARD FORTRAN server that uses the TCPDRIVER |
Writing a datagram client consists of the same type of operations as writing a stream client. Use the following sequence, with socket library (and system QIO equivalent) routines given:
1. Create a socket by calling the socket routine and requesting a SOCK_DGRAM socket (SYS$ASSGN).
2. (Optionally) issue a connect call if you are only going to exchange information with a particular remote peer (IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP).
In this case, when you issue a connect call, the process reserves internal socket fields that identify the internet address and port number to which the data is to go. This means you can send data using standard write and send calls instead of having to use the sendto call to specify the destination. Note that:
· connect names the communication endpoint. There is no need to specify it with each send.
· No connection is opened with UDP.
3. Send a request datagram calling the sendto or send routines (use send only if connected). (IO$_WRITEVBLK.)
If you did not issue a connect call, you need to issue a sendto call that specifies the buffer you want to send and where to send the buffer.
4. Start a timer. For example, the program might give the server five seconds to respond. If there is no response within that time, the program reissues the request.
The program could also indicate to the user that there is a problem if, for example, the server does not respond after five attempts to reach the server.
5. Receive a reply datagram using the call recvfrom (or recv if connected). Cancel the timer if the proper reply was received; otherwise reissue the recvfrom or recv. (IO$_READVBLK.)
6. If the application needs to request more data, go to step 3.
7. Close the socket by calling the close routine (IO$_SETMODE | IO$M_CTRL | IO$M_SHUTDOWN and then SYS$DASSGN).
The programs in the below table that demonstrate datagram client applications are included in the TCPWARE_ROOT:[TCPWARE.EXAMPLES] directory.
Program |
Description |
UDPSAMPLE.FOR |
DISCARD FORTRAN client that uses the UDPDRIVER |
Writing a datagram server consists of the same type of operations as writing a stream server, with socket library (and system QIO equivalent) routines given:
1. Create a socket by calling the socket routine and requesting a SOCK_DGRAM socket (SYS$ASSGN).
2. Bind the socket to the well-known port for the service by calling the bind routine (IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP). This call says the socket is willing to accept datagrams sent to this particular port.
3. Wait for a request to arrive using the call recvfrom (IO$_READVBLK). In this call you specify the buffer, the length of the buffer, and the sockaddr_in structure that will be filled in with the information about who sent you that datagram.
4. Decode and service the request.
5. Send the reply using the call sendto (IO$_WRITEVBLK.).
6. Go to step 3.
The programs in the below table that demonstrate datagram server applications are included in the TCPWARE_ROOT:[TCPWARE.EXAMPLES] directory.
Program |
Description |
BG_UDP_SERVER.C |
DISCARD server that uses the BGDRIVER |
UDPDRIVER.C |
DISCARD server that uses the UDPDRIVER |
UDPSAMPLE.FOR |
DISCARD FORTRAN server that uses the UDPDRIVER |
UDP_SOCK_SERVER.C |
DISCARD server that uses the HP Socket Library |
Servers that need to service several connections at once are complex. They require use of multiplexing services because they must not block waiting for any one connection. Use:
· BSD select operation (if using a socket library interface)
· ASTs or EFNs for SYS$QIO (if using a QIO interface)
Such servers also typically require a context block per connection. This block includes the socket or channel number and other information about the state which that connection is in and any information processing that connection requires.
You can also use a master server (such as NETCP in TCPware or inetd in UNIX) if you are writing a server. Initiating the server with the master server allows you to:
· Avoid a separate listening process for each service.
For example, you might have several different servers, for each of which you would need a unique detached process. A master server avoids doing this because there is one process that listens for any of these ports. Once a connection comes in on one of these ports, the server creates a process to service that connection. The master server process does the listen and then the accept. When the accept has completed, it creates a detached process and that detached process services that connection. In the meantime, the master server has gone back to waiting for another connection to come in.
· Simplify support for multiple connections. This means you do not need to write multithreaded servers.
When you use the master server process, you add server information to its database as to:
o Whether the application uses TCP or UDP.
o The well-known port number or port name to listen on.
o Information about the process that needs to be created (for example where the image is located, what privileges it must run with, and so on).
The server is started automatically when a connection is established or a datagram is sent to the well-known port. The server's input, output, and error files are assigned to the socket (or, in VMS, the device name). This simplifies the server because it does not need to do socket, bind, listen, and accept operations since these have been done by the master server. All the server does is service the one connection and events occurring on it.
For details on using the TCPware master server, see Appendix A, TCPware Socket Library, of this guide, and the ADD SERVICE command in the NETCU Command Reference.