Basics Of SOCKET

What Is a Socket?

A socket is a communication mechanism that allows client/server systems to be developed either locally, on a single machine, or across networks. Linux functions such as printing and network utilities such as rlogin and ftp usually use sockets to communicate. Sockets are created and used differently from pipes because they make a clear distinction between client
and server. The socket mechanism can implement multiple clients attached to a single server.

Socket Connections


You can think of socket connections as telephone calls into a busy building. A call comes into an organi- zation and is answered by a receptionist who puts the call through to the correct department (the server process) and from there to the right person (the server socket). Each incoming call (client) is routed to an appropriate end point and the intervening operators are free to deal with further calls. Before you look at the way socket connections are established in Linux systems, you need to understand how they operate for socket applications that maintain a connection.
First of all, a server application creates a socket, which like a file descriptor is a resource assigned to the server process and that process alone. The server creates it using the system call socket, and it can’t be shared with other processes.
Next, the server process gives the socket a name. Local sockets are given a filename in the Linux file sys- tem, often to be found in /tmp or /usr/tmp. For network sockets, the filename will be a service identifier (port number/access point) relevant to the particular network to which the clients can connect. This identifier allows Linux to route incoming connections specifying a particular port number to the correct server process. A socket is named using the system call bind. The server process then waits for a client to connect to the named socket. The system call, listen, creates a queue for incoming connections. The server can accept them using the system call accept.
When the server calls accept, a new socket is created that is distinct from the named socket. This new socket is used solely for communication with this particular client. The named socket remains for further connections from other clients. If the server is written appropriately, it can take advantage of multiple connections. For a simple server, further clients wait on the listen queue until the server is ready again. The client side of a socket-based system is more straightforward. The client creates an unnamed socket by calling socket. It then calls connect to establish a connection with the server by using the server’s named socket as an address.
Once established, sockets can be used like low-level file descriptors, providing two-way data communications.







Socket Attributes

 
To fully understand the system calls used in this example, you need to learn a little about UNIX networking.
Sockets are characterized by three attributes: domain, type, and protocol. They also have an address used as their name. The formats of the addresses vary depending on the domain, also known as the protocol family. Each protocol family can use one or more address families to define the address format.

Socket Domains

 
Domains specify the network medium that the socket communication will use. The most common socket domain is AF_INET, which refers to Internet networking that’s used on many Linux local area networks and, of course, the Internet itself. The underlying protocol, Internet Protocol (IP), which only has one address family, imposes a particular way of specifying computers on a network. This is called the IP address.
Although names almost always refer to networked machines on the Internet, these are translated into lower-level IP addresses. An example IP address is 192.168.1.99. All IP addresses are represented by four numbers, each less than 256, a so-called dotted quad. When a client connects across a network via sockets, it needs the IP address of the server computer.
There may be several services available at the server computer. A client can address a particular service on a networked machine by using an IP port. A port is identified within the system by assigning a unique 16-bit integer and externally by the combination of IP address and port number. The sockets are communication end points that must be bound to ports before communication is possible.
Servers wait for connections on particular ports. Well-known services have allocated port numbers that are used by all Linux and UNIX machines. These are usually, but not always, numbers less than 1024. Examples are the printer spooler (515), rlogin (513), ftp (21), and httpd (80). The last of these is the server for the World Wide Web (WWW). Usually, port numbers less than 1024 are reserved for system services and may only be served by processes with superuser privileges. X/Open defines a constant in netdb.h, IPPORT_RESERVED, to stand for the highest reserved port number. Because there is a standard set of port numbers for standard services, computers can easily connect to each other without having to establish the correct port. Local services may use nonstandard port addresses. The domain in our first example is the UNIX file system domain, AF_UNIX, which can be used by sockets based on a single computer that perhaps isn’t networked. When this is so, the underlying protocol is file input/output and the addresses are absolute filenames. The address that you used for the server socket was server_socket, which you saw appear in the current directory when you ran the server application.
Other domains that might be used include AF_ISO for networks based on ISO standard protocols and AF_XNS for the Xerox Network System. We won’t cover these here.

Socket Types


A socket domain may have a number of different ways of communicating, each of which might have different characteristics. This isn’t an issue with AF_UNIX domain sockets, which provide a reliable two- way communication path. In networked domains, however, you need to be aware of the characteristics of the underlying network. Internet protocols provide two distinct levels of service: streams and datagrams.

Stream Sockets


Stream sockets (in some ways similar to standard input/output streams)  provide a connection that is a sequenced and reliable two-way byte stream. Thus, data sent is guaranteed not to be lost, duplicated, or reordered without an indication that an error has occurred. Large messages are fragmented, transmitted, and reassembled. This is like a file stream, as it accepts large amounts of data and writes it to the low- level disk in smaller blocks. Stream sockets have predictable behavior.
Stream sockets, specified by the type SOCK_STREAM, are implemented in the AF_INET domain by TCP/IP connections. They are also the usual type in the AF_UNIX domain. We’ll concentrate on SOCK_STREAM sockets in this chapter because they are more commonly used in programming network
applications.
TCP/IP stands for Transmission Control Protocol/Internet Protocol. IP is the low-level protocol for packets that provides routing through the network from one computer to another. TCP provides sequencing, flow control, and retransmission to ensure that large data transfers arrive with all of the data present and correct or with an appropriate error condition reported.
 

Datagram Sockets

In contrast, a datagram socket, specified by the type SOCK_DGRAM, doesn’t establish and maintain a connection. There is also a limit on the size of a datagram that can be sent. It’s transmitted as a single network message that may get lost, duplicated, or arrive out of sequence—ahead of datagrams sent after it.
Datagram sockets are implemented in the AF_INET domain by UDP/IP connections and provide anunsequenced, unreliable service. However, they are relatively inexpensive in terms of resources, since network connections need not be maintained. They’re fast because there is no associated connection setup time. UDP stands for User Datagram Protocol. Datagrams are useful for “single-shot” inquiries to information services, for providing regular status information, or for performing low-priority logging. They have the advantage that the death of a server doesn’t necessarily require a client restart. Because datagram-based servers usually retain no connection
information, they can be stopped and restarted without unduly disturbing their clients. For now, we leave the topic of datagrams; see the “Datagrams” section near the end of this chapter for more information on datagrams.

Shared Memory



Shared memory allows two unrelated processes to access the same logical memory. Shared memory is a very efficient way of transferring data between two running processes. Although the X/Open standard doesn’t require it, it’s probable that most implementations of shared memory arrange for the memory being shared between different processes to be the same physical memory.
Shared memory provides an efficient way of sharing and passing data between multiple processes. Since it provides no synchronization facilities, we usually need to use some other mechanism to synchronize access to the shared memory. Typically, we might use shared memory to provide efficient access to large areas of memory and pass small messages to synchronize access to that memory.
Shared memory is a special range of addresses that is created by IPC for one process and appears in the address space of that process. Other processes can then “attach” the same shared memory segment into their own address space. All processes can access the memory locations just as if the memory had been allocated by malloc. If one process writes to the shared memory, the changes immediately become visible to any other process that has access to the same shared memory.

The functions for shared memory resemble those for semaphores:

#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);

As with semaphores, the include files sys/types.h and sys/ipc.h are normally also required before shm.h is included.

shmget
We create shared memory using the shmget function:
int shmget(key_t key, size_t size, int shmflg);
As with semaphores, the program provides key, which effectively names the shared memory segment, and the shmget function returns a shared memory identifier that is used in subsequent shared memory functions. There’s a special key value, IPC_PRIVATE, that creates shared memory private to the process. You wouldn’t normally use this value, and, as with semaphores, you may find the private shared memory is not actually private on many Linux systems. The second parameter, size, specifies the amount of memory required in bytes. The third parameter, shmflg, consists of nine permission flags that are used in the same way as the mode flags for creating files. Aspecial bit defined by IPC_CREAT must be bitwise ORed with the permissions to create a new shared memory segment. It’s not an error to have the IPC_CREAT flag set and pass the key of an existing shared memory segment. The IPC_CREAT flag is silently ignored if it is not required. The permission flags are very useful with shared memory because they allow a process to create shared memory that can be written by processes owned by the creator of the shared memory but only read by processes that other users have created. We can use this to provide efficient read-only access to data by placing it in shared memory without the risk of its being changed by other users. If the shared memory is successfully created, shmget returns a nonnegative integer, the shared memory identifier. On failure, it returns –1.

shmat
When we first create a shared memory segment, it’s not accessible by any process. To enable access to the shared memory, we must attach it to the address space of a process. We do this with the shmat function:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
The first parameter, shm_id, is the shared memory identifier returned from shmget. The second parameter, shm_addr, is the address at which the shared memory is to be attached to the current process. This should almost always be a null pointer, which allows the system to choose the address at which the memory appears. The third parameter, shmflg, is a set of bitwise flags. The two possible values are SHM_RND, which, in conjunction with shm_addr, controls the address at which the shared memory is attached, and SHM_RDONLY, which makes the attached memory read-only. It’s very rare to need to control the address at which shared memory is attached; you should normally allow the system to choose an address for you, as doing otherwise will make the application highly hardware-dependent. If the shmat call is successful, it returns a pointer to the first byte of shared memory. On failure –1 is returned.
The shared memory will have read or write access depending on the owner (the creator of the shared memory), the permissions, and the owner of the current process. Permissions on shared memory are similar to the permissions on files. An exception to this rule arises if shmflg & SHM_RDONLY is true. Then the shared memory won’t be writable, even if permissions would have allowed write access.

shmdt
The shmdt function detaches the shared memory from the current process. It takes a pointer to the address returned by shmat. On success, it returns 0, on error –1. Note that detaching the shared memory doesn’t delete it; it just makes that memory unavailable to the current process.
shmctl
The control functions for shared memory are (thankfully) somewhat simpler than the more complex ones for semaphores:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
The shmid_ds structure has at least the following members:
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
The first parameter, shm_id, is the identifier returned from shmget.

The first parameter, shm_id, is the identifier returned from shmget. The second parameter, command, is the action to take. It can take three values:

Command Description
IPC_STAT Sets the data in the shmid_ds structure to reflect the values associated with the shared memory.
IPC_SET Sets the values associated with the shared memory to those provided in the shmid_ds data structure, if the process has permission to do so.
IPC_RMID Deletes the shared memory segment.

The third parameter, buf, is a pointer to structure containing the modes and permissions for the shared memory.

On success, it returns 0, on failure, –1. X/Open doesn’t specify what happens if you attempt to delete a shared memory segment while it’s attached. Generally, a shared memory segment that is attached but deleted continues to function until it has been detached from the last process. However, because this behavior isn’t specified, it’s best not to rely on it.