Added fragmented message sending to the socket server
This commit is contained in:
+208
-47
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user