Created
September 25, 2016 12:04
-
-
Save en-em/e8889248644723b0fc28ce2dd5d50701 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
#include <errno.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/select.h> | |
#include <sys/time.h> | |
#include <netinet/in.h> | |
#include <netdb.h> | |
#include <readline/readline.h> | |
#include <readline/history.h> | |
#include <stdint.h> | |
#ifndef __USE_POSIX | |
#define __USE_POSIX | |
#endif | |
#include <limits.h> | |
static void my_exit (int code) | |
{ | |
rl_callback_handler_remove(); | |
printf ("Bye!\n"); | |
exit (code); | |
} | |
static void handle_error (const char* s) | |
{ | |
perror (s); | |
my_exit (EXIT_FAILURE); | |
} | |
static int socket_to_write; | |
void my_write (int fd, void* data, size_t size) | |
{ | |
char* d = data; | |
int n_written; | |
while ((n_written = write (fd, d, size)) < size) | |
{ | |
if (n_written < 0) | |
handle_error ("write"); | |
size -= n_written; | |
d += n_written; | |
} | |
} | |
static void my_read (int fd, void* data, size_t size) | |
{ | |
char* d = data; | |
int n_read; | |
while ((n_read = read (fd, d, size)) < size) | |
{ | |
if (n_read == 0) | |
my_exit (EXIT_SUCCESS); | |
if (n_read < 0) | |
handle_error ("read"); | |
size -= n_read; | |
d += n_read; | |
} | |
} | |
static void my_rlhandler(char* line) | |
{ | |
if (line == NULL) | |
my_exit (EXIT_SUCCESS); | |
size_t len = strlen(line); | |
if (*line != 0) | |
{ | |
add_history(line); | |
uint32_t wlen = htonl(len); | |
my_write (socket_to_write, &wlen, sizeof(wlen)); | |
my_write (socket_to_write, line, len); | |
} | |
free (line); | |
} | |
static char* readdata (int fd) | |
{ | |
uint32_t sz; | |
my_read (fd, &sz, sizeof(sz)); | |
size_t size = ntohl(sz); | |
char* s = malloc(size+1); | |
my_read (fd, s, size); | |
s[size] = 0; | |
return s; | |
} | |
static void communicate(int sock) | |
{ | |
fd_set fds; | |
socket_to_write = sock; | |
rl_callback_handler_install("> ", (rl_vcpfunc_t*) &my_rlhandler); | |
while (1) | |
{ | |
FD_ZERO(&fds); | |
FD_SET(sock, &fds); | |
FD_SET(0, &fds); | |
while (select (sock+1, &fds, 0, 0, 0) < 0) | |
{ | |
if (errno != EINTR && errno != EAGAIN) | |
handle_error("select"); | |
} | |
if (FD_ISSET(0, &fds)) | |
{ | |
rl_callback_read_char(); | |
} | |
if (FD_ISSET(sock, &fds)) | |
{ | |
char* message = readdata (sock); | |
int saved_point = rl_point; | |
char *saved_line = rl_copy_text(0, rl_end); | |
rl_save_prompt(); | |
rl_replace_line("", 0); | |
rl_redisplay(); | |
printf("] %s\n", message); | |
free(message); | |
rl_restore_prompt(); | |
rl_replace_line(saved_line, 0); | |
rl_point = saved_point; | |
rl_redisplay(); | |
free(saved_line); | |
} | |
} | |
} | |
static void usage (const char* pgm) | |
{ | |
fprintf (stderr, "Usage: %s [-p port] [hostname]\n", pgm); | |
exit (EXIT_FAILURE); | |
} | |
int main (int argc, char* argv[]) | |
{ | |
int ls, cs; | |
struct sockaddr_in addr; | |
int port = 9999; | |
if (argc > 1) | |
{ | |
if (strcmp(argv[1], "-p") == 0) | |
{ | |
if (argc > 2) | |
{ | |
port = atoi(argv[2]); | |
argv += 2; | |
argc -= 2; | |
} | |
else | |
usage(argv[0]); | |
} | |
} | |
if (argc == 1) | |
{ | |
struct sockaddr_in peer; | |
// we are server | |
ls = socket(AF_INET, SOCK_STREAM, 0); | |
if (ls < 0) | |
handle_error("socket"); | |
memset(&addr, 0, sizeof(addr)); | |
addr.sin_family = AF_INET; | |
addr.sin_addr.s_addr = INADDR_ANY; | |
addr.sin_port = htons(9999); | |
if (bind(ls, (struct sockaddr *) &addr, | |
sizeof(struct sockaddr_in)) < 0) | |
handle_error("bind"); | |
if (listen (ls, 1) < 0) | |
handle_error ("listen"); | |
printf ("Waiting for a connection..."); | |
fflush(stdout); | |
socklen_t addrlen=sizeof(peer); | |
if ((cs = accept(ls, (struct sockaddr*)&peer, &addrlen)) >= 0) | |
{ | |
char hostname[HOST_NAME_MAX+1]; | |
getnameinfo ((struct sockaddr*)&peer, addrlen, hostname, HOST_NAME_MAX+1, 0, 0, 0); | |
printf ("Connected to %s\n", hostname); | |
communicate (cs); | |
} | |
handle_error ("accept"); | |
} | |
else if (argc == 2) | |
{ | |
struct hostent *server; | |
server = gethostbyname(argv[1]); | |
if (!server) | |
handle_error("gethostbyname"); | |
memset(&addr, 0, sizeof(addr)); | |
addr.sin_family = AF_INET; | |
memcpy (&addr.sin_addr.s_addr, server->h_addr, sizeof(addr.sin_addr.s_addr)); | |
addr.sin_port = htons(9999); | |
cs = socket(AF_INET, SOCK_STREAM, 0); | |
if (cs < 0) | |
handle_error("socket"); | |
if (connect (cs, (struct sockaddr *) &addr, | |
sizeof(struct sockaddr_in)) < 0) | |
handle_error("connect"); | |
communicate (cs); | |
} | |
else | |
usage(argv[0]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment