Last active
July 4, 2016 08:01
-
-
Save akkyie/da0b8f4eb8e4a32cb449 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
// | |
// main.c | |
// (c) 2016 Akio Yasui | |
// | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netdb.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#define BUFFER_SIZE 1024 | |
void print_usage() { | |
const char usage[] = ( | |
"Usage: socketter server <PORT>\n" | |
" socketter client <HOSTNAME> <PORT>\n" | |
); | |
fprintf(stdout, "%s", usage); | |
} | |
// MARK: Server | |
int server(const char *port) { | |
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; | |
struct addrinfo *info0 = NULL; | |
int error = getaddrinfo(NULL, port, &hints, &info0); | |
if (error != 0) { | |
fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); | |
return -1; | |
} | |
for (struct addrinfo *info = info0; info != NULL; info = info->ai_next) { | |
int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); | |
if (fd < 0) { | |
continue; | |
} | |
#ifdef IPV6_V6ONLY | |
const int ON = 1; | |
if (info->ai_family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &ON, sizeof(ON)) != 0) { | |
close(fd); | |
continue; | |
} | |
#endif | |
if (bind(fd, info->ai_addr, info->ai_addrlen) < 0) { | |
close(fd); | |
freeaddrinfo(info); | |
continue; | |
} | |
if (listen(fd, 5) < 0) { | |
close(fd); | |
freeaddrinfo(info); | |
continue; | |
} | |
while (1) { | |
struct sockaddr_storage from; | |
socklen_t fromlen = sizeof(from); | |
int client = accept(fd, (struct sockaddr *)&from, &fromlen); | |
if (client < 0) { | |
continue; | |
} | |
pid_t pid = fork(); | |
if (pid == 0) { | |
char path_buffer[BUFFER_SIZE], host_buffer[NI_MAXHOST], data_buffer[BUFFER_SIZE]; | |
int error = getnameinfo(info->ai_addr, info->ai_addrlen, host_buffer, NI_MAXHOST, NULL, 0, 0); | |
if (error < 0) { | |
fprintf(stderr, "%s", gai_strerror(error)); | |
continue; | |
} | |
ssize_t length; | |
while ((length = read(client, path_buffer, BUFFER_SIZE)) > 0) { | |
path_buffer[length - 1] = '\0'; | |
fprintf(stdout, "request: %s\n", path_buffer); | |
int fd = open(path_buffer, O_RDONLY); | |
if (fd < 0) { | |
strcpy(data_buffer, "no such file\n"); | |
write(client, data_buffer, strlen(data_buffer)); | |
continue; | |
} | |
ssize_t file_length; | |
while ((file_length = read(fd, data_buffer, BUFFER_SIZE)) > 0) { | |
write(client, data_buffer, file_length); | |
} | |
} | |
} | |
close(client); | |
} | |
break; | |
} | |
return 0; | |
} | |
// MARK: Client | |
int client(const char *hostname, const char *port) { | |
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; | |
struct addrinfo *info0 = NULL; | |
int error = getaddrinfo(hostname, port, &hints, &info0); | |
if (error != 0) { | |
fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); | |
return -1; | |
} | |
char host_buffer[NI_MAXHOST], service_buffer[NI_MAXSERV], input_buffer[BUFFER_SIZE], data_buffer[BUFFER_SIZE]; | |
for (struct addrinfo *info = info0; info != NULL; info = info->ai_next) { | |
int error = getnameinfo(info->ai_addr, info->ai_addrlen, host_buffer, NI_MAXHOST, service_buffer, NI_MAXSERV, 0); | |
if (error < 0) { | |
fprintf(stderr, "%s", gai_strerror(error)); | |
continue; | |
} | |
int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); | |
if (fd < 0) { | |
close(fd); | |
continue; | |
} | |
if (connect(fd, info->ai_addr, info->ai_addrlen) < 0) { | |
close(fd); | |
continue; | |
} | |
ssize_t length; | |
while ((length = read(0, input_buffer, BUFFER_SIZE)) > 0) { | |
write(fd, input_buffer, length); | |
length = read(fd, data_buffer, BUFFER_SIZE); | |
write(STDOUT_FILENO, data_buffer, length); | |
} | |
close(fd); | |
break; | |
} | |
return 0; | |
} | |
// MARK: Main | |
int main(int argc, const char * argv[]) { | |
if (argc < 2 || argv[1] == NULL) { | |
print_usage(); | |
return -1; | |
} | |
int error = 0; | |
if (strcmp(argv[1], "server") == 0) { | |
fprintf(stdout, "Starting as server...\n"); | |
error = server(argv[2]); | |
} else if (strcmp(argv[1], "client") == 0) { | |
fprintf(stdout, "Starting as client...\n"); | |
error = client(argv[2], argv[3]); | |
} else { | |
fprintf(stderr, "No such command: %s\n", argv[2]); | |
print_usage(); | |
} | |
return error; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment