From 54f2b12d7d5892b1d6c01eac999d7abab9af4492 Mon Sep 17 00:00:00 2001 From: eddyem Date: Fri, 22 Apr 2016 16:49:21 +0300 Subject: [PATCH] add very small websocket example --- comments_before.c | 97 ++++++++++++++ simple_websockets/Makefile | 22 ++++ simple_websockets/Readme.md | 3 + simple_websockets/dbg.h | 39 ++++++ simple_websockets/main.c | 224 ++++++++++++++++++++++++++++++++ simple_websockets/que.c | 47 +++++++ simple_websockets/que.h | 44 +++++++ simple_websockets/www/test.html | 86 ++++++++++++ xor.c | 33 +++++ 9 files changed, 595 insertions(+) create mode 100644 comments_before.c create mode 100644 simple_websockets/Makefile create mode 100644 simple_websockets/Readme.md create mode 100644 simple_websockets/dbg.h create mode 100644 simple_websockets/main.c create mode 100644 simple_websockets/que.c create mode 100644 simple_websockets/que.h create mode 100644 simple_websockets/www/test.html create mode 100644 xor.c diff --git a/comments_before.c b/comments_before.c new file mode 100644 index 0000000..1330518 --- /dev/null +++ b/comments_before.c @@ -0,0 +1,97 @@ +/* + * comments_before.c - put multiline comments before text in string they follow + * + * + * Copyright 2015 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct{ + char *data; + size_t len; +} mmapbuf; + +mmapbuf *My_mmap(char *filename){ + int fd; + char *ptr; + size_t Mlen; + struct stat statbuf; + if(!filename) exit(-1); + if((fd = open(filename, O_RDONLY)) < 0) exit(-2); + if(fstat (fd, &statbuf) < 0) exit(-3); + Mlen = statbuf.st_size; + if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) + exit(-4); + if(close(fd)) exit(-5); + mmapbuf *ret = calloc(1, sizeof(mmapbuf)); + ret->data = ptr; + ret->len = Mlen; + return ret; +} + + +int main(int argc, char **argv){ + mmapbuf *buf = My_mmap(argv[1]); + char *afile = strdup(buf->data), *eptr = afile + buf->len; + char *beforecom = NULL; + while(afile < eptr){ + char *nl = strchr(afile, '\n'); + if(!nl){ + printf("%s\n", afile); + return 0; + } + *nl++ = 0; + char *commstart = strstr(afile, "/*"); + if(commstart){ + *commstart = 0; + free(beforecom); + beforecom = strdup(afile); + commstart += 2; + char *commend = NULL; + if(*commstart) commend = strstr(commstart, "*/"); + if(commend){ // single comment + *commend = 0; + afile = commend + 2; + printf("/* %s */\n", commstart); + printf("%s%s\n", beforecom, afile); + afile = nl; + }else{ // multiline comment + printf("/*%s\n", commstart); + commend = strstr(nl, "*/"); + if(!commend){ + printf("%s*/\n", nl); + return -1; + } + *commend = 0; + afile = commend + 2; + if(*afile == '\n') ++afile; + printf("%s*/\n", nl); + } + }else{ + printf("%s\n", afile); + afile = nl; + } + } +} diff --git a/simple_websockets/Makefile b/simple_websockets/Makefile new file mode 100644 index 0000000..24f9659 --- /dev/null +++ b/simple_websockets/Makefile @@ -0,0 +1,22 @@ +PROGRAM = websocktest +LDFLAGS = $(shell pkg-config --libs libwebsockets) -lpthread +SRCS = main.c que.c +CC = gcc +DEFINES += -DEBUG +CXX = gcc +CFLAGS = -Werror -Wextra $(DEFINES) $(shell pkg-config --cflags libwebsockets) +OBJS = $(SRCS:.c=.o) +all : $(PROGRAM) +$(PROGRAM) : $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +# some addition dependencies +# %.o: %.c +# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@ +#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS) +# @touch $@ + +clean: + /bin/rm -f *.o *~ +depend: + $(CXX) -MM $(CXX.SRCS) diff --git a/simple_websockets/Readme.md b/simple_websockets/Readme.md new file mode 100644 index 0000000..9857e84 --- /dev/null +++ b/simple_websockets/Readme.md @@ -0,0 +1,3 @@ +### Websockets test + + diff --git a/simple_websockets/dbg.h b/simple_websockets/dbg.h new file mode 100644 index 0000000..aecaf75 --- /dev/null +++ b/simple_websockets/dbg.h @@ -0,0 +1,39 @@ +/* + * dbg.h + * + * Copyright 2016 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#pragma once +#ifndef __DBG_H__ +#define __DBG_H__ + +#if defined EBUG || defined DEBUG +#ifndef DBG +#define DBG(...) do{fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");}while(0) +#endif +#else +#define DBG(...) +#endif + + +#ifndef _U_ + #define _U_ __attribute__((__unused__)) +#endif + +#endif // __DBG_H__ diff --git a/simple_websockets/main.c b/simple_websockets/main.c new file mode 100644 index 0000000..0e2a839 --- /dev/null +++ b/simple_websockets/main.c @@ -0,0 +1,224 @@ +/* + * main.c + * + * Copyright 2016 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbg.h" +#include "que.h" + +#define MESSAGE_LEN (512) +#define FRAME_SIZE (LWS_SEND_BUFFER_PRE_PADDING + MESSAGE_LEN + LWS_SEND_BUFFER_POST_PADDING) + + +char *client_IP = NULL; // IP of first connected client +volatile int stop = 0; +pthread_mutex_t command_mutex, ip_mutex; + +static void interrupt(_U_ int signo) { + stop = 1; +} + + +int ws_write(struct lws *wsi_in, char *str){ + if (!str || !wsi_in) + return -1; + unsigned char buf[FRAME_SIZE]; + int n; + size_t len = strlen(str); + if(len > MESSAGE_LEN) len = MESSAGE_LEN; + memcpy(buf + LWS_SEND_BUFFER_PRE_PADDING, str, len); + n = lws_write(wsi_in, buf + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT); + DBG("write %s\n", str); + return n; +} + +void websig(char *command){ + /*while(data_in_buf); + pthread_mutex_lock(&command_mutex); + strncpy(cmd_buf, command, CMDBUFLEN); + data_in_buf = 1; + pthread_mutex_unlock(&command_mutex);*/ + DBG("get message: %s\n", command); +} + +static int control_callback(struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len){ + char client_name[128]; + char client_ip[128]; + control_data *dat = (control_data *) user; + char *msg = (char*) in; + void parse_que_msg(control_data *d){ + char *M; + if((M = get_message_from_que(d))){ + ws_write(wsi, M); + } + } + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + memset(dat, 0, sizeof(control_data)); + pthread_mutex_lock(&ip_mutex); + lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), + client_name, 128, client_ip, 128); + if(!client_IP) + client_IP = strdup(client_ip); + else if(strcmp(client_IP, client_ip)){ + char buf[256]; + snprintf(buf, 255, "Already connected from %s.
Please, disconnect.", client_IP); + DBG("Already connected\n"); + put_message_to_que(buf, dat); + snprintf(buf, 255, "Try of connection from %s", client_ip); + glob_que(buf); + dat->already_connected = 1; + } + pthread_mutex_unlock(&ip_mutex); + lws_callback_on_writable(wsi); + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + if(dat->num) + parse_que_msg(dat); + if(!dat->already_connected && global_que.num) + parse_que_msg(&global_que); + usleep(500); + lws_callback_on_writable(wsi); + return 0; + break; + case LWS_CALLBACK_RECEIVE: + if(!dat->already_connected) + websig(msg); + break; + case LWS_CALLBACK_CLOSED: + DBG("closed\n"); + if(!dat->already_connected){ + pthread_mutex_lock(&ip_mutex); + free(client_IP); + client_IP = NULL; + pthread_mutex_unlock(&ip_mutex); + } + break; + } + return 0; +} + +struct lws_protocols protocols[] = { + { + "IRBIS_control", // name + control_callback, // callback + sizeof(control_data), // control_data_size + FRAME_SIZE, // max frame size / rx buffer + 0, NULL // id, user + }, + { NULL, NULL, 0, 0, 0, NULL} /* terminator */ +}; + +void *websock_thread(_U_ void *buf){ + struct lws_context_creation_info info; + struct lws_context *context; + + memset(&info, 0, sizeof info); + info.port = 9999; + info.iface = NULL; + info.protocols = protocols; + info.extensions = lws_get_internal_extensions(); + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + info.gid = -1; + info.uid = -1; + info.options = 0; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + stop = 1; + } + while (!stop) { + lws_service(context, 50); + } + usleep(10); + lws_context_destroy(context); + return NULL; +} + +void *worker_thread(_U_ void* buf){ + time_t t = time(NULL), oldt; + while(!stop){ + oldt = t; + if(client_IP){ + glob_que(ctime(&t)); + } + while((t = time(NULL)) == oldt) usleep(1000); + } +} + +static void main_proc(){ + pthread_t ws_thread, wr_thread; + if(pthread_create(&ws_thread, NULL, websock_thread, NULL)){ + fprintf(stderr, "Can't create websocket thread!\n"); + stop = 1; + return; + } + if(pthread_create(&wr_thread, NULL, worker_thread, NULL)){ + fprintf(stderr, "Can't create worker thread!\n"); + stop = 1; + return; + } + while(!stop){ + /* pthread_mutex_lock(&command_mutex); + if(data_in_buf) process_buf(cmd_buf); + data_in_buf = 0; + pthread_mutex_unlock(&command_mutex);*/ + usleep(1000); // give another treads some time to fill buffer + } + DBG("stop threads"); + pthread_cancel(ws_thread); + pthread_cancel(wr_thread); + pthread_join(ws_thread, NULL); + pthread_join(wr_thread, NULL); + DBG("return main_proc"); +} + +int main(void) { + signal(SIGINT, interrupt); + signal(SIGTERM, interrupt); + signal(SIGQUIT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + while(1){ + if(stop) return 0; + pid_t childpid = fork(); + if(childpid){ + DBG("Created child with PID %d\n", childpid); + wait(NULL); + DBG("Child %d died\n", childpid); + }else{ + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + main_proc(); + return 0; + } + } + return 0; +} diff --git a/simple_websockets/que.c b/simple_websockets/que.c new file mode 100644 index 0000000..e97a78d --- /dev/null +++ b/simple_websockets/que.c @@ -0,0 +1,47 @@ +/* + * que.c + * + * Copyright 2016 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include "que.h" + +char que[MESSAGE_LEN]; +control_data global_que; + +void put_message_to_que(char *msg, control_data *dat){ + int L = strlen(msg); + if(dat->num >= MESSAGE_QUE_SIZE) return; + ++dat->num; + if(L < 1 || L > MESSAGE_LEN - 1) L = MESSAGE_LEN - 1; + strncpy(dat->message[dat->idxwr], msg, L); + dat->message[dat->idxwr][L] = 0; + if((++(dat->idxwr)) >= MESSAGE_QUE_SIZE) dat->idxwr = 0; +} + +void glob_que(char *buf){ + put_message_to_que(buf, &global_que); +} + +char *get_message_from_que(control_data *dat){ + char *R = dat->message[dat->idxrd]; + if(dat->num <= 0) return NULL; + if((++dat->idxrd) >= MESSAGE_QUE_SIZE) dat->idxrd = 0; + --dat->num; + return R; +} diff --git a/simple_websockets/que.h b/simple_websockets/que.h new file mode 100644 index 0000000..1691ab6 --- /dev/null +++ b/simple_websockets/que.h @@ -0,0 +1,44 @@ +/* + * que.h + * + * Copyright 2016 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#pragma once +#ifndef __QUE_H__ +#define __QUE_H__ + +#define MESSAGE_LEN (512) +#define MESSAGE_QUE_SIZE (16) + + +typedef struct { + int num; + int idxwr; + int idxrd; + char message[MESSAGE_QUE_SIZE][MESSAGE_LEN]; + int already_connected; +} control_data; + +extern char que[]; +extern control_data global_que; +void glob_que(char *buf); +void put_message_to_que(char *msg, control_data *dat); +char *get_message_from_que(control_data *dat); + +//#define GLOB_MESG(...) do{snprintf(que, 512, __VA_ARGS__); glob_que();}while(0) +#endif // __QUE_H__ diff --git a/simple_websockets/www/test.html b/simple_websockets/www/test.html new file mode 100644 index 0000000..abf6959 --- /dev/null +++ b/simple_websockets/www/test.html @@ -0,0 +1,86 @@ + + + Testing websockets + + + + +
+
+
No connection
+
+
+ + diff --git a/xor.c b/xor.c new file mode 100644 index 0000000..05bd1e9 --- /dev/null +++ b/xor.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include + +void xor(char *buf, char *pattern, size_t l){ + size_t i; + for(i = 0; i < l; ++i) + printf("%c", buf[i] ^ pattern[i]); +} + +int main(int argc, char **argv){ + if(argc != 3){ + printf("Usage: %s \n", argv[0]); + return -1; + } + char *key = strdup(argv[2]); + size_t n = 0, keylen = strlen(key); + char *buff = malloc(keylen); + int f = open(argv[1], O_RDONLY); + if(f < 0){ + perror("Can't open file"); + return f; + } + do{ + n = read(f, buff, keylen); + xor(buff, key, n); + }while(n); + return 0; +}