Last active
April 21, 2023 09:58
-
-
Save 3Hren/8c729b857664da874626e558586150d7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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