Socket Programming..
Stream sockets (SOCK_STREAM) provide a reliable, bidirectional, byte-stream communication
channel.
Datagram sockets (SOCK_DGRAM) allow data to be exchanged in the form of messages
called datagrams.
Socket I/O can be performed using the conventional read() and write() system calls,
or using a range of socket-specific system calls (e.g., send(), recv(), sendto(), and
recvfrom()). By default, these system calls block if the I/O operation can’t be completed
immediately. Nonblocking I/O is also possible, by using the fcntl() F_SETFL
operation (Section 5.3) to enable the O_NONBLOCK open file status flag.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Returns file descriptor on success, or –1 on error
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Returns 0 on success, or –1 on error
#include <sys/socket.h>
int listen(int sockfd, int backlog);
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The key point to understand about accept() is that it creates a new socket, and it is
this new socket that is connected to the peer socket that performed the connect(). A
file descriptor for the connected socket is returned as the function result of the
accept() call.
Upon return from accept(), this integer is set to indicate the number of bytes of data
actually copied into the buffer.
If we are not interested in the address of the peer socket, then addr and addrlen
should be specified as NULL and 0, respectively. (If desired, we can retrieve the peer’s
address later using the getpeername() system call.
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr {
sa_family_t sa_family; /* Address family (AF_* constant) */
char sa_data[14]; /* Socket address (size varies according to socket domain) */
};
Stream Sockets
The operation of stream sockets can be explained by analogy with the telephone
system:
1. The socket() system call, which creates a socket, is the equivalent of installing a
telephone. In order for two applications to communicate, each of them must
create a socket.
2. Communication via a stream socket is analogous to a telephone call. One application
must connect its socket to another application’s socket before communication
can take place. Two sockets are connected as follows:
a) One application calls bind() in order to bind the socket to a well-known
address, and then calls listen() to notify the kernel of its willingness to
accept incoming connections. This step is analogous to having a known
telephone number and ensuring that our telephone is turned on so that
people can call us.
b) The other application establishes the connection by calling connect(), specifying
the address of the socket to which the connection is to be made. This
is analogous to dialing someone’s telephone number.
c) The application that called listen() then accepts the connection using accept().
This is analogous to picking up the telephone when it rings. If the accept() is
performed before the peer application calls connect(), then the accept() blocks
(“waiting by the telephone”).
3. Once a connection has been established, data can be transmitted in both directions
between the applications (analogous to a two-way telephone conversation)
until one of them closes the connection using close(). Communication is performed
using the conventional read() and write() system calls or via a number of socketspecific
system calls (such as send() and recv()) that provide additional functionality.

Datagram Sockets
The operation of datagram sockets can be explained by analogy with the postal system:
1. The socket() system call is the equivalent of setting up a mailbox. (Here, we
assume a system like the rural postal service in some countries, which both
picks up letters from and delivers letters to the mailbox.) Each application that
wants to send or receive datagrams creates a datagram socket using socket().
2. In order to allow another application to send it datagrams (letters), an application
uses bind() to bind its socket to a well-known address. Typically, a server
binds its socket to a well-known address, and a client initiates communication
by sending a datagram to that address. (In some domains—notably the UNIX
domain—the client may also need to use bind() to assign an address to its socket
if it wants to receive datagrams sent by the server.)
3. To send a datagram, an application calls sendto(), which takes as one of its arguments
the address of the socket to which the datagram is to be sent. This is
analogous to putting the recipient’s address on a letter and posting it.
4. In order to receive a datagram, an application calls recvfrom(), which may block
if no datagram has yet arrived. Because recvfrom() allows us to obtain the
address of the sender, we can send a reply if desired. (This is useful if the
sender’s socket is bound to an address that is not well known, which is typical
of a client.) Here, we stretch the analogy a little, since there is no requirement
that a posted letter is marked with the sender’s address.
5. When the socket is no longer needed, the application closes it using close().
Just as with the postal system, when multiple datagrams (letters) are sent from one
address to another, there is no guarantee that they will arrive in the order they
were sent, or even arrive at all. Datagrams add one further possibility not present
in the postal system: since the underlying networking protocols may sometimes
retransmit a data packet, the same datagram could arrive more than once.

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
Returns number of bytes received, 0 on EOF, or –1 on error
ssize_t sendto(int sockfd, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
Returns number of bytes sent, or –1 on error
The src_addr and addrlen arguments are used to obtain or specify the address of
the peer socket with which we are communicating.
For recvfrom(), the src_addr and addrlen arguments return the address of the
remote socket used to send the datagram. (These arguments are analogous to the addr
and addrlen arguments of accept(), which return the address of a connecting peer
socket.) The src_addr argument is a pointer to an address structure appropriate to
the communication domain. As with accept(), addrlen is a value-result argument.
Prior to the call, addrlen should be initialized to the size of the structure pointed to by
src_addr; upon return, it contains the number of bytes actually written to this structure.
Regardless of the value specified for length, recvfrom() retrieves exactly one message
from a datagram socket. If the size of that message exceeds length bytes, the
message is silently truncated to length bytes.
If we employ the recvmsg() system call (Section 61.13.2), then it is possible to
find out about a truncated datagram via the MSG_TRUNC flag returned in the
msg_flags field of the returned msghdr structure. See the recvmsg(2) manual
page for details.
Using connect() with Datagram Sockets
Even though datagram sockets are connectionless, the connect() system call serves a
purpose when applied to datagram sockets. Calling connect() on a datagram socket
causes the kernel to record a particular address as this socket’s peer. The term
connected datagram socket is applied to such a socket. The term unconnected datagram
socket is applied to a datagram socket on which connect() has not been called (i.e.,
the default for a new datagram socket).
After a datagram socket has been connected:
Datagrams can be sent through the socket using write() (or send()) and are automatically
sent to the same peer socket. As with sendto(), each write() call results
in a separate datagram.
Only datagrams sent by the peer socket may be read on the socket.
The obvious advantage of setting the peer for a datagram socket is that we can use
simpler I/O system calls when transmitting data on the socket. We no longer need
to use sendto() with dest_addr and addrlen arguments, but can instead use write(). Setting
the peer is useful primarily in an application that needs to send multiple datagrams
to a single peer (which is typical of some datagram clients).
Stream sockets (SOCK_STREAM) provide a reliable, bidirectional, byte-stream communication
channel.
Datagram sockets (SOCK_DGRAM) allow data to be exchanged in the form of messages
called datagrams.
Socket I/O can be performed using the conventional read() and write() system calls,
or using a range of socket-specific system calls (e.g., send(), recv(), sendto(), and
recvfrom()). By default, these system calls block if the I/O operation can’t be completed
immediately. Nonblocking I/O is also possible, by using the fcntl() F_SETFL
operation (Section 5.3) to enable the O_NONBLOCK open file status flag.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Returns file descriptor on success, or –1 on error
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Returns 0 on success, or –1 on error
#include <sys/socket.h>
int listen(int sockfd, int backlog);
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The key point to understand about accept() is that it creates a new socket, and it is
this new socket that is connected to the peer socket that performed the connect(). A
file descriptor for the connected socket is returned as the function result of the
accept() call.
Upon return from accept(), this integer is set to indicate the number of bytes of data
actually copied into the buffer.
If we are not interested in the address of the peer socket, then addr and addrlen
should be specified as NULL and 0, respectively. (If desired, we can retrieve the peer’s
address later using the getpeername() system call.
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr {
sa_family_t sa_family; /* Address family (AF_* constant) */
char sa_data[14]; /* Socket address (size varies according to socket domain) */
};
Stream Sockets
The operation of stream sockets can be explained by analogy with the telephone
system:
1. The socket() system call, which creates a socket, is the equivalent of installing a
telephone. In order for two applications to communicate, each of them must
create a socket.
2. Communication via a stream socket is analogous to a telephone call. One application
must connect its socket to another application’s socket before communication
can take place. Two sockets are connected as follows:
a) One application calls bind() in order to bind the socket to a well-known
address, and then calls listen() to notify the kernel of its willingness to
accept incoming connections. This step is analogous to having a known
telephone number and ensuring that our telephone is turned on so that
people can call us.
b) The other application establishes the connection by calling connect(), specifying
the address of the socket to which the connection is to be made. This
is analogous to dialing someone’s telephone number.
c) The application that called listen() then accepts the connection using accept().
This is analogous to picking up the telephone when it rings. If the accept() is
performed before the peer application calls connect(), then the accept() blocks
(“waiting by the telephone”).
3. Once a connection has been established, data can be transmitted in both directions
between the applications (analogous to a two-way telephone conversation)
until one of them closes the connection using close(). Communication is performed
using the conventional read() and write() system calls or via a number of socketspecific
system calls (such as send() and recv()) that provide additional functionality.
Datagram Sockets
The operation of datagram sockets can be explained by analogy with the postal system:
1. The socket() system call is the equivalent of setting up a mailbox. (Here, we
assume a system like the rural postal service in some countries, which both
picks up letters from and delivers letters to the mailbox.) Each application that
wants to send or receive datagrams creates a datagram socket using socket().
2. In order to allow another application to send it datagrams (letters), an application
uses bind() to bind its socket to a well-known address. Typically, a server
binds its socket to a well-known address, and a client initiates communication
by sending a datagram to that address. (In some domains—notably the UNIX
domain—the client may also need to use bind() to assign an address to its socket
if it wants to receive datagrams sent by the server.)
3. To send a datagram, an application calls sendto(), which takes as one of its arguments
the address of the socket to which the datagram is to be sent. This is
analogous to putting the recipient’s address on a letter and posting it.
4. In order to receive a datagram, an application calls recvfrom(), which may block
if no datagram has yet arrived. Because recvfrom() allows us to obtain the
address of the sender, we can send a reply if desired. (This is useful if the
sender’s socket is bound to an address that is not well known, which is typical
of a client.) Here, we stretch the analogy a little, since there is no requirement
that a posted letter is marked with the sender’s address.
5. When the socket is no longer needed, the application closes it using close().
Just as with the postal system, when multiple datagrams (letters) are sent from one
address to another, there is no guarantee that they will arrive in the order they
were sent, or even arrive at all. Datagrams add one further possibility not present
in the postal system: since the underlying networking protocols may sometimes
retransmit a data packet, the same datagram could arrive more than once.
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
Returns number of bytes received, 0 on EOF, or –1 on error
ssize_t sendto(int sockfd, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
Returns number of bytes sent, or –1 on error
The src_addr and addrlen arguments are used to obtain or specify the address of
the peer socket with which we are communicating.
For recvfrom(), the src_addr and addrlen arguments return the address of the
remote socket used to send the datagram. (These arguments are analogous to the addr
and addrlen arguments of accept(), which return the address of a connecting peer
socket.) The src_addr argument is a pointer to an address structure appropriate to
the communication domain. As with accept(), addrlen is a value-result argument.
Prior to the call, addrlen should be initialized to the size of the structure pointed to by
src_addr; upon return, it contains the number of bytes actually written to this structure.
Regardless of the value specified for length, recvfrom() retrieves exactly one message
from a datagram socket. If the size of that message exceeds length bytes, the
message is silently truncated to length bytes.
If we employ the recvmsg() system call (Section 61.13.2), then it is possible to
find out about a truncated datagram via the MSG_TRUNC flag returned in the
msg_flags field of the returned msghdr structure. See the recvmsg(2) manual
page for details.
Using connect() with Datagram Sockets
Even though datagram sockets are connectionless, the connect() system call serves a
purpose when applied to datagram sockets. Calling connect() on a datagram socket
causes the kernel to record a particular address as this socket’s peer. The term
connected datagram socket is applied to such a socket. The term unconnected datagram
socket is applied to a datagram socket on which connect() has not been called (i.e.,
the default for a new datagram socket).
After a datagram socket has been connected:
Datagrams can be sent through the socket using write() (or send()) and are automatically
sent to the same peer socket. As with sendto(), each write() call results
in a separate datagram.
Only datagrams sent by the peer socket may be read on the socket.
The obvious advantage of setting the peer for a datagram socket is that we can use
simpler I/O system calls when transmitting data on the socket. We no longer need
to use sendto() with dest_addr and addrlen arguments, but can instead use write(). Setting
the peer is useful primarily in an application that needs to send multiple datagrams
to a single peer (which is typical of some datagram clients).
Comments
Post a Comment