Chapter 3. Sockets Migration and Sockets-to-TLI Conversion

This chapter provides an introduction to the issues involved in porting a sockets-based application to TLI. It includes notes on the differences between BSD sockets and IRIX SVR4 sockets that programmers must understand.

Topics covered in this chapter include:

Introduction

Although existing sockets-based applications can be rewritten for TLI relatively easily, such ports are not necessary for sockets-based applications that are to run only over TCP/IP or UDP/IP networks.

Both TLI and sockets-interface routines are defined in terms of communication paths identified by file descriptors. These file descriptors are known as transport endpoints for TLI and as sockets for the sockets interface. In most cases, there are parallel routines for each transport function. For example, the TLI routine t_open() returns a file descriptor that identifies a transport endpoint; the routine socket() returns a file descriptor that identifies a socket. Table 3-1, near the end of this chapter, shows the parallels between TLI and sockets interface routines.

This chapter highlights the areas in which there is no direct correspondence between TLI and sockets interface routines. The examples first show code that uses the sockets interface and then show how to rewrite the program using TLI.

The last section of the chapter documents differences between IRIX SVR4 sockets and IRIX SVR3 or IRIX BSD sockets. Programmers must be aware of these differences before moving sockets-based applications to IRIX SVR4.

IRIX –systype svr4 sockets calls are implemented as library routines. Application programs for –systype svr4 that use sockets should be compiled and linked with sockets libraries as follows:

IRIS% cc prog.c -dy -lsocket -lnsl

Connection Mode

Both TLI and the sockets interface support two distinct types of service: connection-mode service and connectionless-mode service.

Establishing Sockets Connections: Client Code

When creating a socket, you must specify the type of service you want (for example, SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW).

The service type determines whether connection-mode or connectionless-mode semantics are used. For a simple example of connection establishment, consider the client side of a datastream-oriented application, as in Example 3-1. It must initiate a connection by first creating a stream socket and then using the connect() call to establish communication with a preexisting socket on a server machine.

Example 3-1. A Client for a Datastream-Oriented Application

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#include <stdio.h> 
main(argc, argv)
    int argc;
    char *argv[]; 
{
    int sock;
    struct sockaddr_in server;
    struct hostent *hp, *gethostbyname();
    struct servent *sp, *getservbyname(); 
    /* create socket */
    sock = socket(AF_INET, SOCK_STREAM, 0) ;
    if (sock < 0) 
    {
       perror("opening stream socket");
       exit(1);
    }
    /* connect socket using name specified by command line */
    server.sin_family = AF_INET;
    hp = gethostbyname(argv[1]);
    if (hp == 0) 
    {
        fprintf(stderr, "%s: unknown host\n", argv[1]);
        exit(2);
    }
    memcpy((char *)&server.sin_addr, (char *)hp->h_addr,
           hp->h_length);
    sp = getservbyname(argv[2], "tcp");
    if (sp == 0) 
    {
        fprintf(stderr, "%s: unknown service\n", argv[2]);
        exit(3);
    }
    server.sin_port = sp -> s_port; 
    if (connect(sock, (struct sockaddr *)&server,
               sizeof server ) < 0) 
    {
        perror("connecting stream socket");
        exit(1);
    }
}

Notice the calls to gethostbyname() and getservbyname(). These are the sockets-oriented network directory services described in “Sockets-based Datagrams”. They take a host and service name, respectively, and return the host network address and the service port. The service port number can be thought of as a machine-specific service address. Certain well-known services are assumed to have specific port numbers in the 1-to-1023 range. Some applications hard code these port numbers rather than using getservbyname().

The routines gethostbyname() and getservbyname() are available to –systype svr3 if –lbsd is linked in on the command line. When porting sockets-based applications, calls to gethostbyname() and getservbyname(), as well as hard-coded port numbers, should be replaced by calls to the netdir_getbyname() routine, which is available for –systype svr4 only.

If the target socket exists and is prepared to handle a connection, the connection completes successfully and the program can begin to send messages. Messages are delivered in order without message boundaries. The connection is destroyed when both sockets are closed.


Note: Some transports hold the connection open briefly in case more data is sent. The user may also have directed the system to wait. For more information, see the discussion of the SO_LINGER option in the getsockopt(3N) manual page.


Establishing TLI Connections: Client Code

The TLI connection-mode transport service is also circuit (datastream) oriented, enabling data to be transferred over an established connection in a reliable, sequenced manner. Typical client code is shown in Example 3-2.

Example 3-2. A TLI Client

#include <stdio.h>
#include <netdir.h>
#include <netconfig.h>
extern int t_errno;
main() 
{
    int fd;
    struct netconfig *nconf;
    void *handlep;

    /* select an appropriate network */
    if ((handlep = setnetpath()) == NULL) {
        nc_perror("Error in initializing networks");
        exit(1);
    }
    /*
     * try all transports until finding one that matches
     * the user's stated preferences
     */ 
    while ((nconf = getnetpath(handlep)) != NULL)
        if (nconf->nc_semantics == NC_TPI_COTS)
            break;
    if (nconf == NULL)
    {
        fprintf(stderr, "no transports available\n");
        exit(1); 
    }
    if ((fd = t_open(nconf->nc_device,O_RDWR,NULL)) < 0) {
        t_error("t_open failed");
        exit(2);
    } 
    if (t_bind(fd, NULL, NULL) < 0) {
        t_error("t_bind failed");
        exit(3);
    } 
    endnetpath(handlep);
}

Network selection is used by TLI applications to find the device filename associated with the requested transport protocol. The device filename that matches the protocol is passed to t_open(). t_open() then returns a file descriptor that identifies a new transport endpoint and, optionally (by way of its third argument), the default characteristics of the transport provider associated with that endpoint (and indirectly specified by the first argument). t_bind() then binds the new transport endpoint to the transport address contained in its second argument. The typical client does not care what its own address is because no other process will try to access it. The second and third arguments in the example are therefore NULL.

Establishing Sockets Connections: Server Code

Connection establishment for a server process is slightly different. The process must bind itself to an address and wait for clients to connect to it. Example 3-3 shows how a sockets-based server is bound to its known address.

Example 3-3. A Sockets-based Server

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>

#define SRV_PORT 2

main(argc,argv)
int argc; 
char *argv[];
{
    int sock;
    struct sockaddr_in server;
    int msgsock;
    char buf[BUFSIZ];
    /* create socket */

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        perror("opening stream socket");
        exit(1);
    }

    /* name socket */

    server.sin_family = AF_INET
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = SRV_PORT; 
    if (bind(sock, (struct sockaddr *)&server,
             sizeof server) < 0)  {
        perror("binding stream socket"); 
        exit(2); 
    } 
    /* the server now does a listen and accept */ 
}

In the example, the server explicitly asks to be bound to port SRV_PORT.

Establishing TLI Connections: Server Code

The equivalent code for a TLI server process is shown in Example 3-4.

Example 3-4. A TLI Server

#include <stdio.h> 
#include <fcntl.h> 
#include <netconfig.h> 
#include <netdir.h> 
#include <tiuser.h> 

#define  SRV_ADDR  2 

main(argc,argv) 
int argc; 
char *argv[]; 
{
    struct nd_hostserve hostserv;
    int fd;
    char buf[BUFSIZ];
    struct netconfig *nconf;
    struct t_bind *bind;
    void *handlep;
    extern int t_errno; 

    if ( argc != 3 )  {
        fprintf(stderr, "USAGE: %s host service\n", argv[0]);
        exit(1);
    } 
    hostserv.h_host = argv[1]; 
    hostserv.h_serv = argv[2]; 

    if ((handlep = setnetpath()) == NULL)  {
        nc_perror("setnetpath");
        exit(2);
    }
    /* select an appropriate transport and
     * get address for remote host/service
     */
    while ((nconf = getnetpath(handlep)) != NULL)  {
        if (nconf->nc_semantics == NC_TPI_COTS)
            break; 
    } 
    if (nconf == NULL)  {
       fprintf(stderr, "no connection-mode transport\n");
       exit(3);
    } 
    if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0)  {
       t_error("t_open failed");
       fprintf(stderr, "unable to open %s\n",
               nconf->nc_device);
       exit(4);
    }
    endnetpath(handlep); 

    if ((bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL))
        == NULL)  {
       t_error("t_alloc of t_bind structure failed");
       exit(5);
    }

    /* for simplicity in this example, assume the
     * address is an integer
     */ 

    bind->qlen = 1;
    bind->addr.len = sizeof(int);
    *(int *)bind->addr.buf = SRV_ADDR;

    /* 2nd arg NULL -> bind to any address */ 

    if (t_bind(fd, bind, bind) < 0)  {
        t_error("t_bind failed");
        exit(6);
    }

    /* was the correct address bound? */

    if (*(int *)bind->addr.buf != SRV_ADDR)  {
        t_error("t_bind bound wrong address");
        exit(7);
    }

    /* the server now does a listen and accept */
}

The examples show two significant differences between the sockets interface and TLI. First, since TLI server applications work over any transport provider, they use the Network Selection and Name-to-Address Mapping features in order to be protocol independent; sockets-based applications use fixed addresses.

A second difference is in the behavior of the TLI and sockets bind() routines when an address is invalid or unavailable: the sockets bind() routine fails, while the TLI t_bind() routine can bind to another address instead. For this reason, servers should check that the address returned by t_bind() as its third argument is correct.

Connectionless Mode

Connectionless-mode transport services, in contrast to connection-mode services, are message-oriented and support transfer in self-contained units (datagrams) with no necessary logical relationship to each other. The sockets interface and TLI both provide connectionless-mode service.

All the information required to deliver a datagram (for example, a destination address) is presented to the transport provider, together with the data to be transmitted, in a single service access. A given service access need not relate to any other service access. Each unit of data transmitted is entirely self-contained, and can be independently routed by the transport provider.

Sockets-based Datagrams

The differences between sockets-library datagrams and the connectionless service provided by TLI parallel the differences between normal “stream” sockets and TLI connection-mode service described above. Example 3-5 gives the code necessary to send an Internet domain datagram to a receiver whose host and service names are given as command-line arguments.

Example 3-5. Sending an Internet Domain Datagram

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>

#define DATA "It was the best of times. It was the worst of times."

/*
 *The form of the command line is:
 *           dgramsend hostname servicename 
 */ 
main(argc, argv)
    int argc;
    char *argv[];
{
    struct servent *sp, *getservbyname();
    int sock;
    struct sockaddr_in name;
    struct hostent *hp, *gethostbyname();

    /* create socket on which to send */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
       perror("opening datagram socket");
       exit(1);
    }
    /* Find the socket to send to. gethostbyname() returns a
     * structure including the network address of the
     * specified host. The port number is taken from the
     * command line.
     */
    hp = gethostbyname(argv[1]);
    if (hp == 0)   {
       fprintf(stderr, "%s: unknown host\n", argv[1]);
       exit(2);
    }
    memcpy((char *)&name.sin_addr, (char *)hp->h_addr,
           hp->h_length);
    name.sin_family = AF_INET;
    sp = getservbyname(argv[2], "tcp");
    if (sp == 0)  {
        fprintf(stderr, "%s: unknown service\n", argv[2]);
        exit(3);
    }
    server.sin_port = sp->s_port;

    /* send message */
    if (sendto(sock, DATA, sizeof DATA, 0,
           (struct sockaddr *)&name, sizeof name) < 0)
        perror("sending datagram message");
    close(sock);
    exit(0);
 }

The program looks up the host address and the service port (both given on the command line) by calling gethostbyname() and getservbyname(). The host network address and service port number are in the structures returned by these two library routines. They are copied into the structure that specifies the destination of the message.

TLI-based Datagrams

TLI connectionless-mode service is functionally similar to sockets datagram service. The sockets-interface address-management routines gethostbyname() and getservbyname() are replaced by netdir_getbyname() for both connection-mode and connectionless-mode service.

The TLI code in Example 3-6 sends a datagram to a receiver whose host and service names are given on the command line.

Example 3-6. Sending a TLI Datagram

#include <stdio.h>
#include <fcntl.h>
#include <netconfig.h>
#include <netdir.h>
#include <tiuser.h>
/*
 * the form of the command line is:
 *           dgramsend hostname servicename
 */ 
main(argc,argv) 
int argc; 
char *argv[]; 
{
    int fd;
    struct nd_hostserve hostserv;
    struct nd_addrlist *addrs;
    struct netconfig *nconf;
    struct t_unitdata *ud;
    void *handlep;
    extern int t_errno;

    if ( argc != 3 )  {
        fprintf(stderr, "USAGE: %s host service\n", argv[0]);
        exit(1);
    }
    hostserv.h_host = argv[1];
    hostserv.h_serv = argv[2];

    if ((handlep = setnetconfig()) == NULL)  {
        nc_perror ("setnetconfig failed");
        exit(1);
    }
    /* select an appropriate transport and
     * get address for remote host/service
     */
    while ((nconf = getnetconfig(handlep)) != NULL)  {
        if (nconf->nc_semantics == NC_TPI_CLTS &&
            netdir_getbyname(nconf, &hostserv, &addrs) == 0)
            break;
    }
    if (nconf == NULL)  {
       fprintf(stderr, "no address for host %s service %s\n",
               argv[1], argv[2]);
       exit(2);
    }
    if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0)  {
        t_error("t_open failed");
        fprintf(stderr, "unable to open %s\n",
                nconf->nc_device);
        exit(3);
    }
    if (t_bind(fd, NULL, NULL) < 0)   {
        t_error("t_bind failed");
        exit(4);
    }
    if ((ud = (struct t_unitdata *)t_alloc(fd, T_UNITDATA,
        T_ALL)) == NULL )  {
        t_error("t_alloc of t_unitdata structure failed");
        exit(5);
    }
    /* use first address returned by netdir_getbyname()
     * filldata() will fill in the data to be sent
     */
    ud->addr = addrs->n_addrs;
    filldata(ud->udata);
    endnetconfig(handlep);

    /* send the datagram */
    if (t_sndudata(fd, ud) <0 )
    {
       t_error("t_sndudata failed");
       exit(6);
    }
    exit(0); 
}

For more information about the functions t_open(3N), t_bind(3N), and t_sndudata(3N), see the manual pages.

Synchronous and Asynchronous Modes

Transport services are inherently asynchronous, with events occurring independently of the actions of the transport user. For example, a user can be sending data over a transport connection when an asynchronous disconnect indication arrives. The user must somehow be informed that the connection has been broken. Both the sockets interface and TLI provide an asynchronous mode for managing such events. Asynchronous mode is most useful for applications that expect long delays between events and have other tasks that they can perform in the meantime.

A socket is put into asynchronous mode by calling fcntl() and specifying O_NDELAY or O_NONBLOCK. Once in asynchronous mode, all relevant primitives such as send() and read() return EWOULDBLOCK whenever they encounter situations that would have caused them to block if they had been in synchronous mode.

The TLI nonblocking mode is also specified with the O_NDELAY or O_NONBLOCK flag. The O_NDELAY and O_NONBLOCK flags can be used when the transport provider is initially opened with the t_open() function, or later with the fcntl() call. If the blocking mode is used, these cause the error code EAGAIN to be returned.

There are different levels of asynchronous operation. Specifying O_NDELAY or O_NONBLOCK puts a socket into nonblocking mode. For true asynchronous operation, however, it is also necessary to test for asynchronous events. Sockets-based applications normally use select(3N) to test for asynchronous events.

TLI-based applications should use poll(2) to test for asynchronous events. select() is supported only for compatibility with older applications.

Both TLI and the sockets interface provide mechanisms for asynchronous event notification. The sockets interface uses fcntl() to request that the system issue a SIGIO signal when it becomes possible to perform I/O on a given file descriptor. TLI uses the I_SETSIG ioctl(). This causes the system to send the process a SIGPOLL signal when the I/O event specified actually occurs. The TLI mechanism is the more powerful of the two, since it allows users to specify the precise kind of I/O event they want to be signaled on (see the streamio(7) manual page for the possible kinds of event).

A process that issues functions in synchronous mode must still be able to recognize certain asynchronous events immediately and act on them if necessary. Eight such asynchronous events are specified for TLI and cover both connection and connectionless modes (see the t_look(3N) manual page). TLI routines that encounter trouble return the special transport error TLOOK. The user can then use the t_look() function to identify the event that generated the error. Alternatively, the transport user can use t_look() to poll the transport endpoint periodically for asynchronous events. If a sockets function encounters trouble, the primitive returns an errno value directly.

Error Handling

TLI attempts to separate communication errors from system errors by defining two levels of errors:

  • Library errors. Each library function has one or more error returns and indicates failure by returning -1. An external integer, t_errno, holds the specific error number when such a failure occurs. This value is set when errors occur but is not cleared by successful library calls. It should therefore be tested only after an error has been indicated. A diagnostic function, t_error(), is provided for printing out information on the current transport error.

  • System errors. The standard external variable errno, is used to report system errors. Such errors can, of course, affect TLI functioning. When they do, t_errno is set to TSYSERR and errno is set to indicate the specific system error that occurred. The state of the transport provider may change if a transport error occurs.

The sockets interface provides a similar facility with getsockopt() when called with an option of SO_ERROR.

Sockets-to-TLI Conversion

Table 3-1 shows some approximate TLI/sockets equivalents. The Comments column describes the differences. Where there are no comments, either the functions are the same or there is no equivalent function in one or the other interface.

Table 3-1. Table of TLI/Sockets Equivalents

TLI function

Sockets function

Comments

read(), write()

read(), write()

In TLI, you must push the tirdwr module before calling read() or write(); in sockets, it is sufficient just to call read() or write().

t_accept()

accept()

 

t_alloc()

 

t_bind()

bind()

t_bind() sets the queue depth for passive sockets, but bind() doesn't. For sockets, the queue length is specified in the call to listen().

t_close()

close()

 

t_connect()

connect()

connect() can be performed without first binding the local endpoint. The endpoint must be bound before calling t_connect(). connect() can be done on a connectionless endpoint to set the default destination address for datagrams.

t_error()

perror()

 

t_free()

 

t_getinfo()

getsockopt()

t_getinfo() returns information about the transport. getsockopt() can return information about the transport and the socket.

t_getname()

getsockname()

 

t_getstate()

 

t_listen()

listen()

t_listen() waits for connection indications. listen() merely sets the queue depth.

t_look()

getsockopt() with the SO_ERROR option returns the same kind of error information as t_look().

t_open()

socket()

 

socketpair()

 

t_optmgmt()

getsockopt(), setsockopt()

t_optmgmt() manages only transport options. getsockopt() and setsockopt() can manage options not only at the transport layer, but also at the socket layer and at arbitrary protocol layers.

t_rcv()

recv(), recvfrom(), recvmsg()

recvfrom() and recvmsg() operate in connection mode as well as datagram mode.

t_rcvconnect()

 

t_rcvdis()

 

t_rcvrel()

 

t_rcvudata()

recvfrom(), recvmsg()

 

t_rcvuderr()

 

t_snd()

send(), sendto(), sendmsg()

sendto() and sendmsg() operate in connection mode as well as datagram mode.

t_snddis()

 

t_sndrel()

shutdown()

 

t_sndudata()

sendto(), sendmsg()

 

t_sync()

 

t_unbind()

 


Moving Sockets Applications to System V, Release 4

Although the IRIX SVR4 sockets implementation and the SVR3/BSD sockets implementation are largely compatible, there are some differences an application programmer must be aware of before moving an SVR3/BSD sockets-based application to IRIX SVR4. These differences are described in Table 3-2.

Table 3-2. Differences in Sockets Implementations

Call

BSD

IRIX

bind()

bind() uses the credentials of the user at the time of the bind() call to determine whether the port requested should be allocated or not.

A call to socket() causes the user's credentials to be remembered and used to validate addresses used in bind().

connect()

If connect() is called on an unbound socket, the protocol determines whether or not the endpoint is bound before the connection takes place.

When connect() is called on an unbound socket, that socket is always bound to an address selected by the transport provider.

getsockname()

getsockname() works when a previously existing connection has been closed.

getsockname() returns -1 and errno is set to EPIPE if a previously existing connection has been closed.

read()

A call to read() fails with errno set to ENOTCONN if read() is used on an unconnected socket that needs to be connected.

A call to read() returns zero bytes read if the socket is in blocking mode. If the socket is in nonblocking mode, it returns -1 with errno set to EAGAIN.

sendmsg() and readmsg()

If the MSG_PEEK flag has been set when sendmsg() is called, and access rights are available, the access rights are copied, leaving them available for reading by a subsequent call to recvmsg().

If the MSG_PEEK flag is specified in a call to recvmsg(), and access rights are available, the access rights are transferred to the user buffer associated with the receiving socket. They are then destroyed, and the transferring socket has no further access to them. They are therefore unavailable to a subsequent call to recvmsg(). Any data associated with the access rights is also copied to the user buffer and is not available to recvmsg().

setsockopt()

setsockopt() can be used at any time during the life of a socket.

Because of the state diagram specified by the Transport Provider Interface (TPI), a setsockopt() operation on a transport provider conforming to this specification fails if issued on a socket that is not bound to a local address. Specifically, if a socket is unbound and setsockopt() is used, then the operation succeeds in the AF_INET domain, but fails in the AF_UNIX domain.

shutdown()

If shutdown() is called with a value of 2 for how, further attempts to receive data return EOF, and attempts to send data return -1 with errno set to EPIPE with a SIGPIPE issued.

Calling shutdown() with the value of how equal to 0 does not cause further attempts to receive data to return zero bytes if the read(2) system call is used and the socket is in nonblocking mode. In this case, read() returns -1 with errno set to EAGAIN. If one of the socket-receive primitives is used, the correct result (EOF) is returned.

 

If shutdown() is called with a value of 2 for how, further attempts to receive data return EOF, and attempts to send data return -1 with errno set to EPIPE with a SIGPIPE issued.

The same results occur, except that attempts to send data using the write() system call causes errno to be set to EIO. As in the above case, if a sockets primitive is used, the correct errno is returned.

S_IFSOCK

This file type identifies a socket descriptor.

There is no socket file type, and this #define does not exist.

SIGIO

SIGIO is delivered every time new data is appended to the socket input queue.

SIGIO is delivered only when data is appended to a socket queue that was previously empty.

SIGURG

SIGURG is delivered every time new data is anticipated or actually arrives.

SIGURG is delivered only when there is no urgent data already pending.

SIOCSPGRP(), FIOSETOWN(), and F_SETOWN()

The SIOCSPGRP, FIOSETOWN, and F_SETOWN ioctl()s and the F_SETOWN fcntl() take as argument a positive process ID or negative process group identifying the intended recipient list of subsequent SIGURG and SIGIO signals.

This is not the case in IRIX –systype svr4. The only acceptable arguments to these system calls is the caller's process ID or a negative process group that has the same absolute value as the caller's process ID. In other words, the only recipient of SIGURG and SIGIO signals is the calling process.

S_ISSOCK

The ISSOCK macro takes the mode of a file as an argument. It returns 1 if the file represents a socket and 0 otherwise.

The ISSOCK macro does not exist. In SVR4, a socket is defined as a file descriptor associated with a Stream character device that has the socket module pushed onto it.

write()

write() fails with errno set to ENOTCONN if it is used on an unconnected socket.

A call to write() on an unconnected socket appears to succeed, but the data is discarded. The socket error option SO_ERROR is set to ENOTCONN if this occurs.

 

write() can be used on type SOCK_DGRAM sockets (either AF_UNIX or AF_INET domains) to send zero-length data.

A call to write() with zero-length data returns -1, with errno set to ERANGE. The functions send(), sendto(), or sendmsg() should be used to send zero-length data.

Miscellaneous

If an invalid buffer is specified in a function, the function normally returns -1 with errno set to EFAULT.

If an invalid buffer is specified in a function, the user's program will probably dump core.

 

If ls –l is executed on a directory that contains a UNIX domain socket, an s is printed on the left side of the mode field.

If ls –l is executed on a directory that contains a UNIX domain socket, a p is printed on the left side of the mode field.

 

Executing ls –F causes an equals sign (=) to be printed after any filename that represents a UNIX domain socket.

Nothing is printed after a filename that represents a UNIX domain socket.