Implement the following functions:
MHD_start_daemon
MHD_stop_daemon
You will also need to start writing the MHD_Daemon
struct. Recall that MHD_Daemon
is defined
in microhttpd.h
as a forward definition. You'll need to
fill it out to hold the data necessary for running an instance of a
web server.
First, go to the daemon.c
and take a look at the
definition of this function. The function takes 6 arguments. Yes
6!:
struct MHD_Daemon * MHD_start_daemon(unsigned int options,
unsigned short port,
MHD_AcceptPolicyCallback apc,
void * apc_cls,
MHD_AccessHandlerCallback dh,
void * dh_cls)
The meaning of the arguments are as follows:
options
: A set of options passed to determine how the
web server is configured. Look at microhttpd.h
and
locate the MHD_OPTION enumeration. This lists the possible options
that might be passed to the web server.port
: The port number that the web server will listen
to for connections.apc
: A pointer to a function that decides if a
connection will be accepted or not. The library user writes this
function and passes it to the web server. Before we accept a
connection, the library calls this function which returns MHD_YES,
meaning to accept the connection, or MHD_NO, meaning to reject the
connection.apc_cls
: A void *
argument that is
passed as the first argument to apc
, the
AcceptPolicyCallback function. Typically, a library user will point
to some data structure that holds the information they need to
decide whether or not to accept a connection.dh
: A pointer to a function that handles a client
request. The library user writes this function and passes it to the
web server on creation. When data comes in on a socket, we parse it
using the HTTP protocol, determine what method was used, and call
this function so that the user of the library can handle it.dh_cls
: A void *
argument that is passed
as the first argument to dh
, the
AccessHandlerCallback. As with apc_cls, this typically points to
some structure that stores information on behalf of the library
user.MHD_start_daemon
are
values that must be stored with the instance of the web server. Thus,
at minimum these will be stored in your MHD_Daemon
structure.
You should notice from the function definition
that MHD_start_daemon
returns a pointer to
a MHD_Daemon
structure. This should be a hint that in
this function, you will have to allocate and return an MHD_Daemon
structure. How do you allocate in C? While I'm sure you have done
this before, here's an example:
struct MHD_Daemon *daemon = (struct MHD_Daemon *) malloc(sizeof(struct MHD_Daemon));
The call to malloc
needs to know the number of bytes
you need to allocate from the operating system. sizeof
will give you the size of something in
bytes. However, malloc
returns a void *
,
so we have to typecast this to a struct MHD_Daemon
*
. One should note that malloc
does not
initialize the memory allocated in any way. One could
use calloc
, for example, to allocate the memory and
then set all bytes in that memory chunk to 0. However, if you are
filling out the values of the MHD_Daemon struct, there's not really
a need to clear everything out first.
Besides just allocating the MHD structure and returning a pointer to it, you also need to write your first socket code. Recall from the socket tutorial that to write a server we have to to the following:
int socket(int domain, int type, int
protocol)
to create a socket from the operating
system. This function returns the socket. To create a TCP socket,
we pass (AF_INET, SOCK_STREAM, 0)
as the parameters
to socket
.int bind(int s, const struct sockaddr* name,
socklen_t namelen)
. Prior to this you have to create
an address structure that indicates what port you want to listen
on.int listen(int s, int
backlog)
. At this point, your socket will actually start
accepting connections to it. The first argument is your socket, the
second argument is the number of TCP connections to queue. Thus, the
socket will not complete the '3-way' handshake that TCP begins with,
but it will queue up new TCP connections until you dequeue them with
a call to accept
. listen
takes an argument
that determines how many new connections can be queued up before
they are rejected. If this value is too small, clients will be
rejected before you can dequeue them with accept
. A
value too large will use up system resources needlessly.
You should not return from MHD_start_daemon
until all of
these steps have been taken. In addition, from these calls you can see
that you will need to add some additional data to
your MHD_Daemon
struct (Hint: You need to store the
socket you listen with somewhere!).
In MHD_stop_daemon
you simply need to close
or shutdown
. Typically, when using TCP sockets, we
call shutdown
so we can determine if any remaining data
can be sent or received before the socket is closed. For example, we
might call shutdown(listenfd, SHUT_RDWR)
which would prevent any further reads or writes on the socket.