Topic : BSD Sockets
Author : Unknown
Page : << Previous 2  Next >>
Go to page :


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 <sys/types.h>
#include <sys/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 <stdio.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 <sys/types.h>
#include <sys/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 <sys/types.h>
#include <sys/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 server can handle multiple client requests for connection and service.
The server responds to any one client's request independently of all other clients.
A client knows how to establish a connection with the server.
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:


The data transmitted has no boundaries.
All bytes in a received message must be read before the next message can be processed.
Bytes in a received message can be read in a loop program control structure since no data bytes are discarded.
The server will usually fork() a child process upon establishment of a client connection.
This child server process is designed to communicate with exactly one client process.
The child server process performs the requested service for its connected client.
The child server process terminates when the service request has been completed.
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 <sys/types.h>
#include <sys/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 <sys/types.h>
#include <sys/socket.h>

int sockfd; /* socket file descriptor */

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






Connection Establishment by Server - accept()
#include <sys/types.h>
#include <sys/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 <sys/types.h>
#include <sys/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 <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/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);
    


Page : << Previous 2  Next >>