Links Powered by MicMac
Cocoa-C-Porting
Fink's translation team
Mac OSX, Fink and Unix
Client/Server Architecture
PowerPC Architecture - UML
System programming
Merise
Pierre-Loïc and Pascaline's wedding

Programming system 12

Last modified: 25/01/2005 22:29:32 CET
Author: Michèle Garoche contact

Contents - Process scheduling - Fork and Wait - Pthread and Exec - Exec - Pipes - Message queues - Memory management - Paging and segmentation - Synchronization (1) - Synchronization (2) - File System Management - Socket Programming (1) - Revision

Programming System

Socket programming

Tutorial class by Philippe Décogné

25/01/2003 (dd/mm/yy)

Exercise 1

Creating and binding a socket

Give the definition of socket and the format of the primitive which creates it.

Which are the processes that can use the socket after its creation?

Which is the primitive that binds a networking address to a socket?

Socket's definition

It is a bidirectional communication mean between processes on independent machines.

The socket is represented by a special file which serves as communication endpoint for a process to send and receive information.

An inode is associated with each socket. The socket's descriptor is similar to a file descriptor (entry in the descriptor table).

Caption

• table des descripteurs du processus: process descriptors table

• table des fichiers ouverts: open files table

• table des i-noeuds: inodes table

Progsys 12-01

Primitive for creating a socket

Socket: int socket(int domain, int type, int protocol);

Creates a communication endpoint and returns a descriptor.

The parameter domain specifies the communications domain which communication will take place in. It is bound to the protocols family used for the communication (see sys/socket.h for families definition). The most used domains are:

- AF_LOCAL (previously AF_UNIX) for local communications

- AF_INET for remote communications

The parameter type specifies the type of communications. It indicates the sequencing, the reliability, the connection mode, and the encoding. The most commonly used type are the following:

- SOCK_STREAM for sequenced, reliable two-way full-duplex connection, in connected state, based on bytes streams.

- SOCK_DGRAM for unreliable, connectionless connection, based on datagrams of a fixed length (typically small messages).

The parameter protocol indicates the type of protocol to be used on the socket. Generally a unique protocol handles a given socket type, but it may happen that several protocols handle the same given socket type, in which case the parameter protocol indicates the desired protocol (see /etc/protocols for protocols definition and netinet/in.h for parameters IPPROTO). Most commonly used specifications:

- 0: the system chooses the default protocol

- TCP with SOCK_STREAM

- UDP with SOCK_DGRAM

- IPPROTO_UDP: UDP within AF_INET domain

- IPPROTO_TCP: TCP within AF_INET domain

On success, returns the socket's descriptor, otherwise -1.

Libraries to be used: sys/types.h and sys/socket.h.

Man page: socket(2) - system calls

Communication management

The socket's inode catalogs both pending and running queues.

Caption

• I-noeud: inode

• Protocole: protocol

• État: state

• Connexions en attente: pending connections

• Connexions en cours: running connections

• Données à émettre: pending outcoming messages

• Données à recevoir: pending incoming messages

Progsys 12-02

Processes that can use the socket

As the socket is known through a file descriptor, the processes that can use it are the following:

- the socket's creator

- any child by duplicating the process environment

Binding a socket to a networking address

The primitive bind allows to bind a socket to a networking address.

Primitive to be used

Bind: int bind(int s, const struct sockaddr *name, int namelen);

Assigns the name name of length namelen to an anonymous socket with descriptor s (the socket created by the primitive socket() is an anonymous socket).

Returns 0 on success, otherwise -1.

Within the UNIX domain the name is a full path to a file. Within the INET domain the name matches an IP address.

name is a pointer to the socket's address. The address structure depends on the socket's domain.

Address structure within AF_UNIX domain

	struct sockaddr-un {
		uint8_t sun_len;		// structure length
		sa_family_t sun_family; // AF_UNIX
		char sun_path[104];		// reference
	}		

The parameter sun_path should be a character string ending with \0.

Socket's iPv4 address structure

	struct sockaddr-in { 
		uint8_t sin_len;		  // structure length
		sa_family_t sin_family;   // AF_INET
		in_port_t sin_port;	 	  //  TCP or UDP port number
		struct in_addr sin_addr;  // machine's IPv4 address structure
		char sin-zero[8];	 	  // padding
	}

The parameter sin_len does not exist on all systems (it exists on Mac OS X), when it exists, it is used by the kernel for routed sockets.

Machine's IPv4 address structure

	struct in-addr { 
		in_addr_t s_addr; 
	}

Socket's IPv6 address structure

	struct sockaddr-in6 { 
		uint8_t sin6_len;		   // structure length
		sa_family_t sin6_family;   // AF_INET6
		in_port_t sin6_port;	   // transport layer's port
		uint32_t sin6_flowinfo	   // priority & flow label
		struct in6_addr sin6_addr; // machine's IPv6 address structure
		char sin-zero[8];	 	   // padding
	}

Machine's IPv6 address structure

	struct in6-addr { 
		uint8_t s6_addr[16]; 
	}

Libraries to be used: sys/types.h and sys/socket.h.

Man page: bind(2) - system calls

Example: creating a socket and binding it to a UNIX address

Primitives to be used

Unlink: int unlink(const char *path);

Removes the link path from the directory where it is located and decrements the link count of the file referenced by the link.

If the link count reaches zero and there is no other process that uses the file, the the file is closed. If there is a process which uses the file when the link count reaches zero, the link is removed, but the removal of the file is delayed till all processes which reference that file have closed it.

Returns 0 on success, otherwise -1

Library to be used: unistd.h.

Man page: unlink(2) - system calls

Bzero: void bzero(void *b, size_t len);

Write len zero bytes into the string b.

Library to be used: string.h.

Man page: bzero(3) - subroutines

Getsockname: int getsockname(int s, struct sockaddr *name, int *namelen);

Returns into name the current name of the socket s. The parameter namelen should be initialized to the length of the structure name. On return it will contain the length in bytes of the returned name.

Returns 0 on success, otherwise -1.

Library to be used: sys/socket.h.

Man page: getsockname(2) - system calls

Computing the address length

Progsys 12-03

Code

	#include <sys/types.h>
	#include <sys/socket.h>
	#include <sys/un.h>
	#include <netinet/in.h>
	#include <netdb.h>
	#include <stdio.h>
	#include <string.h>
	#include <unistd.h>
	#include <stdlib.h>

	int main(int argc, char **argv)
	{
		int sock1;
		int socklen;
		
		// Define the full pathname to socket name
		char pathname[] = "/tmp/foo.bar";
		struct sockaddr_un addr1;
		
		// Create the socket
		if ( (sock1 = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
		{
			perror("error socket");
			exit(EXIT_FAILURE);
		}
		
		// Delete the eventual link to pathname
		unlink(pathname);
		
		// Fill in the structure with zeros
		bzero(&addr1, sizeof(struct sockaddr_un));
		
		// Define the domain of the socket
		addr1.sun_family = AF_LOCAL;
		
		// Fill in the pathname member with given pathname
		strcpy(addr1.sun_path, pathname);
		
		// Compute the length of the path in the structure
		socklen = sizeof(struct sockaddr_un) -sizeof(addr1.sun_path) + 
				  strlen(addr1.sun_path);
		
		// Attach the address to the socket
		bind(sock1, (struct sockaddr_un *) &addr1, socklen);
		
		// Get the name of the socket
		getsockname(sock1, (struct sockaddr *) &addr1, &socklen);
		
		// Print it
		printf("Bound name: %s, returned len: %d\n", addr1.sun_path,
				socklen);
		
		// Remove the link
		unlink(pathname);
		
		// Exit
		exit(0);
	}

Caution: this program should handle errors on all primitives. One example is given with perror(). But it would be better to wrap each primitive inside a new function which handles the most relevant errors.

The program first deletes the eventual link to the socket path, as if a link already exists the bind primitive issues an error. Then the address structure is zeroed, so that we begin in a clean state. Then we define the family and the name, and finally we compute the address length.

Once the address structure is defined, it is possible to bind it to the socket, retrieve its name and the address length, and print them. Finally we should delete the link created previously to get out of the program in a clean state.

Networking address

Storage of networking addresses, and more generally storage of bytes, may be made locally in two ways depending on the system (some systems use both types of storage):

- big endian (used on Mac OS X)

- little endian

Big endian byte order vs little endian byte order for a 16-bit integer (2 bytes of 8 bits).

• Byte de plus fort poids: high-order byte

• Byte de plus faible poids: low-order byte

• Adresse: address

• Sens de l'adressage: increasing memory address

Progsys 12-04

For a simple application to retrieve the machine's byte ordering, see FindByteOrder for a version with a GUI and ByteOrder for a version without a GUI.

On the contrary, Internet protocols use always big endian byte ordering. Some primitives and structures use also big endian order, no matter which byte ordering is used on the host. Therefore we need primitives to convert between both types of bytes orders.

Primitives to be used

Inet_pton: int inet_pton(int af, const char *src, void *dst);

Converts an address in decimal format held in the character string src to an address in big endian network format which will be held in the dst structure.

Returns 1 if the address is valid for the protocols family af, 0 if the address is not valid for the family af, and -1 if some system error occurs.

Libraries to be used: sys/types.h, sys/socket.h, netinet/in.h, arpa/inet.h

Man page: inet_pton(3) - subroutines

Inet_ntoa: char * inet_ntoa(struct in_addr in);

Converts an address in big endian network format held in the structure in to an address in decimal format held in an ASCII character string.

Libraries to be used: sys/types.h, sys/socket.h, netinet/in.h, arpa/inet.h

Man page: inet_pton(3) - subroutines

Htons: uint16_t htons(uint16_t hostshort);

Converts a 16-bits value hostshort in host format to the same value in big endian network format.

On a Mac, the macro is defined as identity.

Library to be used: arpa/inet.h

Man page: byteorder(3) - subroutines

Htonl: uint32_t htonl(uint32_t hostlong);

Converts a 32-bits value hostlong in host format to the same value in big endian network format.

On a Mac, the macro is defined as identity.

Library to be used: arpa/inet.h

Man page: byteorder(3) - subroutines

For simple applications using those primitives, see TimeService and TimeService1 for versions with a GUI, and timeservice and timeservice1 for versions without a GUI.

Global error handing

Here we use the so called variadic functions, which can handle as many arguments as the caller passes, combined with the functions dedicated to printing character strings and which accept a variable number of arguments.

Basics

1 - We define a function that accepts a variable number of arguments using an ellipsis in the argument list:

void function(int b, ...)

2 - Inside this function we declare a variable argument list va_list

3 - We initialize a pointer on that list with:

va_start

4 - We call a function that use some printing primitives passing the argument variable list as parameter.

5 - On return we delete the pointer to the argument variable list with:

va_end

See The GNU C Library - Variadic Functions for further explanations and other usage of those functions.

Library to be used: starg.h.

Man page: stdarg(3) - subroutines

Primitives to be used

Vsnprintf: int vsnprintf(char *str, size_t size, const char *format, va_list ap);

Writes at most size - 1 characters in the buffer str using the arguments supplied by the list ap and converted to format format.

Returns the number of characters that would have been written if the buffer's size were unlimited. If the number of characters to be written is greater than the buffer's size, truncates the character string.

Library to be used: stdio.h.

Man page: printf(3) - subroutines

Snprintf: int snprintf(char *str, size_t size, const char *format, ...);

Writes at most -1 characters in the buffer str, the string is converted to the format format.

Returns the number of characters that would have been written if the buffer's size were unlimited. If the number of characters to be written is greater than the buffer's size, truncates the character string.

Library to be used: stdio.h.

Man page: printf(3) - subroutines

Example: creating and binding an Internet address

Primitives to be used

Strlen: size_t strlen(const char *s);

Returns the number of characters inside the string s, excluding the terminating NUL character.

Library to be used: string.h.

Man page: strlen(3) - subroutines

Strcat: char * strcat(char *s, const char *append);

Appends the string append to the end of the string s.

Returns the pointer s.

Library to be used: string.h.

Man page: strcat(3) - subroutines

Fflush: int fflush(FILE *stream);

Forces a write of all buffered data in the file stream.

Returns 0 on success, otherwise EOF.

Library to be used: stdio.h.

Man page: fflush(3) - subroutines

Fputs: int puts(const char *str, FILE *stream);

Writes the string str to stream.

Returns a positive integer on success, otherwise EOF.

Library to be used: stdio.h.

Man page: fputs(3) - subroutines

Bzero: void bzero(void *b, size_t len);

Writes len zero bytes in the string b.

Library to be used: string.h.

Man page: bzero(3) - subroutines

Getsockname: int getsockname(int s, struct sockaddr *name, int *namelen);

Returns into name the current name of the socket s. The parameter namelen should be initialized to the length of the structure name. On return it will contain the length in bytes of the returned name.

Returns 0 on success, otherwise -1.

Library to be used: sys/socket.h.

Man page: getsockname(2) - system calls

Code

	/* Includes */
	#include <stdio.h>
	#include <stdlib.h>
	#include <sys/socket.h>
	#include <string.h>
	#include <unistd.h>
	#include <sys/errno.h>
	#include <arpa/inet.h>
	#include <netinet/in.h>
	#include <stdarg.h>

	/* Defines */
	#define MAXLINE 1024 /* buffer size */
	#define SA struct sockaddr /* convenience to make the code easier to read */
	#define socklen_t unsigned int /* defined before the right type be defined */

	/* Function declarations */
	void err_doit(const char *fmt, va_list ap); /* to handle errors */
	void err_sys(const char *fmt, ...); /* to handle variadic functions */
	int Socket(int family, int type, int protocol); /* wrapper to socket */
	void Bind(int fd, const struct sockaddr *sa, socklen_t salen); /* wrapper to bind */
	void Close(int fd); /* wrapper to close */

	/* Main */
	int main(int argc, char **argv)
	{
		int listenfd;	/* active socket */
		int port_served = 9877;  /* bounded port */
		struct sockaddr_in servaddr; /* server's address structure */
		int socklen; /* length of address structure */

		/* Create an active TCP socket */
		listenfd = Socket(AF_INET, SOCK_STREAM, 0);

		/* Fill in the address structure with zeros */
		bzero(&servaddr, sizeof(servaddr));
	
		/* Set up the domain */
		servaddr.sin_family      = AF_INET;
	
		/* Set up the port */
		servaddr.sin_port        = htons(port_served);

		/* The system will choose the address at connect time.
			At bind time, the given address is 0.0.0.0 */
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		
		/* Attach the socket to the address */
		Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

		/* Computes the length of the address structure */
		socklen = sizeof(servaddr);
	
		/* Get the address */
		getsockname(listenfd, (SA *) &servaddr, &socklen);
	
		/* Print it */
		printf("The socket %d will be attached to port %d, bounded on name %s 
			with an address length of %d bits.\n", listenfd, port_served, 
			inet_ntoa(servaddr.sin_addr), socklen);
	
		/* Close the socket */
		Close(listenfd);	
	}

	/* Primitives with error handling */
	int Socket(int family, int type, int protocol)
	{
		int  n; /* for return */

		if ( (n = socket(family, type, protocol)) < 0)
		{
			err_sys("socket error");
		}

		return n;
	}

	void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
	{
		if (bind(fd, sa, salen) < 0)
		{
			err_sys("bind error");
		}
	}

	void Close(int fd)
	{
  		if (close(fd) == -1)
  		{
			err_sys("close error");
		}
	}

	void err_sys(const char *fmt, ...)
	{
		va_list ap;  /* list of variable arguments */

		/* Define the pointer to the list */
		va_start(ap, fmt);
		
		/* Branch to the effective error handler */
		err_doit(fmt, ap);
		
		/* Remove the pointer */
		va_end(ap);
		
		exit(EXIT_FAILURE);
	}

	void err_doit(const char *fmt, va_list ap)
	{
		int errno_save; /* to save the error number passed to the function */
		int n; /* buffer size holding the error message */
		char buf[MAXLINE + 1]; /* buffer to hold the error message */

		/* Save the error number passed to the function 
			before another error overrides it eventually */
		errno_save = errno;
		
		/* Put the message passed to the function into buf */
		vsnprintf(buf, MAXLINE, fmt, ap);
		
		/* Computes the buffer length */
		n = strlen(buf);

		/* Append the error message corresponding to the error number 
			to the buffer */		
		snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));

		/* Append the end-of-line character to the buffer */
		strcat(buf, "\n");

		/* Force a write of the buffer to standard output device */
		fflush(stdout);
		
		/* Put buffer into standard error device */
		fputs(buf, stderr);
		
		/* Force a write of the buffer to standard error device */
		fflush(stderr);

		return;
	}

We first create an active socket. Then we clean up the address structure. Thereafter, wed define the protocol family, the port and the address converting them to big endian format as needed.

Then we attach the address structure to the socket, retrieve the name and length of the address, and print them. Finally, we close the socket to get out of the program in a clean state.

Variant

In place of the generic address INADDR_ANY, you can use a given address as 127.0.0.1, or 192.168..., or your Apache server's address. In this case, replace the following line in the code:

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

by:

inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

Error test on bind

To check if an error occurs on bind due to address already in use, just:

1 - insert the following line just before the close call:

sleep(10);

2 - recompile the code with:

cc myprog.c -o myprog

3 - open two xterm or equivalent, type ./myprog in both of them, push the return key in both xterm (we have 10 seconds to do so).


Contents - Process scheduling - Fork and Wait - Pthread and Exec - Exec - Pipes - Message queues - Memory management - Paging and segmentation - Synchronization (1) - Synchronization (2) - File System Management - Socket Programming (1) - Revision

Powered by Apache/1.3.41 (Darwin) PHP/4.4.8 on Mac OS X bluefish distributed.net Cssed icon Conglomerate icon Valid HTML 4.0.1 Valid CSS
Local date (dd/mm/yyyy): 29/08/2008 06:08:03 CEST