Added fragmented message sending to the socket server

This commit is contained in:
2026-03-15 09:07:58 +10:00
parent 57069c8c0e
commit 68e3a60fd8
2 changed files with 218 additions and 48 deletions
+208 -47
View File
@@ -11,14 +11,17 @@
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "external/cJSON.h"
#include "configHandler.h"
#include "libopensubsonic/utils.h"
#include "libopensubsonic/httpclient.h"
#include "libopensubsonic/endpoint_getStarred.h"
#include "socket.h"
#define SOCKET_PATH "/tmp/ossp_socket" // TODO Make this configurable through the configuration file
static int server_fd = -1;
static int client_fd = -1;
static int rc = -1;
@@ -27,9 +30,12 @@ struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
extern configHandler_config_t* configObj;
bool isSocketHandlerLoopRunning = false;
void socketHandler_read();
void socketHandler_performAction(int id, char** retDataStr);
int SockSig_Length = 4;
@@ -37,12 +43,12 @@ const uint32_t OSSP_Sock_ACK = 0x7253FF87;
const uint32_t OSSP_Sock_CliConn = 0xE3566C2E;
const uint32_t OSSP_Sock_GetConnInfo = 0x8E4F6B01;
const uint32_t OSSP_Sock_Size = 0x1F7E8BCF;
const uint32_t OSSP_Sock_ClientGetReq = 0x210829CF;
const uint32_t OSSP_Sock_FragMsgACK = 0x32A093B4;
void socket_setup() {
int socketHandler_init() {
printf("[SocketHandler] Initializing.\n");
// Create server socket, and ensure that the socket file is removed
@@ -52,12 +58,12 @@ void socket_setup() {
// TODO
}
unlink(SOCKET_PATH);
unlink(configObj->client_socket_path);
// Bind server socket to SOCKET_PATH
memset(&server_addr, 0, sizeof(struct sockaddr_un));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
strncpy(server_addr.sun_path, configObj->client_socket_path, sizeof(server_addr.sun_path) - 1);
rc = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_un));
if (rc == -1) {
@@ -83,27 +89,56 @@ void socket_setup() {
}
}
//socketHandler_read();
// Initialize connection with client
socketHandler_initClientConnection();
// Enter socket handler loop
isSocketHandlerLoopRunning = true;
//while (isSocketHandlerLoopRunning) {
//
//}
printf("------------------------------\n");
while (1) {
// Receive ClientGetReq with Size
int size = 0;
socketHandler_receiveCliGetReq(&size);
printf("Size to alloc: %d bytes\n", size);
char* reqBuf = malloc(size);
// Receive ClientGetReq with Size
int size = 0;
socketHandler_receiveCliGetReq(&size);
printf("Size to alloc: %d bytes\n", size);
char* reqBuf = malloc(size);
// Send ACK
socketHandler_sendAck();
// Send ACK
socketHandler_sendAck();
// Receive JSON data
socketHandler_receiveJson(&reqBuf, size);
printf("Received JSON: %s\n", reqBuf);
// Receive JSON data
socketHandler_receiveJson(&reqBuf, size);
printf("Received JSON: %s\n", reqBuf);
// Parse client request JSON
cJSON* cliReqJson = cJSON_Parse(reqBuf);
int id = 0;
OSS_Pioj(&id, cliReqJson, "id");
printf("Action ID is %d\n", id);
// Perform action
char* retData = NULL;
socketHandler_performAction(id, &retData);
int retDataSize = strlen(retData);
// Send size back to client
socketHandler_sendSize(retDataSize);
// Wait for ACK
socketHandler_receiveAck();
// Send JSON
socketHandler_sendJson(retData, retDataSize);
// Wait for ACK
socketHandler_receiveAck();
}
}
void socketHandler_cleanup() {
@@ -111,7 +146,66 @@ void socketHandler_cleanup() {
close(client_fd);
close(server_fd);
unlink(SOCKET_PATH);
unlink(configObj->client_socket_path);
}
void socketHandler_performAction(int id, char** retDataStr) {
switch (id) {
case OSSP_SOCKET_ACTION_GETSTARREDSONGS:
printf("[SocketHandler] Client requested Starred Songs.\n");
// Fetch /getStarred endpoint
opensubsonic_httpClient_URL_t* url = malloc(sizeof(opensubsonic_httpClient_URL_t));
opensubsonic_httpClient_URL_prepare(&url);
url->endpoint = OPENSUBSONIC_ENDPOINT_GETSTARRED;
opensubsonic_httpClient_formUrl(&url);
opensubsonic_getStarred_struct* getStarredStruct;
opensubsonic_httpClient_fetchResponse(&url, (void**)&getStarredStruct);
opensubsonic_httpClient_URL_cleanup(&url); // Free URL
// Format into JSON
cJSON* retData = cJSON_CreateObject();
cJSON_AddItemToObject(retData, "songCount", cJSON_CreateNumber(getStarredStruct->songCount));
cJSON* songArray = cJSON_CreateArray();
cJSON_AddItemToObject(retData, "songs", songArray);
for (int i = 0; i < getStarredStruct->songCount; i++) {
cJSON* song = cJSON_CreateObject();
cJSON_AddItemToObject(song, "title", cJSON_CreateString(getStarredStruct->songs[i].title));
cJSON_AddItemToObject(song, "album", cJSON_CreateString(getStarredStruct->songs[i].album));
cJSON_AddItemToObject(song, "artist", cJSON_CreateString(getStarredStruct->songs[i].artist));
cJSON_AddItemToObject(song, "id", cJSON_CreateString(getStarredStruct->songs[i].id));
cJSON_AddItemToObject(song, "size", cJSON_CreateNumber(getStarredStruct->songs[i].size));
cJSON_AddItemToObject(song, "duration", cJSON_CreateNumber(getStarredStruct->songs[i].duration));
cJSON_AddItemToObject(song, "albumId", cJSON_CreateString(getStarredStruct->songs[i].albumId));
cJSON_AddItemToObject(song, "artistId", cJSON_CreateString(getStarredStruct->songs[i].artistId));
// Convert cover art ID to URL
opensubsonic_httpClient_URL_t* coverArtUrl = malloc(sizeof(opensubsonic_httpClient_URL_t));
opensubsonic_httpClient_URL_prepare(&coverArtUrl);
coverArtUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART;
coverArtUrl->id = strdup(getStarredStruct->songs[i].coverArt);
opensubsonic_httpClient_formUrl(&coverArtUrl);
cJSON_AddItemToObject(song, "coverArtUrl", cJSON_CreateString(coverArtUrl->formedUrl));
cJSON_AddItemToArray(songArray, song);
opensubsonic_httpClient_URL_cleanup(&coverArtUrl); // Free Cover Art URL
}
*retDataStr = cJSON_PrintUnformatted(retData);
cJSON_Delete(retData);
opensubsonic_getStarred_struct_free(&getStarredStruct); // Free Struct
break;
default:
printf("[SocketHandler] Unknown action.\n");
break;
}
}
@@ -119,20 +213,7 @@ void socketHandler_cleanup() {
// Byte Array to uint32_t Big Endian
uint32_t util_byteArrToUint32BE(char buf[]) {
// NOTE: I could use a combination of memcpy() and htons() here, but bitshifting is a single move
uint32_t retVal = 0x0;
retVal = buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24;
return retVal;
}
// Byte Array to uint32_t Little Endian
uint32_t util_byteArrToUint32LE(char buf[]) {
uint32_t retVal = 0x0;
retVal = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
return retVal;
}
int socketHandler_initClientConnection() {
/*
@@ -214,18 +295,18 @@ int socketHandler_sendAck() {
}
int socketHandler_receiveAck() {
char buf[4] = { 0x00 };
uint8_t buf[4] = { 0x00 };
rc = read(client_fd, buf, SockSig_Length);
if (rc != SockSig_Length) {
printf("[SocketHandler] Failed to receive OSSP_Sock_ACK (Signature is the wrong length).\n");
return 1;
}
uint32_t AckBE = util_byteArrToUint32LE(buf);
uint32_t AckLE = socketHandlerUtil_byteArrToUint32LE(buf);
rc = memcmp(&AckBE, &OSSP_Sock_ACK, SockSig_Length);
rc = memcmp(&AckLE, &OSSP_Sock_ACK, SockSig_Length);
if (rc != 0) {
printf("[SocketHandler] Failed to receive OSSP_Sock_ACK (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_ACK, AckBE);
printf("[SocketHandler] Failed to receive OSSP_Sock_ACK (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_ACK, AckLE);
return 1;
}
printf("[SocketHandler] Received OSSP_Sock_ACK.\n");
@@ -234,6 +315,27 @@ int socketHandler_receiveAck() {
int socketHandler_receiveFragMsgAck() {
uint8_t buf[4] = { 0x00 };
rc = read(client_fd, buf, SockSig_Length);
if (rc != SockSig_Length) {
printf("[SocketHandler] Failed to receive OSSP_Sock_FragMsgACK (Signature is the wrong length).\n");
return 1;
}
uint32_t AckLE = socketHandlerUtil_byteArrToUint32LE(buf);
rc = memcmp(&AckLE, &OSSP_Sock_FragMsgACK, SockSig_Length);
if (rc != 0) {
printf("[SocketHandler] Failed to receive OSSP_Sock_FragMsgACK (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_FragMsgACK, AckLE);
return 1;
}
printf("[SocketHandler] Received OSSP_Sock_FragMsgACK.\n");
return 0;
}
int socketHandler_receiveCliConn() {
char buf[4] = { 0x00 };
rc = read(client_fd, buf, SockSig_Length);
@@ -242,12 +344,11 @@ int socketHandler_receiveCliConn() {
return 1;
}
//uint32_t CliConnBE = util_byteArrToUint32BE(buf);
uint32_t CliConnBE = util_byteArrToUint32LE(buf);
uint32_t CliConnLE = socketHandlerUtil_byteArrToUint32LE(buf);
rc = memcmp(&CliConnBE, &OSSP_Sock_CliConn, SockSig_Length);
rc = memcmp(&CliConnLE, &OSSP_Sock_CliConn, SockSig_Length);
if (rc != 0) {
printf("[SocketHandler] Failed to receive OSSP_Sock_CliConn (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_CliConn, CliConnBE);
printf("[SocketHandler] Failed to receive OSSP_Sock_CliConn (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_CliConn, CliConnLE);
return 1;
}
printf("[SocketHandler] Received OSSP_Sock_CliConn.\n");
@@ -264,11 +365,11 @@ int socketHandler_receiveGetConnInfo() {
return 1;
}
uint32_t GetConnInfoBE = util_byteArrToUint32LE(buf);
uint32_t GetConnInfoLE = socketHandlerUtil_byteArrToUint32LE(buf);
rc = memcmp(&GetConnInfoBE, &OSSP_Sock_GetConnInfo, SockSig_Length);
rc = memcmp(&GetConnInfoLE, &OSSP_Sock_GetConnInfo, SockSig_Length);
if (rc != 0) {
printf("[SocketHandler] Failed to receive OSSP_Sock_GetConnInfo (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_GetConnInfo, GetConnInfoBE);
printf("[SocketHandler] Failed to receive OSSP_Sock_GetConnInfo (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_GetConnInfo, GetConnInfoLE);
return 1;
}
printf("[SocketHandler] Received OSSP_Sock_GetConnInfo.\n");
@@ -322,7 +423,7 @@ int socketHandler_receiveSize(uint32_t* size) {
int socketHandler_receiveCliGetReq(int* size) {
char buf[8] = { 0x00 };
uint8_t buf[8] = { 0x00 };
rc = read(client_fd, buf, 8);
if (rc != 8) {
printf("[SocketHandler] Failed to receive OSSP_Sock_ClientGetReq (Invalid size).\n");
@@ -355,12 +456,53 @@ int socketHandler_receiveJson(char** data, int size) {
int socketHandler_sendJson(char* json, int size) {
printf("[SocketHandler] Sending JSON.\n");
/*
* Notes on IPC messaging sizes and fragmented sending
* - Able to get macOS max IPC message size with sysctl kern.ipc.maxsockbuf
* - Sticking with 8192 bytes for safety
* - IPC messages REQUIRES contiguous memory
*/
rc = send(client_fd, json, size, 0);
if (rc != size) {
printf("[SocketHandler] Failed to send JSON.\n");
return 1;
float msgCount_intermediate = (float)size / 8192;
int msgCount = (int)ceil(msgCount_intermediate);
int msgCount_remainer = size % 8192; // If using fragmented messaging, find remaining bytes in last message
if (msgCount > 1) {
// Fragmented messaging required
printf("[SocketHandler] Sending fragmented payload in %d chunks.\n", msgCount);
for (int i = 0; i < msgCount; i++) {
printf("[SocketHandler] Sending fragment %d.\n", i);
if (i == msgCount - 1) {
// Last fragment, use remainder bytes as message length
rc = send(client_fd, json + (i * 8192), msgCount_remainer, 0);
if (rc != msgCount_remainer) {
printf("[SocketHandler] Failed to send JSON.\n");
return 1;
}
} else {
// Use 8192 bytes as the message length
rc = send(client_fd, json + (i * 8192), 8192, 0);
if (rc != 8192) {
printf("[SocketHandler] Failed to send JSON.\n");
return 1;
}
}
// Wait for OSSP_Sock_FragMsgACK
socketHandler_receiveFragMsgAck();
}
} else {
// Payload fits inside single message
printf("[SocketHandler] Sending non-fragmented payload.\n");
rc = send(client_fd, json, size, 0);
if (rc != size) {
printf("[SocketHandler] Failed to send JSON.\n");
return 1;
}
}
return 0;
}
@@ -378,3 +520,22 @@ int socketHandler_processClientGetReq() {
// Step 4 - --
}
/*
* Utility Functions
*/
// Byte Array to uint32_t Big Endian
uint32_t socketHandlerUtil_byteArrToUint32BE(uint8_t buf[]) {
// NOTE: I could use a combination of memcpy() and htons() here, but bitshifting is a single move
uint32_t retVal = 0x0;
retVal = buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24;
return retVal;
}
// Byte Array to uint32_t Little Endian
uint32_t socketHandlerUtil_byteArrToUint32LE(uint8_t buf[]) {
uint32_t retVal = 0x0;
retVal = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
return retVal;
}
+10 -1
View File
@@ -2,6 +2,10 @@
#define _SOCKET_H
#include <stdint.h>
#define OSSP_SOCKET_ACTION_GETSTARREDSONGS 101
#define OSSP_SOCKET_ACTION_NOW_PLAYING 201
#define OSSP_SOCKET_ACTION_STATS 202
typedef struct {
uint32_t signature;
uint32_t size;
@@ -12,11 +16,13 @@ typedef struct {
uint32_t size;
} __attribute__((packed)) OSSP_Sock_ClientGetReq_t;
void socket_setup();
int socketHandler_init();
void socketHandler_cleanup();
int socketHandler_initClientConnection();
int socketHandler_sendAck();
int socketHandler_receiveAck();
int socketHandler_receiveFragMsgAck();
int socketHandler_receiveCliConn();
int socketHandler_receiveGetConnInfo();
@@ -27,4 +33,7 @@ int socketHandler_receiveCliGetReq(int* size);
int socketHandler_receiveJson(char** data, int size);
int socketHandler_sendJson(char* json, int size);
uint32_t socketHandlerUtil_byteArrToUint32BE(uint8_t buf[]);
uint32_t socketHandlerUtil_byteArrToUint32LE(uint8_t buf[]);
#endif