Topic : BSD Sockets
Author : Unknown
Page : 1 Next >>
Go to page :


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 <sys/types.h>
#include <sys/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:


AF_UNIX for communication between processes on one system;
AF_INET for communication between processes on the same or different systems using the DARPA standard protocols (IP/UDP/TCP).
Socket type refers to the "style" of communication. The two most commonly used values include:

SOCK_STREAM: A stream of data with no record boundaries. Delivery in a networked environment is guaranteed; if delivery is impossible, the sender receives an error indicator.
SOCK_DGRAM: A stream of records, each of a given size. Delivery in a networked environment is not guaranteed.
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 <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.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 <sys/types.h>
#include <sys/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 <sys/types.h>
#include <sys/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 <sys/types.h>
#include <sys/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

Page : 1 Next >>