Creating a socket
The first thing to do is to create a socket which
you can then manipulate in many ways. To create a socket, you can use the
socket() function.
HEADER FILE:
#include <sys/socket.h>
Syntax:
int socket(int af, int type, int protocol);
int socket_desc;
socket_desc=socket(AF_INET,SOCK_STREAM,0);
if
(socket_desc==-1)
perror("Create socket");
socket() returns a socket
descriptor which can be used in other network commands.
This will create a socket which uses DARPA Internet addresses, and the method
of connection is a byte stream
which is similar to a pipe. An alternative to byte streams is a datagram but
this tutorial does not cover them.
With servers, the first socket created is
often known as a " master socket". Before the socket can
send or receive data, it must be connected to another socket. If acting as a
master socket, it must be bound to a port
number so that clients can know where to "find" the socket and
connect to it.
If successful, socket() returns a valid socket descriptor;
otherwise it returns -1 and sets errno to indicate the error. perror() or
strerror() can be used to turn the errno value into a human readable string.
Binding a socket to a port
To set up a master socket, you need to bind the
socket descriptor to a in many ways. To create a socket, you can use the
socket() function as described above in Creating a socket.
HEADER:
#include <sys/socket.h>
#include <netinet/in.h>
Syntax:
int bind(int s, struct sockaddr *addr, int
addrlen);
struct sockaddr_in address;
/* type of socket created in socket() */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
/* 7000 is the port to use for connections */
address.sin_port = htons(7000);
/* bind the socket to the port specified above */
bind(socket_desc,(struct sockaddr *)&address,sizeof(address));
If successful, bind() returns 0; otherwise it
returns -1 and sets errno to indicate the error.
The port specified in the
source code above (port 7000) is where the server can be connected to.
Listening for connections
Before any connections can be accepted, the
socket must be told to listen for connections and also the maximum number of
pending connections using listen()
HEADER:
#include <sys/socket.h>
Syntax:
int listen(int s, int backlog);
listen(socket_desc,3);
the above line specifies that there can be up to
3 connections pending. If a connection request arrives when there are already 3
connections pending, the client receives a timeout error.
If successful,
listen() returns 0; otherwise it returns -1 and sets errno to indicate the
error.
Accepting a connection
To actually tell the server to accept a
connection, you have to use the function accept()
HEADER:
#include <sys/socket.h>
Syntax:
int accept(int s, struct sockaddr *addr, int
*addrlen);
int addrlen;
struct sockaddr_in address;
addrlen =
sizeof(struct sockaddr_in);
new_socket = accept(socket_desc, (struct sockaddr *)&address,
&addrlen);
if
(new_socket<0)
perror("Accept connection");
accept() is used with connection based sockets
such as streams. The parameters are the socket descriptor of the master socket
followed by a sockaddr_in structure and the size of the structure. If
successful, accept() returns a positive integer which is the socket descriptor
for the accepted socket. If an error occurs, -1 is returned and errno is set to
indicate the cause.
Closing connections
Probably one of the easiest things to do with a
socket, is close it. This is done using close()
HEADER:
#include <unistd.h>
Syntax:
int close(int sockdes);
close(new_socket);
close() closes the socket descriptor indicated by
sockdes.
Upon successful completion, close() returns a value of 0; otherwise,
it returns -1 and sets errno to indicate the error.
Sending data to a connection
Accepting a connection would not be any use without
the means to send or receive data. Send without receive could be used for an
information server which always returns a fixed message.
HEADER:
#include <sys/socket.h>
#include <string.h>
Syntax:
int send(int s, const void *msg, int len, int
flags);
char *message="This is a message to
send\n\r";
send(new_socket,message,strlen(message),0);
The message should have \n\r instead of just \n
or \r because otherwise the text which appears on some clients may seem
strange. e.g the text with just a \n would appear as follows:
This is a message
and this is the second line
and
the third.
instead of:
This is a message
and this is the second line
and the third.
send() is used to transmit a message to another
socket and can be used only when the socket is in a connected state. The socket
descriptor that specifies the socket on which the message will be sent is 's'
in the syntax above. 'msg' points to the buffer containing the message and the
length of the message is given by len, in bytes.
The supported values for
flags are zero, or MSG_OOB (to
send out-of-band data) - a write() call made to a socket behaves in exactly the
same way as send() with flags set to zero.
Upon successful completion, send()
returns the number of bytes sent. Otherwise, it returns -1 and sets errno to
indicate a locally-detected error.
Receiving data from a connection
Accepting a connection would not be any use without
the means to send or receive data. Receive only could be used as a data
collection method.
HEADER:
#include <sys/socket.h>
Syntax:
int recv(int s, void *msg, int len, int flags);
int bufsize=1024; /* a 1K buffer */
char *buffer=malloc(bufsize);
recv(new_socket,buffer,bufsize,0);
The flags parameter can be set to MSG_PEEK, MSG_OOB,
both, or zero. If it is set to MSG_PEEK, any data returned to the user still is
treated as if it had not been read, i.e the next recv() re-reads the same data.
A read() call made to a socket behaves in exactly the same way as a recv()
with flags set to zero.
If successful, recv() returns the number of bytes
received, otherwise, it returns -1 and sets errno to indicate the error. recv()
returns 0 if the socket is blocking and the connection to the remote node
failed.
Setting socket options
To allow certain socket operations requires
manipulation of socket options using setsockopt()
HEADER:
#include <sys/socket.h>
Syntax:
int
setsockopt(int s, int level, int optname,
const void *optval, int optlen);
#define TRUE
1
#define FALSE
0
int socket_desc; /* master socket returned by socket() */
int opt=TRUE; /* option is to be on/TRUE or off/FALSE
*/
setsockopt(socket_desc,SOL_SOCKET,SO_REUSEADDR,
(char *)&opt,sizeof(opt));
SOL_SOCKET specifies the option is a `socket
level' option, these are defined in <sys/socket.h>
The
socket is identified by the socket
descriptor s.
The option SO_REUSEADDR is
only valid for AF_INET sockets.
There are two kinds of options: boolean and
non-boolean. Boolean options are either set or not set and also can use optval
and optlen to pass information. Non-boolean options always use optval and
optlen to pass information.
Handling more than one connection
To enable a socket to be read without waiting if
there is no input, the socket must be set non-blocking
using the following snippet of code.
fcntl(mastersocket, F_SETFL, FNDELAY);
or
fcntl(mastersocket, F_SETFL, O_NONBLOCK);
If the above returns a non-zero result, the
operation failed and errno should be set to an appropriate value.
Using select to monitor a number of sockets (or
just one) is fairly straightforward and is shown in the code below. Please note
this is incomplete code as the creation of the master socket is not included
(see previous details).
fd_set readfds;
/* create a list of sockets to check for activity
*/
FD_ZERO(&readfds);
/* specify mastersocket - ie listen for new
connections */
FD_SET(mastersocket, &readfds);
/* wait for connection, forever if have to */
new_conns=select(max_conns, readfds, NULL, NULL,
NULL);
if ((new_conns<0) && (errno!=EINTR)) {
/* there
was an error with select() */
}
if (FD_ISSET(mastersocket,&readfds)) {
/* Open
the new socket */
}
Of course, the above will only wait for activity
on the master socket. What you need to do is run it inside a loop which repeats
until the server is shut down.
Any newly created sockets will need to be
monitored as well (unless the connections accepted are closed after outputing a
message).
Converting a hostname into a network address
HEADER:
#include <netdb.h>
Syntax:
struct
hostent *gethostbyname(const char *name);
struct
hostent *hent;
hent =
gethostbyname("www.foobar.net");
A hostent structure:
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
Some of the network functions require a structure
containing the network address and sometimes the port number to connect to or
from. The easiest way to convert a hostname to a network address is to use the gethostbyname()
function.
gethostbyname() returns a structure of type
hostent - this structure contains the name of the host, an array of alternative
names and also an array of network addresses (in network byte order).
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.