HTTP Server in C
C, HTTP, Sockets
This article describes a simple http server socket in Linux.
Server Socket
The basic procedure:
- Create socket with
socket()
call bind()
this to an IP and port where it canlisten()
for connections, thenaccept()
connection andsend()
orreceive()
data to/from connected sockets
Note that if struct sockaddr_in serverAddress.sin_addr.s_addr
is set to INADDR_ANY the socket is bound
to all local interfaces. INADDR_ANY is a constant set to zero, defined in netinet/in.h
. This will correspond to an IP address of 0.0.0.0 in the standard IPv4 notation. Note that htonl(INADDR_LOOPBACK)
and inet_addr("127.0.0.1")
are functionally equivalent.
Simple Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h> // for getnameinfo()
// Usual socket headers
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SIZE 1024
#define BACKLOG 10 // Passed to listen()
void report(struct sockaddr_in *serverAddress);
void setHttpHeader(char httpHeader[])
{
// File object to return
FILE *htmlData = fopen("index.html", "r");
char line[100];
char responseData[8000];
while (fgets(line, 100, htmlData) != 0) {
strcat(responseData, line);
}
// char httpHeader[8000] = "HTTP/1.1 200 OK\r\n\n";
strcat(httpHeader, responseData);
}
int main(void)
{
char httpHeader[8000] = "HTTP/1.1 200 OK\r\n\n";
// Socket setup: creates an endpoint for communication, returns a descriptor
// -----------------------------------------------------------------------------------------------------------------
int serverSocket = socket(
AF_INET, // Domain: specifies protocol family
SOCK_STREAM, // Type: specifies communication semantics
0 // Protocol: 0 because there is a single protocol for the specified family
);
// Construct local address structure
// -----------------------------------------------------------------------------------------------------------------
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8001);
serverAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);//inet_addr("127.0.0.1");
// Bind socket to local address
// -----------------------------------------------------------------------------------------------------------------
// bind() assigns the address specified by serverAddress to the socket
// referred to by the file descriptor serverSocket.
bind(
serverSocket, // file descriptor referring to a socket
(struct sockaddr *) &serverAddress, // Address to be assigned to the socket
sizeof(serverAddress) // Size (bytes) of the address structure
);
// Mark socket to listen for incoming connections
// -----------------------------------------------------------------------------------------------------------------
int listening = listen(serverSocket, BACKLOG);
if (listening < 0) {
printf("Error: The server is not listening.\n");
return 1;
}
report(&serverAddress); // Custom report function
setHttpHeader(httpHeader); // Custom function to set header
int clientSocket;
// Wait for a connection, create a connected socket if a connection is pending
// -----------------------------------------------------------------------------------------------------------------
while(1) {
clientSocket = accept(serverSocket, NULL, NULL);
send(clientSocket, httpHeader, sizeof(httpHeader), 0);
close(clientSocket);
}
return 0;
}
void report(struct sockaddr_in *serverAddress)
{
char hostBuffer[INET6_ADDRSTRLEN];
char serviceBuffer[NI_MAXSERV]; // defined in `<netdb.h>`
socklen_t addr_len = sizeof(*serverAddress);
int err = getnameinfo(
(struct sockaddr *) serverAddress,
addr_len,
hostBuffer,
sizeof(hostBuffer),
serviceBuffer,
sizeof(serviceBuffer),
NI_NUMERICHOST
);
if (err != 0) {
printf("It's not working!!\n");
}
printf("\n\n\tServer listening on http://%s:%s\n", hostBuffer, serviceBuffer);
}
Protocol Family
When setting up a socket using socket()
the following protocols are available:
Name | Purpose | Man page |
---|---|---|
AF_UNIX , AF_LOCAL |
Local communication | man unix |
AF_INET |
IPv4 Internet protocols | man ip |
AF_INET6 |
IPv6 Internet protocols | man ipv6 |
AF_IPX |
IPX - Novell protocols | |
AF_NETLINK |
Kernel user interface device | man netlink |
AF_X25 |
ITU-T X.25 / ISO-8208 protocol | man x25 |
AF_AX25 |
Amateur radio AX.25 protocol | |
AF_ATMPVC |
Access to raw ATM PVCs | |
AF_APPLETALK |
AppleTalk | man ddp |
AF_PACKET |
Low level packet interface | man packet |
AF_ALG |
Interface to kernel crypto API |
You can acces this list by running man socket
.
Socket Type
The socket has the indicated type, which specifies the communication semantics. Currently defined types are:
Type | Description |
---|---|
SOCK_STREAM |
Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported. Used for TCP protocol. |
SOCK_DGRAM |
Supports datagrams - connectionless, unreliable messages of a fixed maximum length. Used for UDP protocol. |
SOCK_SEQPACKET |
Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call. |
SOCK_RAW |
Provides raw network protocol access. |
SOCK_RDM |
Provides a reliable datagram layer that does not guarantee ordering. |
SOCK_PACKET |
Obsolete and should not be used in new programs; see packet(7). |
Constructing a Local Address Structure
If serverAddress.sin_addr.s_addr
is set to INADDR_ANY
the socket is bound to all local interfaces. INADDR_ANY
is a constant set to zero, defined in netinet/in.h
. This will correspond to an IP address of 0.0.0.0 in the standard IPv4 notation. Note that htonl(INADDR_LOOPBACK)
and inet_addr("127.0.0.1")
are functionally equivalent.
Bind socket to local address
When a socket is created with socket(), it exists in a name space (address family) but has no address assigned to it. bind() assigns the address specified by serverAddress to the socket referred to by the file descriptor serverSocket. serverAddressLength specifies the size, in bytes, of the address structure pointed to by serverAddress.
This operation is known as “assigning a name to a socket”.
Connecting
The programme runs an infinite loop in which we wait and create a connected socket if a connection is pending.
The accept()
function gets the first connection request on the queue of pending connections for the listening socket (in this case denoted by serverSocket
). It then creates a new connected socket and returns a file descriptor referring to this socket. The newly created socket is NOT in a listening state. The original socket (serverSocket
) is unaffected by this call.
Using getnameinfo
Used in the report()
function
int getnameinfo(
const struct sockaddr *sa, // pointer to a generic socket address structure (of type sockaddr_in or sockaddr_in6)
socklen_t salen, // size of the above
char *host, // Caller allocated buffer, will receive host
socklen_t hostlen, // Size of above
char *serv, // Caller allocate buffer, will receive service name
socklen_t servlen, // Size of above
int flags);
References
comments powered by Disqus