Topic : Socket Programming
Author : Reg Quinton
Page : << Previous 2  Next >>
Go to page :

same octet sequence on all machines. See byteorder(3N) for host to net conversions (host format to/from network format).

Services and Ports:
Services have names (eg. SMTP the Simple Mail Transfer Protocol). Ports have numbers (eg. SMTP is a service on port 25). The mapping from service names to port numbers is listed in /etc/services.

[1:22pm julian] page /etc/services
# $Author: reggers $
# $Date: 1997/05/02 20:17:16 $
# Network services, Internet style
ftp             21/tcp
telnet          23/tcp
smtp            25/tcp          mail
whois           43/tcp          nicname
domain          53/tcp          nameserver
domain          53/udp          nameserver
tftp            69/udp
finger          79/tcp
nntp            119/tcp         readnews untp
ntp             123/udp
snmp            161/udp
xdmcp           177/udp         xdm

Programming Calls:
But programmers don't scan /etc/services, they use library routines. The C library routines getservbyname(3N) (and getservbyport(3N) on the same page) each return a pointer to an object with the following structure containing the broken-out fields of a line in /etc/services.

struct   servent {
   char   *s_name;      /* name of service */
   char   **s_aliases;  /* alias list */
   int    s_port;       /* port for service */
   char   *s_proto;     /* protocol to use */

Client applications connect to a service port. Usually this is prefaced by translating a service name (eg. SMTP) into the port number (but if you knew the port number you could carefully skip that step).

int     tcpopen(host,service)
char    *service, *host;
    struct  servent         *sp;
    if ((sp=getservbyname(service,"tcp")) == NULL) then error...

Ie. to determine the port number for a particular tcp service. Note that you'd do the same to determine port numbers for UDP services.

Socket Addressing:
A Socket Address is a host.port pair (communication is between host.port pairs -- one on the server, the other on the client). We know how to determine host numbers and service numbers so we're well on our way to filling out a structure were we specify those numbers. The structure is sockaddr_in, which has the address family is AF_INET as in this fragment:

int     tcpopen(host,service)
char    *service, *host;
{   int     unit;
    struct  sockaddr_in     sin;
    struct  servent         *sp;
    struct  hostent         *hp;
    if ((sp=getservbyname(service,"tcp")) == NULL) then error...
    if ((hp=gethostbyname(host)) == NULL) then error...

    bzero((char *)&sin, sizeof(sin));
    bcopy(hp->h_addr,(char *)&sin.sin_addr, hp->h_length);

The code fragment is filling in the IP address type AF_INET, port number and IP address in the Socket Address structure -- the address of the remote host.port where we want to connect to find a service.

There's a generic Socket Address structure, a sockaddr, used for communication in arbitrary domains. It has an address family field and an address (or data) field:

/* from: /usr/include/sys/socket.h */
struct sockaddr {
    u_short sa_family;   /* address family */
    char    sa_data[14]; /* max 14 byte addr */

The sockaddr_in structure is for Internet Socket Addresses (address family AF_INET). An instance of the generic socket address.

/* from: /usr/include/netinet/in.h */
struct sockaddr_in {
    short   sin_family;       /* AF_INET      */
    u_short sin_port;         /* service port */
    struct  in_addr sin_addr; /* host number  */
    char    sin_zero[8];      /* not used     */

The family defines the interpretation of the data. In other domains addressing will be different -- services in the UNIX domain are names (eg. /dev/printer). In the sockaddr_in structure we've got fields to specify a port and a host IP number (and 8 octets that aren't used at all!). That structure specifies one end of an IPC connection. Creating that structure and filling in the right numbers has been pretty easy so far.

File Descriptors and Sockets:

File Descriptors:
File Descriptors are the fundamental I/O object. You read(2) and write(2) to file descriptors.

    int cc, fd, nbytes;
    char *buf;

    cc = read(fd, buf, nbytes);
    cc = write(fd, buf, nbytes)

The read attempts to read nbytes of data from the object referenced by the file descriptor fd into the buffer pointed to by buf. The write does a write to the file descriptor from the buffer. Unix I/O is a byte stream.

File descriptors are numbers used for I/O. Usually the result of open(2) and creat(2) calls.

All Unix applications run with stdin as file descriptor 0, stdout as file descriptor 1, and stderr as file descriptior 3. But stdin is a FILE (see stdio(3S)) not a file descriptor. If you want a stdio FILE on a file descriptor use fdopen(3S).

A Socket is a Unix file descriptor created by the socket(3N) call -- you don't open(2) or creat(2) a socket. By way of comparison pipe(2) creates file descriptors too -- you might be familiar with pipes which predate sockets in the development of the Unix system.

   int s, domain, type, protocol;
   s = socket(domain, type, protocol);
   cc = read(s, buf, nbytes);

The domain parameter specifies a communications domain (or address family). For IP use AF_INET but note that socket.h lists all sorts of address families. This is to inform the system how an address should be understood -- on different networks, like AF_DECnet, addressing may be longer than the four octets of an IP number. We're only concerned with IP and the AF_INET address family.

The type parameter specifies the semantics of communication (sometimes know as a specification of quality of services). For TCP/IP use SOCK_STREAM (for UDP/IP use SOCK_DGRAM). Note that any address family might support those service types. See socket.h for a list of service types that might be supported.

A SOCK_STREAM is a sequenced, reliable, two-way connection based byte stream. If a data cannot be successfully transmitted within a reasonable length of time the connection is considered broken and I/O calls will indicate an error.

The protocol specifies a particular protocol to be used with the socket -- for TCP/IP use 0. Actually there's another programmers interface getprotobyname(3N) that provides translates protocol names to numbers. It's an interface to the data found in /etc/protocols -- compare with the translation of service names to port numbers discussed above.

Client Connect:
A client application creates a socket(3N)and then issues a connect(3N) to a service specified in a sockaddr_in structure:

int     tcpopen(host,service)
char    *service, *host;
{   int     unit;
    struct  sockaddr_in     sin;
    struct  servent         *sp;
    struct  hostent         *hp;

    if ((sp=getservbyname(service,"tcp")) == NULL) then error...
    if ((hp=gethostbyname(host)) == NULL) then Ierror...
    bzero((char *)&sin, sizeof(sin))
    if ((unit=socket(AF_INET,SOCK_STREAM,0)) < 0) then error...
    if (connect(unit,&sin,sizeof(sin))

Page : << Previous 2  Next >>