added websocket smol server
This commit is contained in:
+7
-1
@@ -24,4 +24,10 @@ add_executable(
|
|||||||
src/smol-http-pthreads.c
|
src/smol-http-pthreads.c
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS smol smol-pthreads smol-http smol-http-pthreads DESTINATION bin)
|
add_executable(
|
||||||
|
ws
|
||||||
|
src/ws.c
|
||||||
|
)
|
||||||
|
target_link_libraries(ws ssl crypto)
|
||||||
|
|
||||||
|
install(TARGETS smol smol-pthreads smol-http smol-http-pthreads ws DESTINATION bin)
|
||||||
@@ -9,7 +9,7 @@ default: all
|
|||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
build: smol smol-pthreads smol-http smol-http-pthreads
|
build: smol smol-pthreads smol-http smol-http-pthreads ws
|
||||||
|
|
||||||
smol:
|
smol:
|
||||||
$(CC) -o $(BIN)/smol $(SRC)/smol.c -lcrypto -lssl $(CFLAGS)
|
$(CC) -o $(BIN)/smol $(SRC)/smol.c -lcrypto -lssl $(CFLAGS)
|
||||||
@@ -23,6 +23,9 @@ smol-http:
|
|||||||
smol-http-pthreads:
|
smol-http-pthreads:
|
||||||
$(CC) -o $(BIN)/smol-http-pthreads $(SRC)/smol-http-pthreads.c $(CFLAGS)
|
$(CC) -o $(BIN)/smol-http-pthreads $(SRC)/smol-http-pthreads.c $(CFLAGS)
|
||||||
|
|
||||||
|
ws:
|
||||||
|
$(CC) -o $(BIN)/ws $(SRC)/ws.c $(CFLAGS) -lcrypto -lssl
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
if [ -d $(BIN) ]; then rm -rv $(BIN);mkdir -pv $(BIN);else mkdir -pv $(BIN);fi
|
if [ -d $(BIN) ]; then rm -rv $(BIN);mkdir -pv $(BIN);else mkdir -pv $(BIN);fi
|
||||||
|
|
||||||
@@ -32,6 +35,7 @@ install:
|
|||||||
$(STRIP) $(PREFIX)/bin/smol-pthreads
|
$(STRIP) $(PREFIX)/bin/smol-pthreads
|
||||||
$(STRIP) $(PREFIX)/bin/smol-http
|
$(STRIP) $(PREFIX)/bin/smol-http
|
||||||
$(STRIP) $(PREFIX)/bin/smol-http-pthreads
|
$(STRIP) $(PREFIX)/bin/smol-http-pthreads
|
||||||
|
$(STRIP) $(PREFIX)/bin/ws
|
||||||
|
|
||||||
uninstall: remove
|
uninstall: remove
|
||||||
|
|
||||||
@@ -40,3 +44,4 @@ remove:
|
|||||||
if [ -f $(PREFIX)/bin/smol-pthreads ];then rm -v $(PREFIX)/bin/smol-pthreads;fi
|
if [ -f $(PREFIX)/bin/smol-pthreads ];then rm -v $(PREFIX)/bin/smol-pthreads;fi
|
||||||
if [ -f $(PREFIX)/bin/smol-http ];then rm -v $(PREFIX)/bin/smol-http;fi
|
if [ -f $(PREFIX)/bin/smol-http ];then rm -v $(PREFIX)/bin/smol-http;fi
|
||||||
if [ -f $(PREFIX)/bin/smol-http-pthreads ];then rm -v $(PREFIX)/bin/smol-http-pthreads;fi
|
if [ -f $(PREFIX)/bin/smol-http-pthreads ];then rm -v $(PREFIX)/bin/smol-http-pthreads;fi
|
||||||
|
if [ -f $(PREFIX)/bin/ws ];then rm -v $(PREFIX)/bin/ws;fi
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="time"></h1>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const uptime = document.querySelector('#time')
|
||||||
|
const ws = new WebSocket('ws://172.16.17.21:8080');
|
||||||
|
ws.onopen = () => {
|
||||||
|
ws.send('Hello Server!');
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onmessage = (e) => {
|
||||||
|
console.log('Received:', e.data);
|
||||||
|
var jsondata = JSON.parse(e.data);
|
||||||
|
if (jsondata.uptime.hour === '0' && jsondata.uptime.minute === '0') {
|
||||||
|
uptime.innerHTML = jsondata.uptime.second + "s "
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (jsondata.uptime.hour === '0') {
|
||||||
|
uptime.innerHTML = jsondata.uptime.minute + "m " + jsondata.uptime.second + "s "
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uptime.innerHTML = jsondata.uptime.hour + "h " + jsondata.uptime.minute + "m " + jsondata.uptime.second + "s "
|
||||||
|
}
|
||||||
@@ -25,3 +25,10 @@ executable(
|
|||||||
'src/smol-http-pthreads.c',
|
'src/smol-http-pthreads.c',
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
executable(
|
||||||
|
'ws',
|
||||||
|
'src/ws.c',
|
||||||
|
dependencies: deps,
|
||||||
|
install: true,
|
||||||
|
)
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/buffer.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#define PORT 8080
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
char* base64_encode(const unsigned char* input, int length) {
|
||||||
|
BIO *bio, *b64;
|
||||||
|
BUF_MEM *bufferPtr;
|
||||||
|
|
||||||
|
b64 = BIO_new(BIO_f_base64());
|
||||||
|
bio = BIO_new(BIO_s_mem());
|
||||||
|
bio = BIO_push(b64, bio);
|
||||||
|
|
||||||
|
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
BIO_write(bio, input, length);
|
||||||
|
BIO_flush(bio);
|
||||||
|
BIO_get_mem_ptr(bio, &bufferPtr);
|
||||||
|
|
||||||
|
// Создаем копию данных, так как буфер BIO будет очищен
|
||||||
|
char *output = (char *)malloc(bufferPtr->length + 1);
|
||||||
|
memcpy(output, bufferPtr->data, bufferPtr->length);
|
||||||
|
output[bufferPtr->length] = '\0';
|
||||||
|
|
||||||
|
BIO_free_all(bio);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* compute_accept_key(const char* client_key) {
|
||||||
|
char combined[256];
|
||||||
|
sprintf(combined, "%s%s", client_key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||||
|
unsigned char sha_result[SHA_DIGEST_LENGTH];
|
||||||
|
SHA1((unsigned char*)combined, strlen(combined), sha_result);
|
||||||
|
return base64_encode(sha_result, SHA_DIGEST_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_fully(int socket, unsigned char* buffer, int length) {
|
||||||
|
int total = 0;
|
||||||
|
while(total < length) {
|
||||||
|
int readed = read(socket, buffer + total, length - total);
|
||||||
|
if(readed <= 0) return readed;
|
||||||
|
total += readed;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int server_fd, client_socket;
|
||||||
|
struct sockaddr_in address;
|
||||||
|
int addrlen = sizeof(address);
|
||||||
|
|
||||||
|
if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||||
|
perror("socket failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
address.sin_port = htons(PORT);
|
||||||
|
|
||||||
|
if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||||
|
perror("bind failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(listen(server_fd, 3) < 0) {
|
||||||
|
perror("listen");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Server listening on port %d\n", PORT);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
|
||||||
|
perror("accept");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Client connected\n");
|
||||||
|
char buffer[BUFFER_SIZE] = {0};
|
||||||
|
int valread = read(client_socket, buffer, BUFFER_SIZE);
|
||||||
|
|
||||||
|
if(valread < 0) {
|
||||||
|
perror("read");
|
||||||
|
close(client_socket);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strstr(buffer, "Upgrade: websocket") && strstr(buffer, "Connection: Upgrade")) {
|
||||||
|
char* key_start = strstr(buffer, "Sec-WebSocket-Key: ");
|
||||||
|
if(key_start) {
|
||||||
|
key_start += 19;
|
||||||
|
char* key_end = strstr(key_start, "\r\n");
|
||||||
|
if(key_end) {
|
||||||
|
char client_key[256];
|
||||||
|
int key_len = key_end - key_start;
|
||||||
|
strncpy(client_key, key_start, key_len);
|
||||||
|
client_key[key_len] = '\0';
|
||||||
|
|
||||||
|
char* accept_key = compute_accept_key(client_key);
|
||||||
|
|
||||||
|
char response[512];
|
||||||
|
sprintf(response,
|
||||||
|
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
"Upgrade: websocket\r\n"
|
||||||
|
"Connection: Upgrade\r\n"
|
||||||
|
"Sec-WebSocket-Accept: %s\r\n\r\n",
|
||||||
|
accept_key);
|
||||||
|
|
||||||
|
if (send(client_socket, response, strlen(response), 0) <= 0) {
|
||||||
|
printf("send failed\n");
|
||||||
|
free(accept_key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(accept_key);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
unsigned char header[2];
|
||||||
|
if(read_fully(client_socket, header, 2) <= 0) break;
|
||||||
|
|
||||||
|
int opcode = header[0] & 0x0F;
|
||||||
|
int mask = (header[1] >> 7) & 1;
|
||||||
|
int length = header[1] & 0x7F;
|
||||||
|
|
||||||
|
if(length == 126) {
|
||||||
|
unsigned char ext_len[2];
|
||||||
|
read_fully(client_socket, ext_len, 2);
|
||||||
|
length = (ext_len[0] << 8) | ext_len[1];
|
||||||
|
} else if(length == 127) {
|
||||||
|
printf("Large frames not supported\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char masking_key[4];
|
||||||
|
if(mask) {
|
||||||
|
read_fully(client_socket, masking_key, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* data = malloc(length + 1);
|
||||||
|
read_fully(client_socket, data, length);
|
||||||
|
|
||||||
|
if(mask) {
|
||||||
|
for(int i = 0; i < length; i++) {
|
||||||
|
data[i] ^= masking_key[i % 4];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[length] = '\0';
|
||||||
|
|
||||||
|
if(opcode == 0x08) break; // Close frame
|
||||||
|
|
||||||
|
printf("Received: %s\n", data);
|
||||||
|
|
||||||
|
// Prepare response
|
||||||
|
while (1) {
|
||||||
|
char timebuffer[BUFSIZ];
|
||||||
|
memset(&timebuffer, '\0', BUFSIZ);
|
||||||
|
struct sysinfo x;
|
||||||
|
if (sysinfo(&x) == 0) {
|
||||||
|
sprintf(timebuffer,
|
||||||
|
"{\"uptime\":{\"hour\":\"%llu\",\"minute\":\"%llu\",\"second\":\"%llu\"}}",
|
||||||
|
(unsigned long long)x.uptime / 3600ULL, (unsigned long long)x.uptime / 60ULL % 60ULL, (unsigned long long)x.uptime % 60ULL);
|
||||||
|
}
|
||||||
|
printf("%s\n", timebuffer);
|
||||||
|
unsigned char response_header[2] = {0x81, strlen(timebuffer)};
|
||||||
|
if (send(client_socket, response_header, 2, 0) <= 0) {
|
||||||
|
printf("send failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (send(client_socket, timebuffer, strlen(timebuffer), 0) <= 0) {
|
||||||
|
printf("send failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Client disconnected\n");
|
||||||
|
close(client_socket);
|
||||||
|
}
|
||||||
|
close(server_fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user