Implement Server #1
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,4 +5,6 @@ compile_commands.json
|
||||
site
|
||||
libflint.so
|
||||
test
|
||||
tcptest
|
||||
testrunner
|
||||
.idea
|
||||
|
@ -1,41 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
project(flint C)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
include_directories(include)
|
||||
|
||||
set(SOURCES
|
||||
src/linkedlist.c
|
||||
src/set.c
|
||||
src/stack.c
|
||||
src/binarytree.c
|
||||
src/input.c
|
||||
src/math.c
|
||||
src/string.c
|
||||
src/vector.c
|
||||
src/utility.c
|
||||
src/crypto.c
|
||||
src/parsing.c
|
||||
)
|
||||
|
||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin"))
|
||||
add_library(flint ${SOURCES} src/macos/macos.c)
|
||||
else()
|
||||
add_library(flint ${SOURCES})
|
||||
endif()
|
||||
|
||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
|
||||
target_link_libraries(flint bsd)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_PROJECT_NAME} STREQUAL flint)
|
||||
add_executable(tests tests/tests.c)
|
||||
target_include_directories(tests PRIVATE include)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(tests flint bsd)
|
||||
else()
|
||||
target_link_libraries(tests flint)
|
||||
endif()
|
||||
endif()
|
19
Makefile
19
Makefile
@ -1,6 +1,7 @@
|
||||
.PHONY : clean
|
||||
.PHONY : clean tests all
|
||||
|
||||
CFLAGS = -std=c99 -Iinclude -pedantic -Wall -Wextra
|
||||
CFLAGS = -std=c99 -Iinclude -pedantic
|
||||
WARNINGS= -Wall -Wextra
|
||||
LDFLAGS = -fPIC -shared
|
||||
|
||||
TARGET = libflint.so
|
||||
@ -11,9 +12,10 @@ PREFIX = $(DESTDIR)/usr/local
|
||||
LIBDIR = $(PREFIX)/lib
|
||||
|
||||
all: $(TARGET)
|
||||
./clanggen.sh
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
cc $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJ)
|
||||
cc $(CFLAGS) $(WARNINGS) $(LDFLAGS) -o $(TARGET) $(OBJ)
|
||||
|
||||
./obj/%.o: ./src/%.c
|
||||
cc $(CFLAGS) -c $< -o $@
|
||||
@ -26,7 +28,12 @@ uninstall:
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
rm -f test
|
||||
rm -f testrunner
|
||||
rm -f tcptest
|
||||
rm -f compile_commands.json
|
||||
|
||||
tests:
|
||||
cc $(CFLAGS) -o testrunner tests/tests.c src/*.c
|
||||
cc $(CFLAGS) -o tcptest tests/tcptest.c src/*.c
|
||||
./run_tests.sh
|
||||
|
||||
test:
|
||||
cc $(CFLAGS) -o test tests/tests.c src/*.c
|
||||
|
10
build.sh
10
build.sh
@ -1,10 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# For building outside of CLion
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
cp compile_commands.json ..
|
||||
cd
|
17
clanggen.sh
Executable file
17
clanggen.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -e
|
||||
|
||||
CFLAGS="-I./include -I/usr/local/include"
|
||||
|
||||
rm -f compile_commands.json
|
||||
|
||||
for f in $(find ./src -type f | grep -v macos); do
|
||||
n=$(echo $f | grep -o '[^/]*$' | sed 's/c$/json/')
|
||||
cc -MJ $n $CFLAGS -c $f
|
||||
done
|
||||
|
||||
rm *.o
|
||||
sed -e '1s/^/[/' -e '$s/,$/]/' *.json > compile_commands.out
|
||||
rm *.json
|
||||
mv compile_commands.out compile_commands.json
|
28
include/lfnetwork.h
Normal file
28
include/lfnetwork.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef LIBFLINT_NET_H
|
||||
#define LIBFLINT_NET_H
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
typedef enum ServerType {
|
||||
SERVERTYPE_TCP,
|
||||
SERVERTYPE_UDP
|
||||
} ServerType;
|
||||
|
||||
typedef struct Server {
|
||||
ServerType server_type;
|
||||
int fd;
|
||||
int port;
|
||||
void (*handler)(struct Server *s);
|
||||
} Server;
|
||||
|
||||
#define DEFAULT_BACKLOG 10
|
||||
|
||||
Server *new_server(ServerType type, const char *port, void(handler)(Server *s));
|
||||
void delete_server(Server *s);
|
||||
int serve(Server *s, int backlog_size);
|
||||
void *get_in_addr(struct sockaddr *sa);
|
||||
|
||||
// Example handlers
|
||||
void handler_tcp_echo(Server *s);
|
||||
|
||||
#endif //LIBFLINT_NET_H
|
12
run_tests.sh
Executable file
12
run_tests.sh
Executable file
@ -0,0 +1,12 @@
|
||||
set -e
|
||||
|
||||
#./testrunner
|
||||
./tcptest &
|
||||
tcpout=$(echo "hello" | nc localhost 18632)
|
||||
echo "tcpout: $tcpout"
|
||||
if [ "$tcpout" != "TEST SEND" ]; then
|
||||
echo "Error: \"$tcpout\" != \"TEST SEND\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
130
src/network.c
Normal file
130
src/network.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "lfnetwork.h"
|
||||
|
||||
static void sighandler(int s) {
|
||||
int saved_errno = errno;
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void *get_in_addr(struct sockaddr *sa)
|
||||
{
|
||||
if (sa->sa_family == AF_INET) {
|
||||
return &(((struct sockaddr_in*)sa)->sin_addr);
|
||||
}
|
||||
|
||||
return &(((struct sockaddr_in6*)sa)->sin6_addr);
|
||||
}
|
||||
|
||||
Server *new_server(ServerType type, const char *port, void(handler)(Server *s)) {
|
||||
Server *s = (Server *)malloc(sizeof(Server));
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
s->server_type = type;
|
||||
s->handler = handler;
|
||||
|
||||
struct addrinfo *addr = NULL;
|
||||
if (getaddrinfo(NULL, port, NULL, &addr) != 0) {
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
s->port = (int)strtol(port, NULL, 10);
|
||||
|
||||
int socktype = 0;
|
||||
switch (type) {
|
||||
case SERVERTYPE_TCP:
|
||||
socktype = SOCK_STREAM;
|
||||
break;
|
||||
case SERVERTYPE_UDP:
|
||||
socktype = SOCK_DGRAM;
|
||||
break;
|
||||
}
|
||||
|
||||
struct addrinfo *p;
|
||||
for (p = addr; p != NULL; p = p->ai_next) {
|
||||
s->fd = socket(AF_INET, socktype, 0);
|
||||
if (s->fd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind(s->fd, p->ai_addr, p->ai_addrlen) != 0) {
|
||||
close(s->fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "Failed to bind\n");
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
freeaddrinfo(addr);
|
||||
return s;
|
||||
}
|
||||
|
||||
void delete_server(Server *s) {
|
||||
free(s);
|
||||
s = NULL;
|
||||
}
|
||||
|
||||
int serve(Server *s, int backlog_size) {
|
||||
if (listen(s->fd, backlog_size) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sighandler;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
|
||||
fprintf(stderr, "Failed to set sigaction\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Server is waiting for connections on port %d...\n", s->port);
|
||||
s->handler(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handler_tcp_echo(Server *s) {
|
||||
struct sockaddr_storage client_addr;
|
||||
while (1) {
|
||||
socklen_t client_addr_sz = sizeof(client_addr);
|
||||
int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz);
|
||||
if (new_fd == -1) {
|
||||
printf("failed to accept. Errno: %d\n", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
char buf[33];
|
||||
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32);
|
||||
printf("Received connection from %s\n", buf);
|
||||
|
||||
// Start child process
|
||||
if (!fork()) {
|
||||
close(s->fd); // Child doesn't need the initial socket
|
||||
if (send(new_fd, "Hello, world!", 13, 0) == -1) {
|
||||
fprintf(stderr, "Failed to send hello world\n");
|
||||
}
|
||||
close(new_fd);
|
||||
_exit(0);
|
||||
}
|
||||
// End child process
|
||||
|
||||
close(new_fd); // new_fd is not used by the parent
|
||||
}
|
||||
}
|
34
tests/tcptest.c
Normal file
34
tests/tcptest.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lfnetwork.h"
|
||||
|
||||
void test_handler(Server *s) {
|
||||
struct sockaddr_storage client_addr;
|
||||
socklen_t client_addr_sz = sizeof(client_addr);
|
||||
int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz);
|
||||
if (new_fd == -1) {
|
||||
printf("failed to accept. Errno: %d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[33];
|
||||
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32);
|
||||
printf("Received connection from %s\n", buf);
|
||||
|
||||
if (send(new_fd, "TEST SEND", 10, 0) == -1) {
|
||||
printf("Failed to send hello world. Errno: %d\n", errno);
|
||||
}
|
||||
close(new_fd);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Server *server = new_server(SERVERTYPE_TCP, "18632", test_handler);
|
||||
serve(server, DEFAULT_BACKLOG);
|
||||
delete_server(server);
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lflinkedlist.h"
|
||||
#include "lfnetwork.h"
|
||||
#include "lfset.h"
|
||||
#include "lfstack.h"
|
||||
#include "lfbinarytree.h"
|
||||
@ -426,4 +427,4 @@ int main() {
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user