added websocket smol server
This commit is contained in:
+7
-1
@@ -24,4 +24,10 @@ add_executable(
|
||||
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
|
||||
|
||||
build: smol smol-pthreads smol-http smol-http-pthreads
|
||||
build: smol smol-pthreads smol-http smol-http-pthreads ws
|
||||
|
||||
smol:
|
||||
$(CC) -o $(BIN)/smol $(SRC)/smol.c -lcrypto -lssl $(CFLAGS)
|
||||
@@ -23,6 +23,9 @@ smol-http:
|
||||
smol-http-pthreads:
|
||||
$(CC) -o $(BIN)/smol-http-pthreads $(SRC)/smol-http-pthreads.c $(CFLAGS)
|
||||
|
||||
ws:
|
||||
$(CC) -o $(BIN)/ws $(SRC)/ws.c $(CFLAGS) -lcrypto -lssl
|
||||
|
||||
clean:
|
||||
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-http
|
||||
$(STRIP) $(PREFIX)/bin/smol-http-pthreads
|
||||
$(STRIP) $(PREFIX)/bin/ws
|
||||
|
||||
uninstall: remove
|
||||
|
||||
@@ -39,4 +43,5 @@ remove:
|
||||
if [ -f $(PREFIX)/bin/smol ];then rm -v $(PREFIX)/bin/smol;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-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 "
|
||||
}
|
||||
@@ -24,4 +24,11 @@ executable(
|
||||
'smol-http-pthreads',
|
||||
'src/smol-http-pthreads.c',
|
||||
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