Skip to content

Instantly share code, notes, and snippets.

@3Hren
Last active April 21, 2023 09:58
Show Gist options
  • Save 3Hren/8c729b857664da874626e558586150d7 to your computer and use it in GitHub Desktop.
Save 3Hren/8c729b857664da874626e558586150d7 to your computer and use it in GitHub Desktop.
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
const char RESP[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\n\r\n";
int server() {
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
return 1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0
addr.sin_port = htons(1337); // 32768-60999
int rc = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if (rc != 0) {
close(fd);
return 1;
}
struct sockaddr_in l_addr = {0};
int l_addr_len = sizeof(l_addr);
getsockname(fd, (struct sockaddr*)&l_addr, &l_addr_len);
printf("bind address: %s:%d\n", inet_ntoa(l_addr.sin_addr), (int)ntohs(l_addr.sin_port));
rc = listen(fd, 16);
if (rc != 0) {
close(fd);
return 1;
}
char buf[512];
for (;;) {
printf("ready for accept\n");
struct sockaddr_in r_addr = {0};
int r_addr_len = sizeof(r_addr);
int c_fd = accept(fd, (struct sockaddr*)&r_addr, &r_addr_len);
printf("accepted connection from: %s:%d\n", inet_ntoa(r_addr.sin_addr), (int)ntohs(r_addr.sin_port));
int n_read = recv(c_fd, &buf, sizeof(buf), 0);
if (n_read == 0) {
printf("closed from remote\n");
close(c_fd);
break;
}
int n_sent = send(c_fd, &RESP, sizeof(RESP), 0);
// EINPROGRESS | EAGAIN
printf("sent %d bytes\n", n_sent);
close(c_fd);
}
close(fd);
return 0;
}
int client() {
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
return 1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1 | 127 0 0 1 | 0x7f, 0x00, 0x00, 0x01
addr.sin_port = htons(1337); // 32768-60999
int rc = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (rc == -1) {
close(fd);
return 1;
}
printf("CONNECTED\n");
send(fd, "Hi", 3, 0);
return 0;
}
int epoll_server() {
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd == -1) {
return 1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(1337);
int rc = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if (rc != 0) {
return 1;
}
struct sockaddr_in l_addr;
socklen_t l_addr_len = sizeof(l_addr);
getsockname(fd, (struct sockaddr*)&l_addr, &l_addr_len);
printf("bind address: %s:%d\n", inet_ntoa(l_addr.sin_addr), (int)ntohs(l_addr.sin_port));
rc = listen(fd, 16);
if (rc != 0) {
return 1;
}
int efd = epoll_create1(0);
if (efd == -1) {
return 1;
}
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
rc = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event);
if (rc == -1) {
return 1;
}
struct epoll_event *events = calloc(1024, sizeof event);
for (;;) {
int n = epoll_wait(efd, events, 1024, -1);
for (int i = 0; i < n; i++) {
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
close (events[i].data.fd);
continue;
} else if (fd == events[i].data.fd) {
// We have a notification on the listening socket, which means one or more incoming connections.
while (1) {
struct sockaddr in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept4(fd, &in_addr, &in_len, SOCK_NONBLOCK);
if (infd == -1) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
// It's ok.
break;
} else {
// Not OK :(
break;
}
}
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
rc = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
if (rc == -1) {
return 1;
}
}
} else {
int done = 0;
while (1) {
char buf[512];
int n_read = recv(events[i].data.fd, buf, sizeof buf, 0);
if (n_read == -1) {
if (errno != EAGAIN) {
done = 1;
}
break;
} else if (n_read == 0) {
// EOF.
done = 1;
break;
}
int n_sent = send(events[i].data.fd, RESP, sizeof(RESP), 0);
if (n_sent == -1) {
if (errno != EAGAIN) {
done = 1;
}
break;
}
done = 1;
break;
}
if (done) {
close(events[i].data.fd);
}
}
}
}
return 0;
}
int main(int argc, char** argv) {
if (argc == 1) {
return server();
} else if (argc == 2) {
return epoll_server();
} else {
return client();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment