From 6faa235cf719acd95bab68b9bd3da9943499e9d5 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 15 Sep 2017 11:25:09 +0200 Subject: [PATCH 01/15] added pthread example code --- thread_examples/Makefile | 20 +++++ thread_examples/thread_example.c | 61 ++++++++++++++ thread_examples/thread_example_monitor.c | 80 ++++++++++++++++++ thread_examples/thread_example_semaphore.c | 95 ++++++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 thread_examples/Makefile create mode 100644 thread_examples/thread_example.c create mode 100644 thread_examples/thread_example_monitor.c create mode 100644 thread_examples/thread_example_semaphore.c diff --git a/thread_examples/Makefile b/thread_examples/Makefile new file mode 100644 index 0000000..606b7d7 --- /dev/null +++ b/thread_examples/Makefile @@ -0,0 +1,20 @@ +# To run: make -f Makefile.threads +# +# For compiling on PC +CC=gcc + +PRGS=thread_example thread_example_monitor thread_example_semaphore +.PHONY: all clean distclean +all: $(PRGS) + +# CPPFLAGS+=-DDEBUG +# CPPFLAGS+=-DSHORT_NUMBER_FRAME_SEQUENCE +CFLAGS+=-g -Wall -Werror -pedantic -pedantic-errors +LDLIBS=-lpthread + +%: %.c + +clean: + +distclean: clean + -rm $(PRGS) diff --git a/thread_examples/thread_example.c b/thread_examples/thread_example.c new file mode 100644 index 0000000..fd90aed --- /dev/null +++ b/thread_examples/thread_example.c @@ -0,0 +1,61 @@ + +#include +#include +#include + + +#include +#include + + +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; + +struct global_data{ + const int count; +}; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + printf("task_a: %d\n",i); + } + return NULL; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + printf("task_b: %d\n",i); + } + return NULL; +} + + +int main() +{ + struct global_data data = {10}; + pthread_t thread_a; + pthread_t thread_b; + + + if(pthread_create(&thread_a, NULL, task_a, &data)){ + printf("Failed to create thread_a\n"); + exit(1); + } + if(pthread_create(&thread_b, NULL, task_b, &data)){ + printf("Failed to create thread_b\n"); + exit(2); + } + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + return 0; +} diff --git a/thread_examples/thread_example_monitor.c b/thread_examples/thread_example_monitor.c new file mode 100644 index 0000000..0b244b5 --- /dev/null +++ b/thread_examples/thread_example_monitor.c @@ -0,0 +1,80 @@ + +#include +#include +#include + + +#include +#include + + +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; + +struct global_data{ + char task; + const int count; +}; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + pthread_mutex_lock(&mtx); + while(d->task != 'a') pthread_cond_wait(&cnd, &mtx); + printf("task_a: %d\n",i); + d->task = 'b'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + pthread_mutex_lock(&mtx); + while(d->task != 'b') pthread_cond_wait(&cnd, &mtx); + printf("task_b: %d\n",i); + d->task = 'a'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + + +int main() +{ + struct global_data data = {0,10}; + pthread_t thread_a; + + pthread_t thread_b; + + + + if(pthread_create(&thread_a, NULL, task_a, &data)){ + printf("Failed to create thread_a\n"); + exit(1); + } + if(pthread_create(&thread_b, NULL, task_b, &data)){ + printf("Failed to create thread_b\n"); + exit(2); + } + sleep(1); + pthread_mutex_lock(&mtx); + printf("setting task to a\n"); + data.task = 'a'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + return 0; +} diff --git a/thread_examples/thread_example_semaphore.c b/thread_examples/thread_example_semaphore.c new file mode 100644 index 0000000..4621db0 --- /dev/null +++ b/thread_examples/thread_example_semaphore.c @@ -0,0 +1,95 @@ +#include +#include +#include + + +#include +#include + +#include + + + +struct global_data{ + int count; + sem_t sem_a; + sem_t sem_b; +}; + +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + sem_wait(&d->sem_a); + pthread_mutex_lock(&mtx); + printf("task_a: %d\n",i); + sem_post(&d->sem_b); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + sem_wait(&d->sem_b); + pthread_mutex_lock(&mtx); + printf("task_b: %d\n",i); + sem_post(&d->sem_a); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + +int global_data_init(struct global_data *d) +{ + d->count = 10; + if(sem_init(&d->sem_a, 0, 0)){ + return 1; + } + if(sem_init(&d->sem_b, 0, 0)){ + return 1; + } + return 0; +} + +int main() +{ + struct global_data data; + pthread_t thread_a; + pthread_t thread_b; + int res; + + if(global_data_init(&data)){ + perror("global_data_init"); + exit(1); + } + + if((res=pthread_create(&thread_a, NULL, task_a, &data))){ + errno = res; + perror("Failed to create thread_a\n"); + exit(2); + } + if((res=pthread_create(&thread_b, NULL, task_b, &data))){ + errno = res; + perror("Failed to create thread_a\n"); + exit(3); + } + sleep(1); + pthread_mutex_lock(&mtx); + printf("signalling semaphore\n"); + sem_post(&data.sem_a); + pthread_mutex_unlock(&mtx); + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + return 0; +} -- GitLab From ddbab7a9023edda932988343000dbd5addbb41c6 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Mon, 2 Oct 2017 12:38:21 +0200 Subject: [PATCH 02/15] added ifdefs for info printouts --- socket_examples/simple_server.c | 117 ++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 socket_examples/simple_server.c diff --git a/socket_examples/simple_server.c b/socket_examples/simple_server.c new file mode 100644 index 0000000..3518b2c --- /dev/null +++ b/socket_examples/simple_server.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +static int bind_server_socket(int fd, int port); + +/* + * create a server socket bound to port + * and listening. + * + * return positive file descriptor + * or negative value on error + */ +int create_server_socket(int port) +{ + int fd = -1; + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + fd = socket(AF_INET,SOCK_STREAM,0); + if(fd < 0) return fd; + if(bind_server_socket(fd,port) < 0) return -1; + + if(listen(fd,10)) return -1; + + return fd; +} + +static int bind_server_socket(int fd, int port){ + + struct sockaddr_in addr; + int val = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + perror("setsockopt"); + return -1; + } + + // see man page ip(7) + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; + +#ifdef INFO + printf("bound fd %d to port %d (%x)\n",fd,port,addr.sin_port); +#endif + + return fd; +} + +/* + * Serve one client: send a message and close the socket + */ +static int do_serve(int fd) +{ + struct sockaddr_in client; + socklen_t addrlen = sizeof(client); + int clientfd; + const char* msg = "Hello, socket!\n" + "I am a text\n" + "BYE.\n"; + size_t len = strlen(msg); + + printf("attempting accept on fd %d\n",fd); + if((clientfd = accept(fd,(struct sockaddr*) &client, &addrlen)) < 0) return -1; +#ifdef INFO + printf("writing msg (len=%lu) to clientfd %d (port %d)\n",len,clientfd,client.sin_port); +#endif + +#ifdef WRITE_LOOP + size_t written = 0; + do { + int res = write(clientfd, msg, len); + if (res < 0) { + perror("write to clientfd"); + goto error; + } +#ifdef INFO + printf("write returned %d\n",res); +#endif + written += res; + } while (written < len); +#else + int res = write(clientfd, msg, len); + if (res < 0) { + perror("write to clientfd"); + goto error; + } +#endif + + error: + printf("closing clientfd\n"); + return close(clientfd); +} + +int main() +{ + int fd = create_server_socket(5000); + + if(fd < 0){ + perror("create_server_socket"); + return 1; + } + + do_serve(fd); + + printf("closing socket: %d\n", fd); + close(fd); + + return 0; +} -- GitLab From c1c32b6e61f30cf4cd06bedb479e130f6ecc7f01 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Tue, 3 Oct 2017 12:44:39 +0200 Subject: [PATCH 03/15] motion_server: exit on failed poll --- examples/motion_server.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/motion_server.c b/examples/motion_server.c index d67f843..d5212ad 100644 --- a/examples/motion_server.c +++ b/examples/motion_server.c @@ -486,9 +486,8 @@ int serve_clients(struct global_state* state) result = errno; }; } -failed_poll: - ; } +failed_poll: return result; } -- GitLab From 21de655939d40794bfe7c783c187df6deba707e5 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Tue, 3 Oct 2017 15:20:37 +0200 Subject: [PATCH 04/15] http_server comments --- examples/http_server.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/http_server.c b/examples/http_server.c index 9803d15..66aaaba 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -73,6 +73,9 @@ static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; #ifdef USE_CAMERA +/* try to open capture interface. + * returns 0 on success + */ int try_open_camera(struct global_state* state) { state->cam = camera_open(); @@ -158,6 +161,9 @@ int client_send_frame(struct client* client, frame* fr) return result; } +/* get frame from camera and send to client + * returns zero on success + */ int try_get_frame(struct client* client) { int result=-1; @@ -276,12 +282,17 @@ void* serve_client(void *ctxt) return (void*) (intptr_t) close(client->connfd); } +/* signal the global condition variable + */ void signal_to_bg_task() { pthread_mutex_lock(&global_mutex); pthread_cond_broadcast(&global_cond); pthread_mutex_unlock(&global_mutex); } +/* set flags to signal shutdown and + * signal global condition variable + */ void server_quit(struct global_state* s) { printf("Quitting\n"); @@ -355,10 +366,11 @@ int try_accept(struct global_state* state, struct client* client) #ifdef INFO printf("accepted connection\n"); #endif - // serve clients in separate thread, for three reasons + // serve clients in separate thread, for four reasons // 1. Illustrate threading // 2. Not blocking the user interface while serving client // 3. Prepare for serving multiple clients concurrently + // 4. The AXIS software requires capture to be run outside the main thread if (pthread_create(&state->client_thread, 0, serve_client, client)) { printf("Error pthread_create()\n"); perror("creating"); @@ -509,6 +521,9 @@ int create_socket(struct global_state* state) return 0; } +/* bind state->listenfd to port and listen + * returns 0 on success + */ int bind_and_listen(struct global_state* state, int port) { struct sockaddr_in serv_addr; -- GitLab From 51a51fd724c7fcac7c967b3aa7910e51f2b53676 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Tue, 3 Oct 2017 15:21:24 +0200 Subject: [PATCH 05/15] quit http_server if poll fails --- examples/http_server.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/http_server.c b/examples/http_server.c index 66aaaba..4d3bf20 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -467,7 +467,7 @@ int serve_clients(struct global_state* state) if(ret <0) { perror("poll"); result = errno; - goto failed_poll; + server_quit(state); } else if (is_running(state)) { if(try_accept(state, client)) { perror("try_accept"); @@ -475,7 +475,6 @@ int serve_clients(struct global_state* state) server_quit(state); }; } -failed_poll: free(client); } failed_to_alloc_client: -- GitLab From e68956fa7b19e8719ceee8a5937ed4bfb980823c Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 12:51:08 +0200 Subject: [PATCH 06/15] thread example return values --- thread_examples/thread_example.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/thread_examples/thread_example.c b/thread_examples/thread_example.c index fd90aed..b920175 100644 --- a/thread_examples/thread_example.c +++ b/thread_examples/thread_example.c @@ -24,7 +24,7 @@ void * task_a(void * ctx) for(i=0; i < d->count; ++i){ printf("task_a: %d\n",i); } - return NULL; + return (void*) 17; } void * task_b(void * ctx) @@ -36,7 +36,7 @@ void * task_b(void * ctx) for(i=0; i < d->count; ++i){ printf("task_b: %d\n",i); } - return NULL; + return (void*) 42; } @@ -45,6 +45,10 @@ int main() struct global_data data = {10}; pthread_t thread_a; pthread_t thread_b; +#ifdef RETURN_INT + void *ret_a = 0; + void *ret_b = 0; +#endif if(pthread_create(&thread_a, NULL, task_a, &data)){ @@ -55,7 +59,13 @@ int main() printf("Failed to create thread_b\n"); exit(2); } +#ifdef RETURN_INT + pthread_join(thread_a, &ret_a); + pthread_join(thread_b, &ret_b); + printf("thread_a returned: %d\nthread_b returned: %d\n", (int)(long)ret_a, (int)(long)ret_b); +#else pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); +#endif return 0; } -- GitLab From be0c7a9874cbd8ac30c2e4d1b44eb29f39522e46 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 12:51:46 +0200 Subject: [PATCH 07/15] thread example mutex_init --- thread_examples/thread_example_semaphore.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/thread_examples/thread_example_semaphore.c b/thread_examples/thread_example_semaphore.c index 4621db0..402edf0 100644 --- a/thread_examples/thread_example_semaphore.c +++ b/thread_examples/thread_example_semaphore.c @@ -12,12 +12,11 @@ struct global_data{ int count; + pthread_mutex_t mtx; sem_t sem_a; sem_t sem_b; }; -pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; - void * task_a(void * ctx) { struct global_data *d = ctx; @@ -26,10 +25,10 @@ void * task_a(void * ctx) printf("started task_a\n"); for(i=0; i < d->count; ++i){ sem_wait(&d->sem_a); - pthread_mutex_lock(&mtx); + pthread_mutex_lock(&d->mtx); printf("task_a: %d\n",i); sem_post(&d->sem_b); - pthread_mutex_unlock(&mtx); + pthread_mutex_unlock(&d->mtx); } return NULL; } @@ -42,10 +41,10 @@ void * task_b(void * ctx) printf("started task_b\n"); for(i=0; i < d->count; ++i){ sem_wait(&d->sem_b); - pthread_mutex_lock(&mtx); + pthread_mutex_lock(&d->mtx); printf("task_b: %d\n",i); sem_post(&d->sem_a); - pthread_mutex_unlock(&mtx); + pthread_mutex_unlock(&d->mtx); } return NULL; } @@ -53,6 +52,7 @@ void * task_b(void * ctx) int global_data_init(struct global_data *d) { d->count = 10; + pthread_mutex_init(&d->mtx, NULL); if(sem_init(&d->sem_a, 0, 0)){ return 1; } @@ -85,10 +85,10 @@ int main() exit(3); } sleep(1); - pthread_mutex_lock(&mtx); + pthread_mutex_lock(&data.mtx); printf("signalling semaphore\n"); sem_post(&data.sem_a); - pthread_mutex_unlock(&mtx); + pthread_mutex_unlock(&data.mtx); pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); return 0; -- GitLab From 3b02cbdcb1eea25bc5cdc6aacd1f456f72fd57aa Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 12:53:46 +0200 Subject: [PATCH 08/15] added readme, updated makefile --- thread_examples/Makefile | 4 - thread_examples/README-pthread.md | 122 ++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 thread_examples/README-pthread.md diff --git a/thread_examples/Makefile b/thread_examples/Makefile index 606b7d7..05a0e64 100644 --- a/thread_examples/Makefile +++ b/thread_examples/Makefile @@ -1,6 +1,3 @@ -# To run: make -f Makefile.threads -# -# For compiling on PC CC=gcc PRGS=thread_example thread_example_monitor thread_example_semaphore @@ -8,7 +5,6 @@ PRGS=thread_example thread_example_monitor thread_example_semaphore all: $(PRGS) # CPPFLAGS+=-DDEBUG -# CPPFLAGS+=-DSHORT_NUMBER_FRAME_SEQUENCE CFLAGS+=-g -Wall -Werror -pedantic -pedantic-errors LDLIBS=-lpthread diff --git a/thread_examples/README-pthread.md b/thread_examples/README-pthread.md new file mode 100644 index 0000000..4e093a6 --- /dev/null +++ b/thread_examples/README-pthread.md @@ -0,0 +1,122 @@ +# Introduction to pthreads + +These three examples are aimed at showing +* how to create threads in C using the pthread library +* how to use monitors +* how to use semaphores + +Please note that when building programs using the pthread library +it must be linked with `libpthread` by providing the flag `-lpthread`. +See the Makefile for details. + +# Creating threads + +The `pthread_create()` function, +```C +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); +``` +starts a new thread in the +calling process. The new thread starts execution by +invoking start_routine(); arg is passed as the sole argument +of start_routine(). + +The sytax of function pointers in C can be somewhat confusing, but the type +```C +void * (*f)(void *) +``` +declares a pointer, f, which can point to a function with the signature +```C +void* function(void *p); +``` +That is, a function with one `void *` parameter, and return type `void *`. + +Before returning, a successful call to `pthread_create()` +stores the ID of the new thread in the buffer pointed to by +thread; this identifier is used to refer to the thread in +subsequent calls to other pthreads functions. + +The new thread terminates in one of the following ways: + + * It calls `pthread_exit(3),` specifying an exit status value + that is available to another thread in the same process + that calls `pthread_join(3).` + + * It returns from `start_routine().` This is equivalent to + calling `pthread_exit(3)` with the value supplied in the + return statement. + + * It is canceled (see `pthread_cancel(3)).` + + * Any of the threads in the process calls `exit(3),` or the + main thread performs a return from `main().` This causes + the termination of all threads in the process. + +## Relevant manual pages: + + pthreads(7) + pthread_create(3) + pthread_attr_init(3) + pthread_join(3) + + +## Minimal example + +The file thread_example.c contains a minimal example, showing +how to create a thread, how to pass arguments to the thread function +and how to use `pthread_join()` to wait for a thread to finish. +The code shows two alternatives, one ignoring the return value +by passing `NULL` to `pthread_join(),` and one to get the +value (including the type casting needed to pass an int in +a void*. + +# Monitors + +The file thread_example_monitor.c shows how to implement +rendez-vous using a monitor (i.e., a pthread mutex and a condition variable). + +Note that in pthreads, the condition variable is a separate entity, +and the tie to a certain mutex is done by passing pointers to +both the mutex and the condition variable to pthread_cond_wait(). +This allows having more than one condition variable, to make +signalling more fine-grained than notifyAll() in Java. + +When allocated on the stack, the mutex and condition can be given +default values using initializer macros: +```C +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; +``` +Another way to initialise a mutex is to use the function +`pthread_mutex_init().` See the man page for details. + +By default, a pthread mutex is not recursive, meaning that if a thread +attemtps to lock a mutex it already holds, it will deadlock. To get the +same semantics as java (i.e., a thread can lock the same mutex multiple +times) the mutex must be created as recursive. See the manual page for +details. + +## Relevant manual pages: + + pthread_mutex_init(3) + pthread_mutex_destroy(3) + pthread_mutex_lock(3) + pthread_mutex_trylock(3) + pthread_mutex_unlock(3) + pthread_mutexattr_init(3) + pthread_mutexattr_destroy(3) + + +# Semaphores + +The file thread_example_semaphore.c shows an example +using a mutex for mutual exclusion and a semaphore for signalling. +It also shows how to initialize a mutex that is a struct member, +using `pthread_mutex_init()`. + +## Relevant manual pages: + +sem_init(3) +sem_destroy(3) +sem_post(3) +sem_wait(3) \ No newline at end of file -- GitLab From 54c6decffd49e5bf8aa06577da6384fd43716d22 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 13:41:50 +0200 Subject: [PATCH 09/15] init fd to unreasonable value --- examples/motion_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/motion_client.c b/examples/motion_client.c index c69c50c..fb84c6b 100644 --- a/examples/motion_client.c +++ b/examples/motion_client.c @@ -24,7 +24,7 @@ static void motion_get(); int running=1; -int motionfd=0; +int motionfd=-1; struct hostent *motion_server; struct sockaddr_in motion_serv_addr; -- GitLab From 45a1e76163270c5d2d5d391d85d352e790529609 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 13:42:05 +0200 Subject: [PATCH 10/15] added program name to printouts --- socket_examples/simple_server.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/socket_examples/simple_server.c b/socket_examples/simple_server.c index 3518b2c..dc2a19b 100644 --- a/socket_examples/simple_server.c +++ b/socket_examples/simple_server.c @@ -40,7 +40,7 @@ static int bind_server_socket(int fd, int port){ return -1; } - // see man page ip(7) + /* see man page ip(7) */ addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); @@ -48,7 +48,7 @@ static int bind_server_socket(int fd, int port){ if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; #ifdef INFO - printf("bound fd %d to port %d (%x)\n",fd,port,addr.sin_port); + printf("simple_server: bound fd %d to port %d\n",fd,port); #endif return fd; @@ -59,18 +59,16 @@ static int bind_server_socket(int fd, int port){ */ static int do_serve(int fd) { - struct sockaddr_in client; - socklen_t addrlen = sizeof(client); int clientfd; const char* msg = "Hello, socket!\n" "I am a text\n" "BYE.\n"; size_t len = strlen(msg); - printf("attempting accept on fd %d\n",fd); - if((clientfd = accept(fd,(struct sockaddr*) &client, &addrlen)) < 0) return -1; + printf("simple_server: attempting accept on fd %d\n",fd); + if((clientfd = accept(fd, NULL, NULL)) < 0) return -1; #ifdef INFO - printf("writing msg (len=%lu) to clientfd %d (port %d)\n",len,clientfd,client.sin_port); + printf("simple_server: writing msg (len=%lu) to clientfd (%d)\n",len,clientfd); #endif #ifdef WRITE_LOOP @@ -82,20 +80,22 @@ static int do_serve(int fd) goto error; } #ifdef INFO - printf("write returned %d\n",res); + printf("simple_server: write returned %d\n",res); #endif written += res; } while (written < len); #else - int res = write(clientfd, msg, len); - if (res < 0) { - perror("write to clientfd"); - goto error; + { + int res = write(clientfd, msg, len); + if (res < 0) { + perror("write to clientfd"); + goto error; + } } #endif error: - printf("closing clientfd\n"); + printf("simple_server: closing clientfd (%d)\n",clientfd); return close(clientfd); } @@ -110,7 +110,7 @@ int main() do_serve(fd); - printf("closing socket: %d\n", fd); + printf("simple_server: closing socket: %d\n", fd); close(fd); return 0; -- GitLab From 13cc07a0ff0e2001666ddb39bd8e84999f9bc1e2 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 13:42:32 +0200 Subject: [PATCH 11/15] added simple client example --- socket_examples/simple_client.c | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 socket_examples/simple_client.c diff --git a/socket_examples/simple_client.c b/socket_examples/simple_client.c new file mode 100644 index 0000000..db9fa5d --- /dev/null +++ b/socket_examples/simple_client.c @@ -0,0 +1,102 @@ +/* +A minimal example of a TCP client + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SERVER_PORT +#define SERVER_PORT 5000 +#endif + +void init(const char* server_name, int port); +static void socket_init(); +static void socket_close(); +static void make_request(); + + +int sockfd=-1; +struct hostent *server; +struct sockaddr_in serv_addr; + +void init(const char* server_name, int port) +{ + server = gethostbyname(server_name); + if (server == NULL) { + fprintf(stderr,"ERROR, server name not found\n"); + exit(1); + } else { + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); + serv_addr.sin_port = htons(port); + } +} + +int main(int argc, char *argv[]) +{ + const char* server_name; + int port; + + if(argc>=2) { + server_name = argv[2]; + } else { + server_name = "localhost"; + } + if(argc==3) { + port = atoi(argv[3]); + } else { + port = SERVER_PORT; + } + + printf("simple_client: connecting to server: %s, port %d\n",server_name, port); + + init(server_name, port); + make_request(); + + return 0; +} + +#define BUFSZ 100 +static void make_request() +{ + char msg[BUFSZ]; +#ifdef DEBUG + printf("simple_client: connecting\n"); +#endif + socket_init(); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR connecting"); + } else { + int res = read(sockfd, msg, BUFSZ-1); + if(res < 0) { + perror("ERROR reading from motion server"); + } else { + msg[res]='\0'; /* ensure msg is null terminated */ + printf("simple_client: response: %s\n",msg); + } + socket_close(); + } +} + +static void socket_init() +{ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + perror("creating motion socket"); + } +} +static void socket_close() +{ + if (sockfd) { + if(close(sockfd)){ + perror("closing motion socket"); + } + } + sockfd=-1; +} -- GitLab From 109448ae9fadb88366b6b8665263919ad68aeb97 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Fri, 20 Oct 2017 13:48:02 +0200 Subject: [PATCH 12/15] created socket example readme --- socket_examples/README-socket.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 socket_examples/README-socket.md diff --git a/socket_examples/README-socket.md b/socket_examples/README-socket.md new file mode 100644 index 0000000..e6052e5 --- /dev/null +++ b/socket_examples/README-socket.md @@ -0,0 +1,15 @@ +# Introduction to socket programming in C + +This mimimal example consists of two files, a server which replies with +a message to the first client that connects and then exits, and a client +which connects to a server and outputs whatever it receives. + +See "Socket programming in C" for more details. + +To build and run the example, do `make run`. See the Makefile for details + +An alternative to using simple_client is to use the telnet program to +connect to the server. To do that, start `simple_server` in one terminal, +and then type `telnet localhost 5000` in another. + +Sometimes, `telnet` is a useful tool to look at or debug text-based protocols. \ No newline at end of file -- GitLab From d4978bee28ca58db29f5df5ff11fef024c212130 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Thu, 26 Oct 2017 10:37:16 +0200 Subject: [PATCH 13/15] added UDP examples --- .../{simple_client.c => simple_tcp_client.c} | 6 +- .../{simple_server.c => simple_tcp_server.c} | 12 +- socket_examples/simple_udp_client.c | 140 ++++++++++++++++++ socket_examples/simple_udp_server.c | 130 ++++++++++++++++ 4 files changed, 279 insertions(+), 9 deletions(-) rename socket_examples/{simple_client.c => simple_tcp_client.c} (91%) rename socket_examples/{simple_server.c => simple_tcp_server.c} (83%) create mode 100644 socket_examples/simple_udp_client.c create mode 100644 socket_examples/simple_udp_server.c diff --git a/socket_examples/simple_client.c b/socket_examples/simple_tcp_client.c similarity index 91% rename from socket_examples/simple_client.c rename to socket_examples/simple_tcp_client.c index db9fa5d..aaa1774 100644 --- a/socket_examples/simple_client.c +++ b/socket_examples/simple_tcp_client.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) port = SERVER_PORT; } - printf("simple_client: connecting to server: %s, port %d\n",server_name, port); + printf("simple_tcp_client: connecting to server: %s, port %d\n",server_name, port); init(server_name, port); make_request(); @@ -67,7 +67,7 @@ static void make_request() { char msg[BUFSZ]; #ifdef DEBUG - printf("simple_client: connecting\n"); + printf("simple_tcp_client: connecting\n"); #endif socket_init(); if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { @@ -78,7 +78,7 @@ static void make_request() perror("ERROR reading from motion server"); } else { msg[res]='\0'; /* ensure msg is null terminated */ - printf("simple_client: response: %s\n",msg); + printf("simple_tcp_client: response: %s\n",msg); } socket_close(); } diff --git a/socket_examples/simple_server.c b/socket_examples/simple_tcp_server.c similarity index 83% rename from socket_examples/simple_server.c rename to socket_examples/simple_tcp_server.c index dc2a19b..a4da0ab 100644 --- a/socket_examples/simple_server.c +++ b/socket_examples/simple_tcp_server.c @@ -48,7 +48,7 @@ static int bind_server_socket(int fd, int port){ if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; #ifdef INFO - printf("simple_server: bound fd %d to port %d\n",fd,port); + printf("simple_tcp_server: bound fd %d to port %d\n",fd,port); #endif return fd; @@ -65,10 +65,10 @@ static int do_serve(int fd) "BYE.\n"; size_t len = strlen(msg); - printf("simple_server: attempting accept on fd %d\n",fd); + printf("simple_tcp_server: attempting accept on fd %d\n",fd); if((clientfd = accept(fd, NULL, NULL)) < 0) return -1; #ifdef INFO - printf("simple_server: writing msg (len=%lu) to clientfd (%d)\n",len,clientfd); + printf("simple_tcp_server: writing msg (len=%lu) to clientfd (%d)\n",len,clientfd); #endif #ifdef WRITE_LOOP @@ -80,7 +80,7 @@ static int do_serve(int fd) goto error; } #ifdef INFO - printf("simple_server: write returned %d\n",res); + printf("simple_tcp_server: write returned %d\n",res); #endif written += res; } while (written < len); @@ -95,7 +95,7 @@ static int do_serve(int fd) #endif error: - printf("simple_server: closing clientfd (%d)\n",clientfd); + printf("simple_tcp_server: closing clientfd (%d)\n",clientfd); return close(clientfd); } @@ -110,7 +110,7 @@ int main() do_serve(fd); - printf("simple_server: closing socket: %d\n", fd); + printf("simple_tcp_server: closing socket: %d\n", fd); close(fd); return 0; diff --git a/socket_examples/simple_udp_client.c b/socket_examples/simple_udp_client.c new file mode 100644 index 0000000..16459b2 --- /dev/null +++ b/socket_examples/simple_udp_client.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUFSZ 1024 +#define SERVER_PORT 5000 + +/* + * create a datagram socket + */ +int create_socket() +{ + int fd = -1; + + fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + return fd; +} + +#undef USE_IP_NUMBER +static int init_remote(struct sockaddr_in* remote_end, socklen_t remote_size, const char* server_str, int port ) +{ + memset((char*) &remote_end, 0, remote_size); + remote_end->sin_family = AF_INET; + remote_end->sin_port = htons(port); +#ifdef USE_IP_NUMBER + if (inet_aton(server_str , &remote_end->sin_addr) == 0) + { + fprintf(stderr, "inet_aton() failed\n"); + return -1; + } +#else + { + struct hostent* server; + server = gethostbyname(server_str); + if (server == NULL) { + fprintf(stderr,"ERROR, server name not found\n"); + return -1; + } else { + bcopy((char *)server->h_addr, (char *)&remote_end->sin_addr.s_addr, server->h_length); + } + } + +#endif + + +} + +/* + * Send a datagram to server and wait for one reply + */ +static int send_and_receive(int fd, const char* server_str, int port) +{ + char buf[BUFSZ]; + struct sockaddr_in remote_end; + socklen_t remote_size = sizeof(remote_end); + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + + printf("simple_udp_client: sending to %s:%d (fd=%d)\n",server_str,port,fd); + + init_remote(&remote_end, remote_size, server_str, port); + + + const char* msg = "Hello, socket!\n" + "I am a text\n" + "BYE.\n"; + ssize_t len = strlen(msg); + len = sendto(fd, msg, len, 0, (struct sockaddr*) &remote_end, remote_size); + if(len == -1){ + perror("sendto"); + return len; + } + printf("simple_udp_client: waiting for datagram on fd %d\n",fd); + + len = recvfrom(fd, buf, BUFSZ-1, 0, (struct sockaddr*) &remote_end, &remote_size); + if(len == -1){ + perror("recvfrom"); + return len; + } + buf[len] = 0; /* add null termination to string*/ + + + printf("simple_udp_client: received packet from %s:%d, len=%zu\n%s\n", + inet_ntoa(remote_end.sin_addr), + ntohs(remote_end.sin_port), + len, + buf); + + return 0; +} + +int main(int argc, char *argv[]) +{ + const char* server_str; + int port; + int fd; + + if(argc>=2) { + server_str = argv[1]; + } else { +#ifdef USE_IP_NUMBER + server_str = "127.0.0.1"; +#else + server_str = "localhost"; +#endif + } + if(argc==3) { + port = atoi(argv[2]); + } else { + port = SERVER_PORT; + } + + // printf("simple_client: connecting to server: %s, port %d\n",server_str, port); + fd = create_socket(); + + if(fd < 0){ + perror("create_socket"); + return 1; + } + + + printf("calling send_and_receive for %s,%d\n",server_str,port); + send_and_receive(fd, server_str, port); + + printf("simple_udp_client: closing socket: %d\n", fd); + close(fd); + + return 0; +} + diff --git a/socket_examples/simple_udp_server.c b/socket_examples/simple_udp_server.c new file mode 100644 index 0000000..be6b48a --- /dev/null +++ b/socket_examples/simple_udp_server.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 1024 +#define SERVER_PORT 5000 + +static int bind_socket(int fd, int port); + +/* + * create a server socket bound to port + * and listening. + * + * return positive file descriptor + * or negative value on error + */ +int create_socket(int port) +{ + int fd = -1; + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + if(fd < 0) return fd; + if(bind_socket(fd,port) < 0) return -1; + printf("simple_udp_server: bound socket to port %d\n",port); + return fd; +} + +static int bind_socket(int fd, int port){ + + struct sockaddr_in addr; + // if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + // perror("setsockopt"); + // return -1; + // } + + /* see man page ip(7) */ + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; + +#ifdef INFO + printf("simple_server: bound fd %d to port %d\n",fd,port); +#endif + + return fd; +} + +/* + * Wait for one datagram and echo it back + */ +static int do_serve(int fd) +{ + char buf[BUFSZ]; + struct sockaddr_in remote_end; + socklen_t remote_size = sizeof(remote_end); + + // const char* msg = "Hello, socket!\n" + // "I am a text\n" + // "BYE.\n"; + // size_t len = strlen(msg); + ssize_t len; + + printf("simple_server: waiting for datagram on fd %d\n",fd); + + len = recvfrom(fd, buf, BUFSZ-1, 0, (struct sockaddr*) &remote_end, &remote_size); + if(len == -1){ + perror("recvfrom"); + return len; + } + buf[len] = 0; /* add null termination to string*/ + + + printf("simple_server: received packet from %s:%d, len=%zu\n%s\n", + inet_ntoa(remote_end.sin_addr), + ntohs(remote_end.sin_port), + len, + buf); + + len = sendto(fd, buf, len, 0, (struct sockaddr*) &remote_end, remote_size); + if(len == -1){ + perror("sendto"); + return len; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + const char* server_name; + int port; + int fd; + + if(argc>=2) { + server_name = argv[2]; + } else { + server_name = "localhost"; + } + if(argc==3) { + port = atoi(argv[3]); + } else { + port = SERVER_PORT; + } + + // printf("simple_client: connecting to server: %s, port %d\n",server_name, port); + fd = create_socket(port); + + if(fd < 0){ + perror("create_socket"); + return 1; + } + + do_serve(fd); + + printf("simple_server: closing socket: %d\n", fd); + close(fd); + + return 0; +} + -- GitLab From 4ad8c9cdeff495e0cd41d08dcc30c63f4b0cf802 Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Thu, 26 Oct 2017 11:54:15 +0200 Subject: [PATCH 14/15] added UDP to socket readme --- socket_examples/README-socket.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/socket_examples/README-socket.md b/socket_examples/README-socket.md index e6052e5..791d0e2 100644 --- a/socket_examples/README-socket.md +++ b/socket_examples/README-socket.md @@ -1,15 +1,29 @@ # Introduction to socket programming in C -This mimimal example consists of two files, a server which replies with -a message to the first client that connects and then exits, and a client -which connects to a server and outputs whatever it receives. +This mimimal example consists of two pars of files for TCP and UDP: +a server which replies with a message to the first client that +connects and then exits, and a client which connects to a server and +outputs whatever it receives. (In the UDP case, the client sends a +datagram and the server echoes it back, as there is no connection +established, so the "client" must start by sending a datagram. +Note that the terms "client" and "server" in the UDP case have a +more vague meaning, where the "client" is the one that takes the initiative +by sending the first datagram.) See "Socket programming in C" for more details. To build and run the example, do `make run`. See the Makefile for details -An alternative to using simple_client is to use the telnet program to -connect to the server. To do that, start `simple_server` in one terminal, +An alternative to using simple_tcp_client is to use the telnet program to +connect to the server. To do that, start `simple_tcp_server` in one terminal, and then type `telnet localhost 5000` in another. -Sometimes, `telnet` is a useful tool to look at or debug text-based protocols. \ No newline at end of file +Sometimes, `telnet` is a useful tool to look at or debug text-based protocols +over TCP. + + +Please note that TCP is connection-based, but not UDP. Thus, when using +UDP, there is nothing like `listen()` or `accept()`. Instead, you simply +send datagrams using `sendto()` or `sendmsg()`, and receive datagrams +with `recvfrom()` or `recvmsg()`. See the manual pages for further information. + -- GitLab From 814e49d33276258a199d9fd226675983a2b4329e Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz Date: Thu, 26 Oct 2017 12:33:13 +0200 Subject: [PATCH 15/15] small cleanup --- examples/http_server.c | 8 ++++---- examples/motion_server.c | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/http_server.c b/examples/http_server.c index 4d3bf20..8ec42d0 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -435,8 +435,8 @@ static void join_bg_thread(pthread_t* bg_thread, const char* msg) static void client_init(struct client* client) { - client->cam=0; - client->connfd=0; + client->cam=NULL; + client->connfd=-1; } int serve_clients(struct global_state* state) { @@ -484,11 +484,11 @@ failed_to_alloc_client: void init_global_state(struct global_state* state) { -pthread_mutex_lock(&global_mutex); + pthread_mutex_lock(&global_mutex); state->running=0; state->quit=0; state->cam=NULL; -pthread_mutex_unlock(&global_mutex); + pthread_mutex_unlock(&global_mutex); } int is_running(struct global_state* state) diff --git a/examples/motion_server.c b/examples/motion_server.c index d5212ad..7648952 100644 --- a/examples/motion_server.c +++ b/examples/motion_server.c @@ -104,10 +104,6 @@ struct client{ static int client_write_string(struct client* client); -/////////////// client stuff - -struct client; - pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; @@ -493,12 +489,12 @@ failed_poll: void init_global_state(struct global_state* state) { -pthread_mutex_lock(&global_mutex); + pthread_mutex_lock(&global_mutex); state->running=0; state->quit=0; state->t_local=0; state->t_remote=0; -pthread_mutex_unlock(&global_mutex); + pthread_mutex_unlock(&global_mutex); } int create_socket(struct global_state* state) -- GitLab