Here is something I picked up on the Net.

This file contains exaples of client and servers using several protocols, might be very usefull.

 

BSD Sockets

Sockets are a generalized networking capability first introduced in 4.1cBSD and subsequently refined into their current form with 4.2BSD. The sockets feature is available with most current UNIX system releases. (Transport Layer Interface (TLI) is the System V alternative). Sockets allow communication between two different processes on the same or different machines. Internet protocols are used by default for communication between machines; other protocols such as DECnet can be used if they are available.

To a programmer a socket looks and behaves much like a low level file descriptor. This is because commands such as read() and write() work with sockets in the same way they do with files and pipes. The differences between sockets and normal file descriptors occurs in the creation of a socket and through a variety of special operations to control a socket. These operations are different between sockets and normal file descriptors because of the additional complexity in establishing network connections when compared with normal disk access.

For most operations using sockets, the roles of client and server must be assigned. A server is a process which does some function on request from a client. As will be seen in this discussion, the roles are not symmetric and cannot be reversed without some effort.

This description of the use of sockets progresses in three stages:

The use of sockets in a connectionless or datagram mode between client and server processes on the same host. In this situation, the client does not explicitly establish a connection with the server. The client, of course, must know the server's address. The server, in turn, simply waits for a message to show up. The client's address is one of the parameters of the message receive request and is used by the server for response.

The use of sockets in a connected mode between client and server on the same host. In this case, the roles of client and server are further reinforced by the way in which the socket is established and used. This model is often referred to as a connection-oriented client-server model.

The use of sockets in a connected mode between client and server on different hosts. This is the network extension of Stage 2, above.

The connectionless or datagram mode between client and server on different hosts is not explicitly discussed here. Its use can be inferred from the presentations made in Stages 1 and 3.

 


Socket Creation Using socketpair()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int socketpair(int af, int type, int protocol, int sv[2])
socketpair() results in the creation of two connected sockets. sv[] is the array where the file descriptors for the sockets are returned. Each descriptor in sv[] is associated with one end of the communications link. Each descriptor can be used for both input and output. This means that full two-way communication between a parent process and one child process is possible.

Normally, one descriptor is reserved for use by a parent process and the other descriptor is used by a child process. The parent process closes the descriptor used by the child process. Conversely, the child process closes the descriptor used by the parent process. fork() is still required to pass one of the sockets to a child.

af represents the domain or address family to which the socket belongs. type is the type of socket to create.

Domains refer to the area where the communicating processes exist. Commonly used domains include:

 

Socket type refers to the "style" of communication. The two most commonly used values include:

 

A protocol value of 0 is very common. This permits the system to choose the first protocol which is permitted with the pair of values specified for family and type.

Example (borrowed from 4.3BSD IPC Tutorial by Stuart Sechrest)

#define DATA1   "test string 1"
#define DATA2   "test string 2"

#include &ltsys/types.h>
#include &ltsys/socket.h>
#include &ltstdio.h>
#include &lterrno.h>

main()
{
  int sockets[2], child;
  char buf[1024];

  /* Get the socket pair */
  if (socketpair(AF_UNIX, SOCK_STREAM,
    0, sockets) < 0) {
      printf("error %d on socketpair\n", errno);
      exit(1);
  }

  /* create child process */
  if ((child = fork()) == -1) {
    printf("fork error %d\n", errno);
    exit(1);
  }

  if (child != 0) { /* this is the parent */
    /* close child's end of socket */
    close(sockets[0]);

    /* read message from child */
    if (read(sockets[1], buf, sizeof(buf)) < 0) {
      printf("error %d reading socket\n", errno);
      exit(1);
    }
    printf("-->%s\n", buf);

    /* write message to child */
    if (write(sockets[1], DATA1, sizeof(DATA1)) < 0) {
      printf("error %d writing socket\n", errno);
      exit(1);
    } 

    /* finished */
    close(sockets[1]);

  } else {  /* the child */

    /* close parent's end of socket */
    close(sockets[1]);

    /* send message to parent */
    if (write(sockets[0], DATA2, sizeof(DATA1)) < 0) {
      printf("error %d writing socket\n", errno);
      exit(1);
    } 

    /* get message from parent */
    if (read(sockets[0], buf, sizeof(buf)) < 0) {
      printf("error %d reading socket\n", errno);
      exit(1);
    }
    printf("-->%s\n", buf);

    /* finished */
    close(sockets[0]);
  }
}

Socket Creation Using socket()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int socket(int af, int type, int protocol)
socket() is very similar to socketpair() except that only one socket is created instead of two. This is most commonly used if the process you wish to communicate with is not a child process. The af, type, and protocol fields are used just as in the socketpair() system call.

On success, a file descriptor to the socket is returned. On failure, -1 is returned and errno describes the problem.

 


Giving a Socket a Name - bind()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int bind(int s, struct sockaddr *name, int namelen)
Recall that, using socketpair(), sockets could only be shared between parent and child processes or children of the same parent. With a name attached to the socket, any process on the system can describe (and use) it.

In a call to bind(), s is the file descriptor for the socket, obtained from the call to socket(). name is a pointer to a structure of type sockaddr. If the address family is AF_UNIX (as specified when the socket is created), the structure is defined as follows:

	struct sockaddr {
		u_short sa_family;
		char    sa_data[14];
	};
	
name.sa_family should be AF_UNIX. name.sa_data should contain up to 14 bytes of a file name which will be assigned to the socket. namelen gives the actual length of name, that is, the length of the initialized contents of the data structure.

A value of 0 is return on success. On failure, -1 is returned with errno describing the error.

Example:

struct sockaddr name;
int s;
name.sa_family = AF_UNIX;
strcpy(name.sa_data, "/tmp/sock");
if((s = socket(AF_UNIX, SOCK_STREAM, 0) < 0)
   {
        printf("socket create failure %d\n", errno);
        exit(0);
   }
if (bind(s, &name, strlen(name.sa_data) +
   sizeof(name.sa_family)) < 0)
       printf("bind failure %d\n", errno);

Specifying a Remote Socket - connect()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int connect(int s, struct sockaddr *name, int namelen)
The bind() call only allows specification of a local address. To specify the remote side of an address connection the connect() call is used. In the call to connect, s is the file descriptor for the socket. name is a pointer to a structure of type sockaddr:
	struct sockaddr {
		u_short sa_family;
		char    sa_data[14];
	};
	
As with the bind() system call, name.sa_family should be AF_UNIX. name.sa_data should contain up to 14 bytes of a file name which will be assigned to the socket. namelen gives the actual length of name. A return value of 0 indicates success, while a value of -1 indicates failure with errno describing the error.

A sample code fragment:


struct sockaddr name;

name.sa_family = AF_UNIX;
strcpy(name.sa_data, "/tmp/sock");

if (connect(s, &name, strlen(name.sa_data) +
	sizeof(name.sa_family)) < 0) {
		printf("connect failure %d\n", errno);
}

Sending to a Named Socket - sendto()

int sendto(int s, char *msg, int len, int flags,
	struct sockaddr *to, int tolen)
This function allows a message msg of length len to be sent on a socket with descriptor s to the socket named by to and tolen, where tolen is the actual length of to. flags will always be zero for our purposes. The number of characters sent is the return value of the function. On error, -1 is returned and errno describes the error.

An example:

struct sockaddr to_name;

to_name.sa_family = AF_UNIX;
strcpy(to_name.sa_data, "/tmp/sock");

if (sendto(s, buf, sizeof(buf), 0, &to_name,
	strlen(to_name.sa_data) +
		sizeof(to_name.sa_family)) < 0) {
	printf("send failure\n");
	exit(1);
}

Receiving on a Named Socket - recvfrom()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int recvfrom(int s, char *msg, int len, int flags,
	struct sockaddr *from, int *fromlen)
This function allows a message msg of maximum length len to be read from a socket with descriptor s from the socket named by from and fromlen, where fromlen is the actual length of from. The number of characters actually read from the socket is the return value of the function. On error, -1 is returned and errno describes the error. flags may be 0, or may specify MSG_PEEK to examine a message without actually receiving it from the queue.

If no message is available to be read, the process will suspend waiting for one unless the socket is set to nonblocking mode (via an ioctl call).

The system I/O call read() can also be used to read data from a socket.

 


Disposing of a Socket

#include &ltstdio.h>

void close(int s).
The I/O call close() will close the socket descriptor s just as it closes any open file descriptor.

Example - sendto() and recvfrom()

/* receiver */
#include &ltsys/types.h>
#include &ltsys/socket.h>

struct sockaddr myname;
struct sockaddr from_name;
char buf[80];

main()
{
  int   sock;
  int   fromlen, cnt;

  sock = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (sock < 0) {
    printf("socket failure %d\n", errno);
    exit(1);
  }

  myname.sa_family = AF_UNIX;
  strcpy(myname.sa_data, "/tmp/tsck");

  if (bind(sock, &myname, strlen(myname.sa_data) +
        sizeof(name.sa_family)) < 0) {
    printf("bind failure %d\n", errno);
    exit(1);
  }

  cnt = recvfrom(sock, buf, sizeof(buf),
    0, &from_name, &fromlen);
  if (cnt < 0) {
    printf("recvfrom failure %d\n", errno);
    exit(1);
  }

  buf[cnt] = '\0';  /* assure null byte */
  from_name.sa_data[fromlen] = '\0';

  printf("'%s' received from %s\n",
    buf, from_name.sa_data);
}

/* sender */
#include &ltsys/types.h>
#include &ltsys/socket.h>

char buf[80];
struct sockaddr to_name;

main()
{
  int   sock;

  sock = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (sock < 0) {
    printf("socket failure %d\n", errno);
    exit(1);
  }

  to_name.sa_family = AF_UNIX;
  strcpy(to_name.sa_data, "/tmp/tsck");

  strcpy(buf, "test data line");

  cnt = sendto(sock, buf, strlen(buf), 0, &to_name,
    strlen(to_name.sa_data) + sizeof(to_name.sa_family));
  if (cnt < 0) {
    printf("sendto failure %d\n", errno);
    exit(1);
  }
}

A Refinement: Client-Server Connections

Sockets can be used to write client-server applications using a connection-oriented client-server technique. Some characteristics of this technique include: The client-server connection, when established, remains in existence until either the client or the server explicitly breaks it, much like a trouble-free telephone call. The socket used for this connection is called a connection-oriented socket.

The socket type is specified as SOCK_STREAM. As a result, the process receiving a message processes that message by the following rules:

Functions listen() and accept() enable the server to listen for service requests. read() and write() may be used by client and server to send/receive messages; send() and recv() may also be used.

Make a Socket a Listen-only Connection Endpoint - listen()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int listen(int s, int backlog)
listen establishes the socket as a passive endpoint of a connection. It does not suspend process execution.

No messages can be sent through this socket. Incoming messages can be received.

s is the file descriptor associated with the socket created using the socket() system call. backlog is the size of the queue of waiting requests while the server is busy with a service request. The current system-imposed maximum value is 5.

0 is returned on success, -1 on error with errno indicating the problem.

Example:

#include &ltsys/types.h>
#include &ltsys/socket.h>

int sockfd; /* socket file descriptor */

if(listen(sockfd, 5) < 0)
	printf ("listen error %d\n", errno);

Connection Establishment by Server - accept()

#include &ltsys/types.h>
#include &ltsys/socket.h>

int accept(int sockfd, struct sockaddr *name, int *namelen)
The accept() call establishes a client-server connection on the server side. (The client requests the connection using the connect() system call.) The server must have created the socket using socket(), given the socket a name using bind(), and established a listen queue using listen().

sockfd is the socket file descriptor returned from the socket() system call. name is a pointer to a structure of type sockaddr as described above

	struct sockaddr {
		u_short sa_family;
		char    sa_data[14];
	};
Upon successful return from accept(), this structure will contain the protocol address of the client's socket.

The data area pointed to by namelen should be initialized to the actual length of name. Upon successful return from accept, the data area pointed to by namelen will contain the actual length of the protocol address of the client's socket.

If successful, accept() creates a new socket of the same family, type, and protocol as sockfd. The file descriptor for this new socket is the return value of accept(). This new socket is used for all communications with the client.

If there is no client connection request waiting, accept() will block until a client request is queued.

accept() will fail mainly if sockfd is not a file descriptor for a socket or if the socket type is not SOCK_STREAM. In this case, accept() returns the value -1 and errno describes the problem.

 


Data Transfer over Connected Sockets - send() and recv()

Two additional data transfer library calls, namely send() and recv(), are available if the sockets are connected. They correspond very closely to the read() and write() functions used for I/O on ordinary file descriptors.
#include &ltsys/types.h>
#include &ltsys/socket.h>

int send(int sd, char *buf, int len, int flags)

int recv(int sd, char * buf, int len, int flags)
In both cases, sd is the socket descriptor.

For send(), buf points to a buffer containing the data to be sent, len is the length of the data and flags will usually be 0. The return value is the number of bytes sent if successful. If not successful, -1 is returned and errno describes the error.

For recv(), buf points to a data area into which the received data is copied, len is the size of this data area in bytes, and flags is usually either 0 or set to MSG_PEEK if the received data is to be retained in the system after it is received. The return value is the number of bytes received if successful. If not successful, -1 is returned and errno describes the error.

 


A Connection-Oriented Example - listen(), accept()

/* Generic program structure for establishing
   connection-oriented client-server environment. */

/* server program */

#include &ltstdio.h>
#include &lterrno.h>
#include &ltsignal.h>
#include &ltsys/types.h>
#include &ltsys/socket.h>

struct sockaddr myname;
char buf[80];

main()
{
  int sock, new_sd, adrlen, cnt;

  sock = socket(AF_UNIX, SOCK_STREAM, 0);
  if (sock < 0) {
    printf("server socket failure %d\n", errno);
    perror("server: ");
    exit(1);
  }

  myname.sa_family = AF_UNIX;
  strcpy(myname.sa_data, "/tmp/billb");
  adrlen = strlen(myname.sa_data) +
      sizeof(myname.sa_family);

  unlink("/tmp/billb");  /* defensive programming */
  if (bind(sock, &myname, adrlen) < 0) {
    printf("server bind failure %d\n", errno);
    perror("server: ");
    exit(1);
  }

  if (listen(sock, 5) < 0 {
    printf("server listen failure %d\n", errno);
    perror("server: ");
    exit(1);
  }

  /* Ignore child process termination.   */

  signal (SIGCHLD, SIG_IGN);

  /*  Place the server in an infinite loop, waiting
      on connection requests to come from clients.
      In practice, there would need to be a clean
      way to terminate this process, but for now it
      will simply stay resident until terminated by
      the starting terminal or the super-user.        */

  while (1) {
    if (new_sd = accept(sock, &myname, &adrlen)) < 0 {
      printf("server accept failure %d\n", errno);
      perror("server: ");
      exit(1);
    }

    /* Create child server process.  Parent does no
       further processing -- it loops back to wait
       for another connection request.                */

    printf("Socket address in server %d is %x, %s\n",
      getpid(), myname.sa_data, myname.sa_data);

    if (fork() == 0) {  /* child process */
      close (sock);  /* child does not need it  */
      /*  . . . . .  */

      cnt = read(new_sd, buf, strlen(buf));
      printf ("Server with pid %d got message %s\n",
         getpid(), buf);
      strcpy (buf, "Message to client");
      cnt = write(new_sd, buf, strlen(buf));
      printf("Socket address in server %d is %x, %s\n",
        getpid(), myname.sa_data, myname.sa_data);

      /*  . . . . .  */
      close (new_sd); /* close prior to exiting  */
      exit(0);
    }   /* closing bracket for if (fork() ... )  */

  }  /* closing bracket for while (1) ... )    */

}  /* closing bracket for main procedure   */

/* client program */

#include &ltstdio.h>
#include &lterrno.h>
#include &ltsys/types.h>
#include &ltsys/socket.h>

char buf[80];
struct sockaddr myname;

main()
{
  int   sock, adrlen, cnt;

  sock = socket(AF_UNIX, SOCK_STREAM, 0);
  if (sock < 0) {
    printf("client socket failure %d\n", errno);
    perror("client: ");
    exit(1);
  }

  myname.sa_family = AF_UNIX;
  strcpy(myname.sa_data, "/tmp/billb");
  adrlen = strlen(myname.sa_data) +
      sizeof(myname.sa_family);

  if (connect( sock, &myname, adrlen) < 0) {
    printf("client connect failure %d\n", errno);
    perror("client: ");
    exit(1);
  }
  /*  . . . . .  */

  strcpy(buf, "Message sent to server");
  cnt = write(sock, buf, strlen(buf));
  cnt = read(sock, buf, strlen(buf));
  printf("Client with pid %d got message %s\n",
     getpid(), buf);
  printf("Socket address in server %d is %x, %s\n",
     getpid(), myname.sa_data, myname.sa_data);

  /*  . . . . .  */
  exit(0);
}

Connecting Across a Network

Sending data across a network is done in much the same way as sending data on a single machine. Establishing a connection is more difficult as a simple file name is not common among different machines. This is why networking domains other than AF_UNIX exist.

The following pages describe several of the routines available to establish a network connection. All these functions are for use with the AF_INET domain. The concept of network addressing is discussed first.

 


Network Addresses - The IP Address

The IP host address, or more commonly just IP address, is used to identify hosts connected to the Internet. IP stands for Internet Protocol and refers to the Internet Layer of the overall network architecture of the Internet. An IP address is a 32-bit quantity interpreted as 4 8-bit numbers or octets. Each IP address uniquely identifies the participating user network, the host on the network, and the class of the user network.

An IP address is usually written in a dotted-decimal notation of the form N1.N2.N3.N4, where each Ni is a decimal number between 0 and 255 decimal (00 through ff hexadecimal). Addresses are controlled and assigned by the Internet Network Information Center at SRI International. There are currently 5 defined classes of networks, called A, B, C, D and E; classes A, B and C are available to users. Each interface on a host system connected to the Internet must have its own IP address.

A brief synopsis of IP address classes A, B, and C is contained in the following table.

Class/Attribute	ABC	Address Format	N.H.H.HN1.N2.H.HN1.N2.N3.HFirst Octet0 < N < 
127128 <= N1 < 192192<= N1 < 224Network IDNN1.N2N1.N2.N3Host IDH.H.HH.HHAvailabilityVery
difficultAlmost exhaustedAmple supplyExample10.133.51.224128.247.116.3192.58.103.7
SubnetsAs the number of hosts on a given network grew large and more geographically separated, network management considerations and physical layer limitations such as maximum cable length gave impetus to research into possible changes in network installations without massive disruptions. Also, the use of heterogeneous physical layer networks (e.g., Ethernet and Token Ring) with the same IP network address was increasing.

The concept of subnet was introduced to help solve these problems. The benefits for Class A and Class B networks especially are worth the subnet planning and implementation effort.

The basic idea in subnetworking (a common word also used is subnetting) is to partition the host identifier portion of the IP address into two parts:

  1. A subnet address within the network address itself; and
  2. A host address on the subnet.
For example, a common Class B address format is N1.N2.S.H, where N1.N2 identifies the Class B network, the 8-bit S field identifies the subnet, and the 8-bit H field identifies the host on the subnet.

 


Network Host Names

Using IP addresses to access hosts on a network is fine for the IP software. Most people are more comfortable with names, and procedures for both proper name construction and translation of these names into IP addresses has been in existence for some time. The most commonly used is the Domain Name System (DNS), occasionally but inaccurately referred to as the Domain Name Service. Naming in DNS is done hierarchically, that is, in a tree-structure format, much like the UNIX file system naming. The top two levels are controlled by the Internet Network Information Center (NIC) at SRI International.

At the top of the domain are two-letter country designators and three-letter (usually) general category designators within the USA. Some examples are:

The next level usually identifies the institution. For example: The combination of these should be enough to specify the name, type and location of the organizational entity. For example:
ukuug.uk -- UNIX Users Group in the United Kingdom
utdallas.edu -- University of Texas at Dallas in the USA
ti.com -- Texas Instruments in the USA
Further hierarchies may be established within these organizations and are controlled locally. Some examples of host names are:
csservr2.utdallas.edu -- host csservr2 at UT-D
pac1.pac.sc.ti.com --	host pac1 within domain pac within domain sc at TI
sunk.ssc.gov -- host sunk at the Superconducting SuperCollider Laboratory
DNS and other software help in maintenance of these naming conventions and in the translation of host names to IP addresses and vice versa.

 


The /etc/hosts File

The correspondence between host names and IP addresses is maintained in a file called hosts in the (top-level) directory /etc. On most systems, any user can read this file. (A word of caution: on many systems, printing this file may be injurious to local paper supply!!)

Entries in this file look like the following:

127.0.0.1		localhost
192.217.44.208	snoopy beagle hound metlife
153.110.34.18	bugs wabbit wascal
153.110.34.19	elmer
153.110.34.20	sam
Note that more than one name may be associated with a given IP address. This file is used when converting from IP address to host name and vice versa.

 


Selecting a Service Port

The protocol specifications of the protocols used in the AF_INET domain require specification of a port. The port number is used to determine which process on the remote machine to talk to.

Certain network services have been assigned Well Known Ports. The port assignments to network services can be found in the file /etc/services. Selection of a Well Known Port involves searching this file and is done with the following functions:

#include &ltnetdb.h>

struct servent *getservbyname(char *name, char *proto)

struct servent *getservbyport(int port, char *proto)
The two options for proto in each call are tcp for stream oriented communications, and udp for datagram oriented communications. port is the (known) port number when the service name is requested, while name is the character string containing the service name when the port number is requested.

The return value for each function is a pointer to a structure with the following form:


	struct    servent {
     	char *s_name;      /* official name of service */
     	char **s_aliases;  /* alias service name list */
     	long s_port;       /* port service resides at */
     	char *s_proto;     /* protocol to use */
     };
If a program does not need to communicate with a Well Known Port it is possible to choose an unused port for use by a program. Illustration of this technique is contained in the sample programs below.

 


Putting a Host Program Address Together

Once a host address and port number are known then the complete process address must be put together in a form that may be used by the system calls already covered. The structures set up to allow this follow:
#include &ltnetinet/in.h>

/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
    union {
         struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
         struct { u_short s_w1,s_w2; } S_un_w;
         u_long S_addr;
    } S_un;
#define s_addr  S_un.S_addr
        /* can be used for most tcp & ip code */
};


/*
 * Socket address, internet style.
 */
struct sockaddr_in {
      short   sin_family;
      u_short sin_port;
      struct  in_addr sin_addr;
      char    sin_zero[8];
};
Filling in the fields for sockaddr_in will produce an Internet version of a socket address.

 


Finding a Machine Address

Finding an address that can be used to connect to a remote machine is done with either of the following commands:
#include &ltnetdb.h>

struct hostent *gethostbyname(char *name)

struct hostent *gethostbyaddr(char *addr, int len, int type)
name contains the host name for which the IP address is needed. addr points to a structure of type in_addr and len is the size in bytes of this structure. In this discussion type is always AF_INET since the discussion is limited to use of IP addresses on the Internet.

Both calls return a pointer to a host entry structure. This structure has the following form:

	struct hostent {
		char   *h_name;  /* official name of host */
		char   **h_aliases;  /* alias list */
		int    h_addrtype;   /* address type */
		int    h_length;     /* length of address */
		char   **h_addr_list;
			/* list of addresses from name server */
	#define  h_addr h_addr_list[0]
			/* address for backward compatibility */
	};

Network Byte Order for Numbers

Not all machine architectures use the same internal representations for numbers. For instance, in host systems based on Intel microprocessors and the DEC VAX architecture bytes are stored in increasing memory locations in reverse or "little-endian" order. All other systems store bytes in increasing memory locations in "normal" or "big-endian" order.

Left as is, communications between, e.g., IBM-compatible PCs and Sun Microsystems workstations would be difficult at best. To overcome this all data sent over a network is converted to Network Byte Order which is, in the context of the above discussion, "big-endian" order.

Routines for converting data between a host's internal representation and Network Byte Order are:

#include &ltsys/types.h>
#include &ltnetinet/in.h>

u_long htonl(u_long hostlong);

u_short htons(u_short hostshort);

u_long ntohl(u_long netlong);

u_short ntohs(u_short netshort);
These functions are macros and result in the insertion of conversion source code into the calling program. On little-endian machines the code will change the values around to network byte order. On big-endian machines no code is inserted since none is needed; the functions are defined as null.

 


Network Address Conversion Routines

An Internet address is usually written and specified in the dotted-decimal notation described above. Internally it becomes part of a structure of type in_addr. To convert between these two representations two functions are available.
#include &ltsys/types.h>
#include &ltsys/socket.h>
#include &ltnetinet/in.h>     /* in_addr structure */

unsigned long inet_addr(char *ptr)

char *inet_ntoa(struct in_addr inaddr)
inet_addr() converts a character string in dotted-decimal notation to a 32-bit Internet address. The return value is not consistent, unfortunately. The correct return should be a pointer to a structure of type in_addr but many systems, following an older convention, return only the internal representation of the dotted-decimal notation. The man pages will clarify the situation for the host system on which the function is used.

inet_ntoa() expects a structure of type in_addr as a parameter (note that the structure itself is passed, not a pointer) and returns a pointer to a character string containing the dotted-decimal representation of the Internet address.

 


Internet Connection-Oriented Client-Server Example

The next two sections contain sample programs for server and client when connection-oriented communication across the Internet (using TCP) is requested. The server and client programs are named vcserver and vcclient, respectively. As built, the programs have some restrictions:

 

If vcclient is started on the same host as vcserver is running, the command line to start vcclient is
	vcclient localhost server-port-number
	
If vcclient is started on a host different from the host on which vcserver is running, the command line to start vcclient is
	vcclient server-host-name server-port-number
vcserver will remain resident until the user terminates it. The cleanest way to do this is
	kill -15 pid
	
where pid is the process id of vcserver.

 


Server Source Listing - vcserver.c

/*  vcserver.c -- TCP network (virtual circuit) server  */

#include &ltstdio.h>
#include &lterrno.h>
#include &ltsys/types.h>
#include &ltsys/socket.h>
#include &ltnetinet/in.h>    /* sockaddr_in structure */

/* This entry allows the program to look up the name
   of the host and any alias names associated with it. */

#include &ltnetdb.h>          /* /etc/hosts table entries */

main (int argc, char *argv[])

{
   int rc,            /* system call return code */
       new_sd, sock,  /* server/listen socket descriptors */
       adrlen,        /* sockaddr length */
       cnt;           /* number of bytes I/O */

   struct sockaddr_in myname;  /* Internet socket name */
   struct sockaddr_in *nptr;   /* ptr to get port number */
   struct sockaddr    addr;    /* generic socket name */

   char buf[80];   /* I/O buffer, kind of small  */

   /* For lookup in /etc/hosts file.   */

   struct hostent *hp, *gethostbyaddr();

   /* Identify the server process. */

   printf("\nThis is the network server with pid %d\n",
        getpid() );

   /* As in UNIX domain sockets, create a "listen" socket */

   if (( sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
      printf("network server socket failure %d\n", errno);
      perror("network server");
      exit(1);
   /* Initialize the fields in the Internet socket name
      structure.                                          */

   myname.sin_family = AF_INET;  /* Internet address */
   myname.sin_port = 0;  /* System will assign port #  */
   myname.sin_addr.s_addr = INADDR_ANY;  /* "Wildcard" */

   /* Bind the Internet address to the Internet socket */

   if (bind(sock, &myname, sizeof(myname) ) < 0 ) {
      close(sock);  /* defensive programming  */
      printf("network server bind failure %d\n", errno);
      perror("network server");
      exit(2);
   }

   /* Get the port number assigned to the Internet socket.
getsockname() obtains the port number associated
with the bound socket and returns it as part of the
information in the sockaddr addr structure.  Note
that, since the port number is not passed directly
by this program to any client, the only way to
"advertise" it is to print it, that is, send it to
the user's stdout.  Other than this printout, this
code is not intrinsic to the connectivity process.
*/

   adrlen = sizeof(addr); /* need int for return value */
   if ( ( rc = getsockname( sock, &addr, &adrlen ) ) < 0 )
{
      printf("setwork server getsockname failure %d\n",
errno);
      perror("network server");
      close (sock);
      exit(3);
   }

   /* DEBUG CODE: the generic address "addr" is used to
return the socket value obtained from the
getsockname() call.  Print this information.  In the
generic structure definition, all but the address
family is defined as a char string.  After this
call, the generic address structure addr is used to
hold information about the client process. */

   printf("\nAfter getsockname():");
   printf(" server listen socket data\n");
   printf("\taddr.sa_family field value is: %d\n",
        addr.sa_family);
   printf("\taddr.sa_data string is %d bytes long;\n",
        sizeof ( addr.sa_data ) );
   printf("\taddr.sa_data string is:");
   for ( cnt = 0: cnt < sizeof (addr.sa_data); cnt++)
        printf(" %x", addr.sa_data[cnt]);
   printf("\n");

   /* Now "advertise" the port number assigned to the
socket.  In this example, this port number must be
used as the second command line parameter when
starting up the client process.  */

   /* Note the use of the pointer nptr, with a different
mapping of the allocated memory, to point to the
generic address structure. */

   nptr = (struct sockaddr_in *) &addr;  /* port # */
   printf("\n\tnetwork server: server has port number: %d\n",
      ntohs ( nptr -> sin_port ) );

   /*  Mark the socket as a "listen-only" or passive socket */

   if ( listen ( sock, 5 ) < 0 ) {
      printf("network server bind failure %d\n", errno);
      perror("network server");
      close (sock);
      exit(4);
   }

   /* Debug output: information contained in myname structure
      (the Internet socket).                                  */

   printf("Server has set up client socket with values:\n");
   printf("\tInternet address is %lx\n", myname.sin_addr.s_addr);
   printf("\tPort number used is %d\n", myname.sin_port);
   printf("\tInternet family ID is %d\n", myname.sin_family);
   printf("\tValues are filled in after connection request ");
   printf("is accepted.");

   /* Set up "infinite loop" to listen for clients.  Since the
      structure "myname" is bound to the listen socket, the
      socket structure name and socket length parameter
      values are omitted from the accept call.  The bound values
      are used.                                               */

   while (1) {
      if ( ( new_sd = accept ( sock, 0, 0 ) ) < 0 ) {
         printf("network server accept failure %d\n", errno);
         perror("network server");
         close (sock);
         exit(5);
      }

      /* Fork child process to handle client service request */

      if ( ( fork() ) == 0 ) {    /* Child process */

         int pid;

         pid = getpid();   /* PID of child process */
         close (sock); /* Do not need listen socket in child. */

         /* Find out who the client is.  Note the use of the
            generic address structure addr to hold information
            about the (connected) client.                    */

         if ((rc = getpeername( new_sd, &addr, &adrlen )) < 0) {
            printf("network server %d getpeername failure %d\n",
                 pid, errno);
            perror("network server");
            close(new_sd);
            exit(6);
         }
         /* Just for grins, "announce" the client.  Note that,
            since pointer nptr is of type struct sockaddr_in,
            the field names as defined in the structure template
            sockaddr_in can be used to access values in the addr
            generic structure.                               */

         printf("\n\tnetwork server %d:", pid);
         printf(" client socket from host %s\n",
              inet_ntoa ( nptr -> sin_addr ) );
         printf("\t     has port number %d\n",nptr -> sin_port);

         /* Now find all names associated with the client; this
            is the reason for the /etc/hosts file lookup
            declarations.   */

         if (( hp = gethostbyaddr (&nptr -> sin_addr,4,AF_INET))
                  != NULL ) {
            printf ("\tfrom hostname: %s\n\twith aliases: ",
                   hp -> h_name );
            while ( *hp -> h_aliases )
               printf ("\n\t\t\t%s", *hp -> h_aliases++ );
            printf("\n\n");
         }
         else {
            printf("network server %d ", pid);
            printf("gethostbyaddr failure %d\n", h_errno);
            perror("network server");
         }

         /*  Exchange data with client.  Clear buffer first. */

         do {
     
            /* Take your pick, depending on system pedigree.
               The System V function has not been tested as of
               this edition.                               */

            bzero( buf, sizeof(buf)); /* zero buf, BSD call. */
            /* memset (buf,0,sizeof(buf)); /* zero buf, S5. */

            /* Read message from remote client; if message length
               = 0, quit. */

            if (( cnt = read (new_sd, buf, sizeof(buf))) < 0 ) {
               printf("network server %d ", pid);
               printf("socket read failure &d\n", errno);
               perror("network server");
               close(new_sd);
               exit(7);
            }
            else
            if (cnt == 0) {
               printf("network server received message");
               printf(" of length %d\n", cnt);
               printf("network server closing");
               printf(" client connection...\n");
               close (new_sd);
               continue; /* break out of loop */
            }
            else {

               /*  Print out message received from client.  Send
                   a message back.        */

               printf("network server %d received message",pid);
               printf(" of length %d\n", cnt);
               printf("network server %d  received", pid));
               printf(" the message %s\n", buf);
               bzero (buf, sizeof(buf)); /* zero buf, BSD. */
               /* memset(buf,0,sizeof(buf)); /* zero buf, S5. */
               strcpy(buf, "Message from server to client");
               write (new_sd, buf, sizeof(buf));
            }  /* end of message-print else */

         }  /* end of do loop statement */

         while (cnt != 0);  /* do loop condition */
         exit(0);  /* Exit child process */

      } /* End of if-child-process true condition */

      else      /* Not child process; must be parent process */

         close (new_sd); /* Parent doesn't need work socket. */

   }  /* end of while (1) */

}  /* end of main procedure */

Client Source Listing - vcclient.c

/*  vcclient.c -- TCP network (virtual circuit) client  */

#include &ltstdio.h>
#include &lterrno.h>
#include &ltsys/types.h>
#include &ltsys/socket.h>
#include &ltnetinet/in.h>    /* sockaddr_in structure */

/* This entry allows the program to look up the name
   of the host and any alias names associated with it.    */

#include &ltnetdb.h>          /* /etc/hosts table entries */

main (argc, argv)
int argc;
char *argv[];

/* Expected command line parameters:

   argv[0] -- name of executable
   argv[1] -- the host name to which connection is desired
   argv[2] -- the port number to be used by the client: the
              value is the port number assigned to the
              server by the server's host system.

   This is not elegant, but is very useful for debugging the
   connectivity code.  For example, if the host name ( argv[1] )
   were speficied as "localhost" both client and server
   could run on the same system but the network connectivity
   code would still be fully exercised.         */

{
   int sock,          /* socket descriptor */
       val,           /* scratch variable */
       cnt;           /* number of bytes I/O */

   struct sockaddr_in myname;  /* Internet socket name (addr) */
   struct sockaddr_in *nptr;   /* pointer to get port number */

   char buf[80];   /* I/O buffer, kind of small  */

   /* For lookup in /etc/hosts file.   */

   struct hostent *hp, *gethostbyaddr();

   /* Check that the user supplied all parameters on the
      command line.  If so, convert argv[2] to integer; copy it
      into the sin_port field of the myname structure.  Use
      the htons function to insure that the value is stored
      in network byte order.                           */

   if ( argc < 3 ) {
      printf("network client failure: required parameters");
      printf(" missing from the command line\n");
      printf("network client: usage");
      printf("[executable-name] [host name] [port number]\n");
      exit(1);
   }

   /* As in UNIX domain, create a client socket to request
      service          */

   if (( sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
      printf("network client socket failure %d\n", errno);
      perror("network client");
      exit(2);
   }

   /* Convert user-supplied port number to integer.  There is
      no consistency check possible in this set-up.        */

   myname.sin_port = htons( atoi(argv[2]) ); /* Server port # */

   myname.sin_family = AF_INET;    /* Internet domain */

   /* For display purposes only, print out the host name and
      the converted port number.                      */

   printf("network client %s will try to connect to host %s\n",
          argv[0], argv[1]);
   printf("network client %s will use port number %d\n",
          argv[0], ntohs ( myname.sin_port ) );

   /* Obtain server host information.  */

   hp = gethostbyname ( argv[1] );

   /* This is mainly debug code; if it becomes necessary to
      insert it (or if it just feels more comfortable to
      have it in place!!!) don't remove it when all is working
      well.  Rather, leave it in place as a comment.      */

   if ( hp == NULL ) {
      printf("network client gethostbyname failure %d\n",
           errno);
      perror("network client");
      close ( sock );
      exit(3);
   }
   else {
      printf("\nServer information obtained via");
      printf(" gethostbyname:\n");
      printf("\tThe official host name is %s\n", hp -> h_name);
      printf("\tThe address type is %d\n", hp -> h_addrtype);
      printf("\tThe length of the address is %d bytes\n",
            hp -> h_length);
      printf("\tThe first host address is %lx\n",
             ntohl ( * (int * ) hp -> h_addr_list[0] ) );
      printf("\tAlias names for the host are:\n");
      while ( *hp -> h_aliases )
         printf( "\t\t%s\n", *hp -> h_aliases++ );
   }

   /* Use either memcpy or bcopy as appropriate
      (System V vs BSD). */

   bcopy ( hp -> h_addr_list[0], &myname.sin_addr.s_addr,
           hp -> h_length );
   /* memcpy ( &myname.sin_addr.s_addr, hp -> h_addr_list[0],
           hp -> h_length ); */
     
   /* More debug code: Verify the contents of structure myname
      prior to trying to connect to the (remote) server.    */

   printf("\nInformation provided in client's");
   printf(" connect request\n");
   printf("\tRemote host address is %lx\n",
        ntohl ( myname.sin_addr.s_addr ) );
   printf("\tPort number supplied is %d\n",
        ntohs ( myname.sin_port ) );
   printf("\tInternet family ID is %d\n", myname.sin_family);
   printf("\tsin_zero character string is: %s\n",
        myname.sin_zero);

   /* Establish socket connection with (remote) server.  */

   if ( ( connect ( sock, &myname, sizeof(myname) ) ) < 0 ) {
         printf("network client %s connect failure %d\n",
              argv[0], errno);
         perror("network client");
         close (sock);
         exit(4);
      }

   /*  Exchange data with client.  Clear buffer bytes first. */

   /* Take your pick, depending on your system's pedigree.  The
      System V function has not been tested. */

   bzero  ( buf,    sizeof( buf) );  /* zero buffer, BSD. */
   /* memset ( buf, 0, sizeof( buf) ); /* zero buffer S5. */

   strcpy ( buf, "Message from client to server" );
   write ( sock, buf, sizeof(buf) );

   /* Now read message sent back by server.    */

   if ( ( cnt = read (sock, buf, sizeof(buf) ) ) < 0 ) {
      printf("network client socket read failure &d\n", errno);
      perror("network client");
      close(sock);
      exit(5);
   }
   else
   printf("network client received the message %s\n", buf);

   /* Now send a message with 0 bytes. */

   bzero  ( buf,    sizeof( buf) );  /* zero buffer, BSD. */
   /* memset ( buf, 0, sizeof( buf) ); /* zero buffer S5. */
   write ( sock, buf, 0 );
   close (sock);
   exit(0);

}  /* end of main procedure */