Last active
June 2, 2024 04:57
-
-
Save jweinst1/ab5b45be4adf6bac682800d95c4fc7e6 to your computer and use it in GitHub Desktop.
a polling and tcp based server and client in C
This file contains 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 <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <signal.h> | |
#include <sys/stat.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <arpa/inet.h> | |
#include <netinet/in.h> | |
#include <netdb.h> | |
#include <assert.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <pthread.h> | |
#include <poll.h> | |
static const char* local_host_ipv4 = "127.0.0.1"; | |
static unsigned short port_nums[] = {12001, 12002}; | |
// int inet_res = inet_aton(g_tcp_host, &(servaddr.sin_addr)); | |
static int create_server_socket(unsigned short portno, const char* host, int blocking) { | |
struct sockaddr_in servaddr; | |
int server_fd = -1; | |
server_fd = socket(AF_INET, SOCK_STREAM, 0); | |
if (server_fd == -1) { | |
return server_fd; | |
} | |
if (!blocking) { | |
int flags = fcntl(server_fd, F_GETFL, 0); | |
if (fcntl(server_fd, F_SETFL, flags | O_NONBLOCK)) { | |
fprintf(stderr, "Cannot set non blocking on socket\n"); | |
close(server_fd); | |
return -1; | |
} | |
} | |
memset(&servaddr, 0, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
int inet_res = inet_aton(host, &(servaddr.sin_addr)); | |
if (inet_res == 0) { | |
fprintf(stderr, "Cannot convert host to ip!\n"); | |
close(server_fd); | |
return -1; | |
} | |
servaddr.sin_port = htons(portno); | |
if ((bind(server_fd, (struct sockaddr*)&servaddr, sizeof(servaddr))) != 0) { | |
fprintf(stderr, "Cannot bind socket\n"); | |
close(server_fd); | |
return -1; | |
} | |
if ((listen(server_fd, 10)) != 0) { | |
fprintf(stderr, "Cannot listen to socket\n"); | |
close(server_fd); | |
return -1; | |
} | |
return server_fd; | |
} | |
static int create_client_socket(unsigned short portno, const char* host, int blocking) { | |
struct sockaddr_in servaddr; | |
int server_fd = -1; | |
server_fd = socket(AF_INET, SOCK_STREAM, 0); | |
if (server_fd == -1) { | |
return server_fd; | |
} | |
memset(&servaddr, 0, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
int inet_res = inet_aton(host, &(servaddr.sin_addr)); | |
if (inet_res == 0) { | |
fprintf(stderr, "Cannot convert host to ip!\n"); | |
close(server_fd); | |
return -1; | |
} | |
servaddr.sin_port = htons(portno); | |
errno=0; | |
while (connect(server_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) { | |
if (errno == EAGAIN || errno == EALREADY) { | |
errno = 0; | |
continue; | |
} | |
fprintf(stderr, "Failed to connect to host=%s, port=%u\n", host, portno); | |
perror("Error: "); | |
close(server_fd); | |
return -1; | |
} | |
if (!blocking) { | |
int flags = fcntl(server_fd, F_GETFL, 0); | |
if (fcntl(server_fd, F_SETFL, flags | O_NONBLOCK)) { | |
fprintf(stderr, "Cannot set non blocking on socket\n"); | |
close(server_fd); | |
return -1; | |
} | |
} | |
return server_fd; | |
} | |
static int RET_GOOD = 0; | |
static int RET_BAD = 1; | |
static void* server_work(void* arg) { | |
uint64_t tid64; | |
int my_server; | |
int core_conn; | |
struct pollfd pollers[10]; | |
nfds_t poller_count = 1; | |
pthread_threadid_np(NULL, &tid64); | |
unsigned short* my_port = arg; | |
printf("tid=%llu, my port is %u\n", tid64, *my_port); | |
if (*my_port % 2 == 0) { | |
// create client connection | |
do { | |
sleep(1); | |
printf("tid=%llu, currently connecting\n", tid64); | |
} while ((core_conn = create_client_socket(*my_port - 1, local_host_ipv4, 0)) == -1); | |
} else { | |
// create server connection | |
my_server = create_server_socket(*my_port, local_host_ipv4, 1); | |
if (my_server == -1) { | |
fprintf(stderr, "Thread id=%llu failed to create server socket!\n", tid64); | |
pthread_exit(&RET_BAD); | |
} | |
socklen_t clen = 0; | |
struct sockaddr_in cliaddr; | |
printf("tid=%llu, going to block on accept to see the other member\n", tid64); | |
int result = accept(my_server, (struct sockaddr*)&cliaddr, &clen); | |
int rflags = fcntl(result, F_GETFL, 0); | |
if (fcntl(result, F_SETFL, rflags | O_NONBLOCK)) { | |
fprintf(stderr, "tid=%llu Cannot set non blocking on socket\n", tid64); | |
close(result); | |
abort(); | |
} | |
core_conn = result; | |
} | |
pollers[0].fd = core_conn; | |
pollers[0].events = POLLIN; | |
while(1) { | |
unsigned char byte_to_send = 0; | |
if (*my_port % 2 == 0) { | |
byte_to_send = 44; | |
} else { | |
byte_to_send = 22; | |
} | |
errno = 0; | |
write(pollers[0].fd, &byte_to_send, sizeof(byte_to_send)); | |
if (errno == EAGAIN || errno == EWOULDBLOCK) { | |
fprintf(stderr, "tid=%llu, got blocked on a write!", tid64); | |
} | |
errno = 0; | |
int ready = poll(pollers, poller_count, -1); | |
if (ready == -1) { | |
fprintf(stderr, "Got bad return from poll, errno=%d\n", errno); | |
//pthread_exit(&RET_BAD); | |
} else if (ready == 0) { | |
printf("tid=%llu, Timeout passed on poll!\n", tid64); | |
} else { | |
if (pollers[0].revents & POLLIN) { | |
if (errno == EWOULDBLOCK || errno == EAGAIN) { | |
printf("tid=%llu, Nothing to read!\n", tid64); | |
} else { | |
printf("tid=%llu, Got in event via poll!", tid64); | |
unsigned char byte = 0; | |
read(pollers[0].fd, &byte, sizeof(byte)); | |
printf("tid=%llu, got byte %u\n", tid64, byte); | |
} | |
} | |
} | |
} | |
if (*my_port % 2 != 0) { | |
close(my_server); | |
} | |
close(core_conn); | |
pthread_exit(&RET_GOOD); | |
} | |
int main(int argc, char const *argv[]) | |
{ | |
//int* foo = NULL; | |
//pthread_join(id, (void**)&foo); | |
signal(SIGPIPE, SIG_IGN); | |
pthread_t id1; | |
pthread_t id2; | |
int s1 = pthread_create(&id1, NULL, &server_work, port_nums); | |
int s2 = pthread_create(&id2, NULL, &server_work, port_nums + 1); | |
if(s1 || s2) { | |
fprintf(stderr, "failed to create thread!!!\n"); | |
abort(); | |
} | |
int* exit_stat = NULL; | |
pthread_join(id1, (void**)&exit_stat); | |
pthread_join(id2, (void**)&exit_stat); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment