Skip to content

Instantly share code, notes, and snippets.

@akkyie
Last active July 4, 2016 08:01
Show Gist options
  • Save akkyie/da0b8f4eb8e4a32cb449 to your computer and use it in GitHub Desktop.
Save akkyie/da0b8f4eb8e4a32cb449 to your computer and use it in GitHub Desktop.
//
// 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