initial hashmap code

This commit is contained in:
2024-11-09 19:58:58 +01:00
parent 2f39ca6fc1
commit 51a309e55f
16 changed files with 172 additions and 87 deletions

View File

@@ -1,3 +1,5 @@
BasedOnStyle: Google
ColumnLimit: 120
UseTab: Always UseTab: Always
IndentWidth: 8 IndentWidth: 8
TabWidth: 8 TabWidth: 8

View File

@@ -1,4 +1,4 @@
{ {
"clangd.path": "/usr/bin/clangd", "clangd.path": "/usr/bin/clangd",
"clangd.arguments": [ "--header-insertion=never" ] "clangd.arguments": [ "--header-insertion=never" ],
} }

View File

@@ -1,5 +1,5 @@
# cws # cws
A simple Web Server written in C (learning purposes), it works only on Linux systems A simple Web Server written in C (learning purposes), it works only on Linux systems.
## Requirements ## Requirements
- [meson](https://mesonbuild.com/index.html) - [meson](https://mesonbuild.com/index.html)

View File

@@ -2,16 +2,16 @@
#define __CLIENT_H__ #define __CLIENT_H__
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
int test_client_connection(const char *hostname, const char *port); int test_client_connection(const char *hostname, const char *service);
#endif #endif

16
include/hashmap.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __HASHMAP_C__
#define __HASHMAP_C__
#include <sys/socket.h>
#define HASHMAP_MAX_ITEMS 10000
struct hashmap {
int sockfd;
struct sockaddr_storage sas;
};
int hash(int sockfd);
void hm_insert(struct hashmap *map, int sockfd, struct sockaddr_storage *sas);
#endif

View File

@@ -2,18 +2,17 @@
#define __SERVER_H__ #define __SERVER_H__
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <fcntl.h> #include <sys/socket.h>
#include <errno.h> #include <sys/types.h>
#include <unistd.h>
#define PORT 3030 #define PORT 3030
#define BACKLOG 10 #define BACKLOG 10
@@ -24,7 +23,9 @@ int start_server(const char *hostname, const char *service);
void setup_hints(struct addrinfo *hints, size_t len, const char *hostname); void setup_hints(struct addrinfo *hints, size_t len, const char *hostname);
void handle_clients(int sockfd); void handle_clients(int sockfd);
void epoll_ctl_add(int epfd, int sockfd, uint32_t events); void epoll_ctl_add(int epfd, int sockfd, uint32_t events);
void epoll_ctl_del(int epfd, int sockfd);
void setnonblocking(int sockfd); void setnonblocking(int sockfd);
void handle_new_client(int sockfd); int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size);
void print_client_ip(struct sockaddr_in *sin);
#endif #endif

View File

@@ -4,12 +4,11 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
// print every IPs of a hostname // print every IPs of a hostname
void print_ips(const char *hostname, const char *port); void print_ips(const char *hostname, const char *port);

View File

@@ -51,7 +51,7 @@ It is similar to `poll()` but more efficient when dealing with lots of fds. The
int epoll_create1(int flags); int epoll_create1(int flags);
``` ```
Just pass 0 for the `flags`, it is an improved version of the `epoll_create()`. It creates a new epoll instance nad returns the fd of that instance. Just pass 0 for the `flags`, it is an improved version of the `epoll_create()`. It creates a new epoll instance and returns the fd of that instance.
```c ```c
#include <sys/epoll.h> #include <sys/epoll.h>

19
notes/hash-table.md Normal file
View File

@@ -0,0 +1,19 @@
# Hash Table
Well, the moment has come. I never made a Hash Map algorithm, but in this scenario I have to save both fd and sockaddr (I could make a simply linked list, but it's a way to learn new things).
Let's start with a little bit of theory.
A *Hash Table* is a data structure also called **dictionary** or **map**. It maps *keys* to *values* thanks to a **hash function** that computes and *index* (**hash code**) into an array of **buckets**.
One problem could be the *hash collision* where the hash function computes the same index for different values. A fix could be the **chaining** method, where for the same hash code you can make a linked list and append the index. Then the lookup function will go through the list and find the key.
In a Hash Map you can insert, delete and lookup (simply search).
### Hash function
The easiest way... don't judge me.
- Integer keys:
$$ hash(\text{key}) = \text{key} \mod \text{table\_dim} $$
- String keys:
$$ hash(key) = \sum_{i=0}^{len(key) - 1} ascii\_value(key[i]) * prime\_number$$

11
notes/http-request.md Normal file
View File

@@ -0,0 +1,11 @@
# HTTP Request
This is an example of a basic HTTP request made from the browser:
```bash
GET / HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
```

View File

@@ -1,9 +1,10 @@
// THIS FILE IS ONLY A TEST FOR THE BASIC STUFF // THIS FILE IS ONLY A TEST FOR THE BASIC STUFF
#include "client.h" #include "client.h"
#include "colors.h" #include "colors.h"
int test_client_connection(const char *hostname, const char *port) { int test_client_connection(const char *hostname, const char *service) {
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *res; struct addrinfo *res;
@@ -11,10 +12,9 @@ int test_client_connection(const char *hostname, const char *port) {
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
int status = getaddrinfo(hostname, port, &hints, &res); int status = getaddrinfo(hostname, service, &hints, &res);
if (status != 0) { if (status != 0) {
fprintf(stderr, RED BOLD "[client] getaddrinfo(): %s\n" RESET, fprintf(stderr, RED BOLD "[client] getaddrinfo(): %s\n" RESET, gai_strerror(status));
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -22,15 +22,17 @@ int test_client_connection(const char *hostname, const char *port) {
status = connect(sockfd, res->ai_addr, res->ai_addrlen); status = connect(sockfd, res->ai_addr, res->ai_addrlen);
if (status != 0) { if (status != 0) {
fprintf(stderr, RED BOLD "[client] connect(): %s\n" RESET, fprintf(stderr, RED BOLD "[client] connect(): %s\n" RESET, strerror(errno));
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
char buf[4096]; char buf[4096];
int bytes_read = recv(sockfd, buf, 4096, 0); int bytes_read = recv(sockfd, buf, sizeof buf, 0);
fprintf(stdout, BLUE "[client] Read %d bytes: %s\n" RESET, bytes_read, fprintf(stdout, BLUE "[client] Read %d bytes: %s\n" RESET, bytes_read, buf);
buf);
fprintf(stdout, "[client] => ");
fgets(buf, sizeof buf, stdin);
send(sockfd, buf, strlen(buf), 0);
freeaddrinfo(res); freeaddrinfo(res);
close(sockfd); close(sockfd);

9
src/hashmap.c Normal file
View File

@@ -0,0 +1,9 @@
#include "hashmap.h"
int hash(int sockfd) { return sockfd % HASHMAP_MAX_ITEMS; }
void hm_insert(struct hashmap *map, int sockfd, struct sockaddr_storage *sas) {
int index = hash(sockfd);
map[index].sockfd = sockfd;
map[index].sas = *sas;
}

View File

@@ -1,4 +1,5 @@
#include "main.h" #include "main.h"
#include "colors.h" #include "colors.h"
#include "server.h" #include "server.h"

View File

@@ -5,7 +5,7 @@
int main(int argc, char **argv) { int main(int argc, char **argv) {
fprintf(stdout, BOLD GREEN "[client] Running cws...\n" RESET); fprintf(stdout, BOLD GREEN "[client] Running cws...\n" RESET);
int ret = test_client_connection("localhost", "3030"); int ret = test_client_connection(argv[1], argv[2]);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, BOLD RED "Unable to start client\n"); fprintf(stderr, BOLD RED "Unable to start client\n");
} }

View File

@@ -1,4 +1,5 @@
#include "server.h" #include "server.h"
#include "colors.h" #include "colors.h"
int start_server(const char *hostname, const char *service) { int start_server(const char *hostname, const char *service) {
@@ -9,25 +10,22 @@ int start_server(const char *hostname, const char *service) {
int status = getaddrinfo(hostname, service, &hints, &res); int status = getaddrinfo(hostname, service, &hints, &res);
if (status != 0) { if (status != 0) {
fprintf(stderr, fprintf(stderr, RED BOLD "[server] getaddrinfo() error: %s\n" RESET, gai_strerror(status));
RED BOLD "[server] getaddrinfo() error: %s\n" RESET,
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
fprintf(stdout, YELLOW "[server] sockfd: %d\n" RESET, sockfd);
status = bind(sockfd, res->ai_addr, res->ai_addrlen); status = bind(sockfd, res->ai_addr, res->ai_addrlen);
if (status != 0) { if (status != 0) {
fprintf(stderr, RED BOLD "[server] bind(): %s\n" RESET, fprintf(stderr, RED BOLD "[server] bind(): %s\n" RESET, gai_strerror(status));
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
status = listen(sockfd, BACKLOG); status = listen(sockfd, BACKLOG);
if (status != 0) { if (status != 0) {
fprintf(stderr, RED BOLD "[server] listen(): %s\n" RESET, fprintf(stderr, RED BOLD "[server] listen(): %s\n" RESET, gai_strerror(status));
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -56,55 +54,56 @@ void handle_clients(int sockfd) {
setnonblocking(sockfd); setnonblocking(sockfd);
epoll_ctl_add(epfd, sockfd, EPOLLIN | EPOLLET); epoll_ctl_add(epfd, sockfd, EPOLLIN | EPOLLET);
struct epoll_event *revents = struct epoll_event *revents = malloc(EPOLL_MAXEVENTS * sizeof(struct epoll_event));
malloc(EPOLL_MAXEVENTS * sizeof(struct epoll_event));
int nfds; int nfds;
char *msg = "Hello there!"; char *msg = "Hello there!";
size_t msg_len = strlen(msg); size_t msg_len = strlen(msg);
char data[4096];
int client_fd;
int run = 1;
for (;;) { while (run) {
nfds = nfds = epoll_wait(epfd, revents, EPOLL_MAXEVENTS, EPOLL_TIMEOUT);
epoll_wait(epfd, revents, EPOLL_MAXEVENTS, EPOLL_TIMEOUT);
for (int i = 0; i < nfds; ++i) { for (int i = 0; i < nfds; ++i) {
if (revents[i].data.fd == sockfd) { if (revents[i].data.fd == sockfd) {
int client_fd = // new client
accept(sockfd, (struct sockaddr *)&their_sa, client_fd = handle_new_client(sockfd, &their_sa, &theirsa_size);
&theirsa_size); setnonblocking(client_fd);
epoll_ctl_add(epfd, client_fd, EPOLLIN);
if (client_fd == -1) { print_client_ip((struct sockaddr_in *)&their_sa);
if (errno != EWOULDBLOCK) {
fprintf(stderr, int bytes_sent = send(client_fd, msg, msg_len, 0);
RED BOLD "[server] " fprintf(stdout, BLUE "[server] Sent %d bytes\n" RESET, bytes_sent);
"accept(): " } else {
"%s\n" RESET, // incoming data
strerror(errno)); client_fd = revents[i].data.fd;
} // fprintf(stdout, "[%d] EPOLLIN\n", client_fd);
int bytes_read = recv(client_fd, data, sizeof data, 0);
if (bytes_read == 0) {
// client disconnect
fprintf(stdout, BLUE "[server] Client disconnection\n");
epoll_ctl_del(epfd, client_fd);
close(client_fd);
continue; continue;
} }
setnonblocking(client_fd); data[strlen(data) - 1] = '\0';
fprintf(stdout, "[server] Bytes read (%d): %s\n", bytes_read, data);
if (strcmp(data, "stop") == 0) {
fprintf(stdout, GREEN BOLD "[server] Stopping...\n" RESET);
run = 0;
break;
}
}
}
}
struct sockaddr_in *client = free(revents);
(struct sockaddr_in *)&their_sa; close(epfd);
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client->sin_addr, client_ip,
INET_ADDRSTRLEN);
fprintf(stdout,
BLUE "[server] Incoming "
"client, ip: %s\n" RESET,
client_ip);
int bytes_sent =
send(client_fd, msg, msg_len, 0);
fprintf(stdout,
BLUE "[server] Sent %d bytes\n" RESET,
bytes_sent);
close(client_fd);
}
}
}
} }
void epoll_ctl_add(int epfd, int sockfd, uint32_t events) { void epoll_ctl_add(int epfd, int sockfd, uint32_t events) {
@@ -113,8 +112,15 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) {
event.data.fd = sockfd; event.data.fd = sockfd;
int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event); int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
if (status != 0) { if (status != 0) {
fprintf(stderr, RED BOLD "[server] epoll_ctl(): %s\n" RESET, fprintf(stderr, RED BOLD "[server] epoll_ctl_add(): %s\n" RESET, strerror(errno));
gai_strerror(status)); exit(EXIT_FAILURE);
}
}
void epoll_ctl_del(int epfd, int sockfd) {
int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
if (status != 0) {
fprintf(stdout, RED BOLD "[server] epoll_ctl_del(): %s\n" RESET, strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@@ -122,12 +128,35 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) {
void setnonblocking(int sockfd) { void setnonblocking(int sockfd) {
int status = fcntl(sockfd, F_SETFL, O_NONBLOCK); int status = fcntl(sockfd, F_SETFL, O_NONBLOCK);
if (status == -1) { if (status == -1) {
fprintf(stderr, RED BOLD "[server] fcntl(): %s\n" RESET, fprintf(stderr, RED BOLD "[server] fcntl(): %s\n" RESET, gai_strerror(status));
gai_strerror(status));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
void handle_new_client(int sockfd) { int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size) {
// handle here new clients int client_fd = accept(sockfd, (struct sockaddr *)their_sa, theirsa_size);
if (client_fd == -1) {
if (errno != EWOULDBLOCK) {
fprintf(stderr,
RED BOLD
"[server] "
"accept(): "
"%s\n" RESET,
strerror(errno));
}
return -1;
}
return client_fd;
}
void print_client_ip(struct sockaddr_in *sin) {
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN);
fprintf(stdout,
BLUE
"[server] Incoming "
"client, IP: %s\n" RESET,
ip);
} }

View File

@@ -1,4 +1,5 @@
#include "utils.h" #include "utils.h"
#include "colors.h" #include "colors.h"
void print_ips(const char *hostname, const char *port) { void print_ips(const char *hostname, const char *port) {
@@ -12,8 +13,7 @@ void print_ips(const char *hostname, const char *port) {
int status = getaddrinfo(hostname, port, &ai, &res); int status = getaddrinfo(hostname, port, &ai, &res);
if (status < 0) { if (status < 0) {
fprintf(stderr, RED "getaddrinfo(): %s\n" RESET, fprintf(stderr, RED "getaddrinfo(): %s\n" RESET, gai_strerror(status));
gai_strerror(status));
exit(1); exit(1);
} }
@@ -22,16 +22,12 @@ void print_ips(const char *hostname, const char *port) {
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET) { if (p->ai_family == AF_INET) {
struct sockaddr_in *sin = struct sockaddr_in *sin = (struct sockaddr_in *)p->ai_addr;
(struct sockaddr_in *)p->ai_addr; inet_ntop(AF_INET, &sin->sin_addr, ipv4, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &sin->sin_addr, ipv4,
INET_ADDRSTRLEN);
fprintf(stdout, BLUE "%s\n" RESET, ipv4); fprintf(stdout, BLUE "%s\n" RESET, ipv4);
} else if (p->ai_family == AF_INET6) { } else if (p->ai_family == AF_INET6) {
struct sockaddr_in6 *sin6 = struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)p->ai_addr;
(struct sockaddr_in6 *)p->ai_addr; inet_ntop(AF_INET6, &sin6->sin6_addr, ipv6, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &sin6->sin6_addr, ipv6,
INET6_ADDRSTRLEN);
fprintf(stdout, BLUE "%s\n" RESET, ipv6); fprintf(stdout, BLUE "%s\n" RESET, ipv6);
} }
} }