From e3c2a8d6caa2826710ae8ff017ebdb8f48428753 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 7 May 2023 06:50:35 -0500 Subject: [PATCH] Add first implementation of multicast functions and multicastclient.c test program. Next step will be to implement this when rig is opened. This will turn Hamlib into a polling system in addition to polling -- caching will handle repeated requests This should mean basic functions of freq, mode, split, ptt (and others when implemented) will have almost immediate responses as only the cached values should be used. https://github.com/Hamlib/Hamlib/issues/695 --- bindings/csharp/multicast/README.txt | 28 +- bindings/csharp/multicast/multicast.cs | 19 + bindings/csharp/multicast/multicast.csproj | 2 +- bindings/csharp/multicast/test.json | 105 +----- include/hamlib/rig.h | 16 + src/multicast.c | 396 +++++++++++++++++++++ src/multicast.h | 47 +++ tests/multicastclient.c | 88 +++++ 8 files changed, 603 insertions(+), 98 deletions(-) create mode 100644 src/multicast.c create mode 100644 src/multicast.h create mode 100644 tests/multicastclient.c diff --git a/bindings/csharp/multicast/README.txt b/bindings/csharp/multicast/README.txt index 2e4004b96..fbb130f50 100644 --- a/bindings/csharp/multicast/README.txt +++ b/bindings/csharp/multicast/README.txt @@ -2,15 +2,15 @@ Works on Windows and Linux Requires you get dotnet installed of course -For Wiundows install Visual Studio or such to get dotnet +For Windows install Visual Studio or such to get dotnet -On Ubuntu 20.04 it was this +On Ubuntu 21.04 it was this apt install snap apt install mono-complete -snap install dotnet-sdk --classic --channel=5.0 +snap install dotnet-sdk --classic --channel=6.0 snap alias dotnet-sdk.dotnet dotnet -snap install dotnet-runtime-50 --classic -snap alias dotnet-runtime-50.dotnet dotnet +snap install dotnet-runtime-60 --classic +snap alias dotnet-runtime-60.dotnet dotnet export DOTNET_ROOT=/snap/dotnet-sdk/current Once dotnet is OK @@ -19,4 +19,20 @@ dotnet build You should then be able to run -./bin/Debug/net5.0/multicast +./bin/Debug/net6.0/multicast + +====================================================== +Waiting for Net 7.0/8.0 to be in Ubunut main packages +Following did not work + +sudo apt remove 'dotnet*' 'aspnet*' 'netstandard*' +touch /etc/apt/preferences +// add to preferences +Package: dotnet* aspnet* netstandard* +Pin: origin "archive.ubuntu.com" +Pin-Priority: -10 +// end preferences +snap remove dotnet-sdk +snap install dotnet-sdk --classic --channel=7.0 +snap alias dotnet-runtime-70.dotnet dotnet + diff --git a/bindings/csharp/multicast/multicast.cs b/bindings/csharp/multicast/multicast.cs index 6e321cd09..9640e7704 100644 --- a/bindings/csharp/multicast/multicast.cs +++ b/bindings/csharp/multicast/multicast.cs @@ -9,6 +9,7 @@ namespace HamlibMultiCast { public class HamlibMulticastClient { + public string __comment1__; public class VFO { public string Name; @@ -17,19 +18,25 @@ namespace HamlibMultiCast public int Width; public bool RX; public bool TX; + public int WidthLower; + public int WidthUpper; } + public string __comment_spectrum__; public class SpectrumClass { public string Name; public int Length; + public string __comment_spectrum_data__; public string Data; public string Type; public int MinLevel; public int MaxLevel; public int MinStrength; public int MaxStrength; + public string __comment_spectrum_center__; public double CenterFreq; public int Span; + public string __comment_spectrum_fixed__; public double LowFreq; public double HighFreq; } @@ -38,14 +45,26 @@ namespace HamlibMultiCast public string Command; public string Status; } + public string __comment_gps__; + public class GPS { + public double Latitude; + public double Longitude; + public double Altitude; + } public string ID; + public string VFOCurr; public List VFOs { get; set; } public bool Split; + public string __comment_time__; + public string Time; public bool SatMode; public string Rig; public string App; + public string __comment_version__; public string Version; + public string __comment_seq__; public UInt32 Seq; + public string __comment_crc__; public string CRC; public List Spectra { get; set; } } diff --git a/bindings/csharp/multicast/multicast.csproj b/bindings/csharp/multicast/multicast.csproj index d08b4c72a..81870de07 100644 --- a/bindings/csharp/multicast/multicast.csproj +++ b/bindings/csharp/multicast/multicast.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net6.0 diff --git a/bindings/csharp/multicast/test.json b/bindings/csharp/multicast/test.json index 91828ab79..ba531aa4b 100644 --- a/bindings/csharp/multicast/test.json +++ b/bindings/csharp/multicast/test.json @@ -1,93 +1,16 @@ +"ID": "FT-991:/dev/ttyUSB0", +"Time": 2023-05-05T04:32:47.834952-0000, +"Sequence": 892, +"VFOCurr": "VFOA", +"VFOs": [ { - "__comment1__": "customizable rig identification -- will allow multiple rigs to be on the multicast", - "ID": "Rig#1", - "VFOs": [ - { - "Name": "VFOA", - "Freq": 14074000, - "Mode": "USB", - "Width": 2800, - "WidthLower": 200, - "WidthUpper": 3000, - "RX": true, - "TX": false - }, - { - "Name": "VFOB", - "Freq": 14076000, - "Mode": "USB", - "Width": 2800, - "WidthLower": 200, - "WidthUpper": 3000, - "RX": false, - "TX": true - }], - - "__comment_spectrum__": "Rigs that have spectrum output may include this data", - "Spectra": [ - { - "Name": "Main", - "Length": 475, - "__comment_spectrum_data__": "2-char hex bytes so data len=2*Length", - "Data": "00AAFF75BD2AAA...", - "Type": "FIXED|CENTER", - "MinLevel": 0, - "MaxLevel": 140, - "MinStrength": -100, - "MaxStrength": 0, - - "__comment_spectrum_center__": "If Type=CENTER, the following fields will be present:", - "CenterFreq": 14267000, - "Span": 25000, - - "__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:", - "LowFreq": 14000000, - "HighFreq": 14250000 - }, - { - "Name": "Sub", - "Length": 475, - "__comment_spectrum_data__": "2-char hex bytes so data len=2*Length", - "Data": "00AAFF75BD2AAA...", - "Type": "FIXED|CENTER", - "MinLevel": 0, - "MaxLevel": 140, - "MinStrength": -100, - "MaxStrength": 0, - - "__comment_spectrum_center__": "If Type=CENTER, the following fields will be present:", - "CenterFreq": 14267000, - "Span": 25000, - - "__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:", - "LowFreq": 14000000, - "HighFreq": 14250000 - }], - "LastCommand": { - "ID": "MyApp 123", - "Command": "set_freq VFOA 14074000", - "Status": "OK" - }, - "__comment_gps__": "Rigs that have gps output may include this data Lat -180 to 180 Lat -90 to 90, altitude in meters", - "GPS": - { - "Latitude": 31.6543, - "Longitude": -91.6543, - "Altitude": 640.1, - "__comment_gps__": "Time is in UTC gps synchronized for this block", - "Time": "2023-01-18T14:29:18.740561" - }, - - "Split": true, - "SatMode": false, - "__comment_Time__": "Time is from internal rig not GPS synced so may be inaccurate -- see GPS Time", - "Time": "2023-01-18T14:29:18.740561", - "Rig": "Dummy", - "App": "Hamlib", - "__comment_version__": "protocol version date YYYYMMDD", - "Version": "20210520", - "__comment_seq__": "Seq is 1-up sequence number 32-bit -- wraps around to 1 from 2^32-1", - "Seq": 1, - "__comment_crc__": "32-bit CRC of entire JSON record replacing the CRC value with 0x00000000", - "CRC": "0x00000000" +{ +, +"Name": "VFOA", +"Freq": 14086800, +"Mode": "AM", +"Width": 9000, +"RX": True, +"TX": False } + diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index d8d5ad3f8..69662c75a 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2464,6 +2464,21 @@ struct rig_cache { int satmode; // if rig is in satellite mode }; +/** + * \brief Multicast data items the are unique per rig instantiation + * This is meant for internal Hamlib use only + */ +#include +struct multicast_s +{ + int multicast_running; + int sock; + int seqnumber; + int runflag; // = 0; + struct ip_mreq mreq; // = {0}; + pthread_t threadid; + struct sockaddr_in dest_addr; // = {0}; +}; /** * \brief Rig state containing live data and customized fields. @@ -2594,6 +2609,7 @@ struct rig_state { char client_version[32]; /*! +#include +#include +#include +#include +#include +//#include +#include +#include "hamlib/rig.h" +#include "misc.h" +#include "multicast.h" + +#define RIG_MULTICAST_ADDR "224.0.0.1" +#define RIG_MULTICAST_PORT 4532 + +#if 0 +static int multicast_running; +static int sock; +static int seqnumber; +static int runflag = 0; +static struct ip_mreq mreq = {0}; +static pthread_t threadid; +static struct sockaddr_in dest_addr = {0}; +#endif + + +static int multicast_status_changed(RIG *rig) +{ + int retval; +#if 0 + freq_t freq, freqsave = rig->state.cache.freqMainA; + + if ((retval = rig_get_freq(rig, RIG_VFO_A, &freq)) != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval)); + } + + if (freq != freqsave) { return 1; } + +#endif + + rmode_t mode, modesave = rig->state.cache.modeMainA; + pbwidth_t width, widthsave = rig->state.cache.widthMainA; + + if (rig->state.multicast->seqnumber % 2 == 0 + && (retval = rig_get_mode(rig, RIG_VFO_A, &mode, &width)) != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval)); + } + + if (mode != modesave) { return 1; } + + if (width != widthsave) { return 1; } + + ptt_t ptt, pttsave = rig->state.cache.ptt; + + if (rig->state.multicast->seqnumber % 2 == 0 + && (retval = rig_get_ptt(rig, RIG_VFO_A, &ptt)) != RIG_OK) + if (ptt != pttsave) { return 1; } + + split_t split, splitsave = rig->state.cache.split; + vfo_t txvfo; + + if (rig->state.multicast->seqnumber % 2 == 0 + && (retval = rig_get_split_vfo(rig, RIG_VFO_A, &split, &txvfo)) != RIG_OK) + if (split != splitsave) { return 1; } + + return 0; +} + +void json_add_string(char *msg, const char *key, const char *value) +{ + if (strlen(msg) != 0) + { + strcat(msg, ",\n"); + } + + strcat(msg, "\""); + strcat(msg, key); + strcat(msg, "\": "); + strcat(msg, "\""); + strcat(msg, value); + strcat(msg, "\""); +} + +void json_add_int(char *msg, const char *key, const int number) +{ + if (strlen(msg) != 0) + { + strcat(msg, ",\n"); + } + + strcat(msg, "\""); + strcat(msg, key); + strcat(msg, "\": "); + char tmp[64]; + sprintf(tmp, "%d", number); + strcat(msg, tmp); +} + +void json_add_double(char *msg, const char *key, const double value) +{ + if (strlen(msg) != 0) + { + strcat(msg, ",\n"); + } + + strcat(msg, "\""); + strcat(msg, key); + strcat(msg, "\": "); + char tmp[64]; + sprintf(tmp, "%f", value); + strcat(msg, tmp); +} + +void json_add_boolean(char *msg, const char *key, const int value) +{ + if (strlen(msg) != 0) + { + strcat(msg, ",\n"); + } + + strcat(msg, "\""); + strcat(msg, key); + strcat(msg, "\": "); + char tmp[64]; + sprintf(tmp, "%s", value == 0 ? "False" : "True"); + strcat(msg, tmp); +} + +void json_add_time(char *msg) +{ + char mydate[256]; + date_strget(mydate, sizeof(mydate), 0); + + if (strlen(msg) != 0) + { + strcat(msg, ",\n"); + } + + strcat(msg, "\"Time\": "); + strcat(msg, mydate); +} + +void json_add_vfoA(RIG *rig, char *msg) +{ + strcat(msg, "{\n"); + json_add_string(msg, "Name", "VFOA"); + json_add_int(msg, "Freq", rig->state.cache.freqMainA); + + if (strlen(rig_strrmode(rig->state.cache.modeMainA)) > 0) + { + json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainA)); + } + + if (rig->state.cache.widthMainA > 0) + { + json_add_int(msg, "Width", rig->state.cache.widthMainA); + } + + json_add_boolean(msg, "RX", !rig->state.cache.ptt); + json_add_boolean(msg, "TX", rig->state.cache.ptt); + strcat(msg, "\n}\n"); +} + +void json_add_vfoB(RIG *rig, char *msg) +{ + strcat(msg, "{\n"); + json_add_string(msg, "Name", "VFOB"); + json_add_int(msg, "Freq", rig->state.cache.freqMainB); + + if (strlen(rig_strrmode(rig->state.cache.modeMainB)) > 0) + { + json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainB)); + } + + if (rig->state.cache.widthMainB > 0) + { + json_add_int(msg, "Width", rig->state.cache.widthMainB); + } + + json_add_boolean(msg, "RX", !rig->state.cache.ptt); + json_add_boolean(msg, "TX", rig->state.cache.ptt); + strcat(msg, "\n},\n"); +} + +static int multicast_send_json(RIG *rig) +{ + char msg[8192]; // could be pretty big + char buf[4096]; +// sprintf(msg,"%s:f=%.1f", date_strget(msg, (int)sizeof(msg), 0), f); + msg[0] = 0; + snprintf(buf, sizeof(buf), "%s:%s", rig->caps->model_name, + rig->state.rigport.pathname); + json_add_string(msg, "ID", buf); + json_add_time(msg); + json_add_int(msg, "Sequence", rig->state.multicast->seqnumber++); + json_add_string(msg, "VFOCurr", rig_strvfo(rig->state.current_vfo)); + json_add_int(msg, "Split", rig->state.cache.split); + strcat(msg, ",\n\"VFOs\": [\n{\n"); + json_add_vfoA(rig, msg); + + // send the thing + multicast_send(rig, (unsigned char *)msg, strlen(msg)); + return 0; +} + +void *multicast_thread(void *vrig) +{ + int retval; + RIG *rig = (RIG *)vrig; + rig_debug(RIG_DEBUG_TRACE, "%s: multicast_thread started\n", __func__); + + // do the 1st packet all the time + multicast_status_changed(rig); + multicast_send_json(rig); + int loopcount = 4; + + while (rig->state.multicast->runflag) + { + hl_usleep(100 * 1000); + freq_t freq, freqsave = 0; + + if ((retval = rig_get_freq(rig, RIG_VFO_A, &freq)) != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval)); + } + + if (freq != freqsave || loopcount-- == 0) + { + multicast_status_changed(rig); + multicast_send_json(rig); + loopcount = 4; + } + + } + + return NULL; +} + +int multicast_init(RIG *rig, char *addr, int port) +{ + if (rig->state.multicast == NULL) + { + rig->state.multicast = calloc(1, sizeof(struct multicast_s)); + } + else if (rig->state.multicast->multicast_running) { return RIG_OK; } // we only need one port + + if (addr == NULL) { addr = RIG_MULTICAST_ADDR; } + + if (port == 0) { port = RIG_MULTICAST_PORT; } + + // Create a UDP socket + rig->state.multicast->sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (rig->state.multicast->sock < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: socket: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + + // Set the SO_REUSEADDR option to allow multiple processes to use the same address + int optval = 1; + + if (setsockopt(rig->state.multicast->sock, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(optval)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + + // Bind the socket to any available local address and the specified port + struct sockaddr_in saddr = {0}; + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + saddr.sin_port = htons(port); + + if (bind(rig->state.multicast->sock, (struct sockaddr *)&saddr, + sizeof(saddr)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: bind: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + + // Construct the multicast group address + // struct ip_mreq mreq = {0}; + rig->state.multicast->mreq.imr_multiaddr.s_addr = inet_addr(addr); + rig->state.multicast->mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + // Set the multicast TTL (time-to-live) to limit the scope of the packets + unsigned char ttl = 1; + + if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + + // Join the multicast group + if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &rig->state.multicast->mreq, sizeof(rig->state.multicast->mreq)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + + // prime the dest_addr for the send routine + rig->state.multicast->dest_addr.sin_family = AF_INET; + rig->state.multicast->dest_addr.sin_addr.s_addr = inet_addr(addr); + rig->state.multicast->dest_addr.sin_port = htons(port); + + printf("starting thread\n"); + + rig->state.multicast->runflag = 1; + pthread_create(&rig->state.multicast->threadid, NULL, multicast_thread, + (void *)rig); + rig->state.multicast->multicast_running = 1; + return RIG_OK; +} + +void multicast_close(RIG *rig) +{ + int retval; + + // Leave the multicast group + if ((retval = setsockopt(rig->state.multicast->sock, IPPROTO_IP, + IP_DROP_MEMBERSHIP, &rig->state.multicast->mreq, + sizeof(rig->state.multicast->mreq))) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno)); + return; + } + + // Close the socket + if ((retval = close(rig->state.multicast->sock))) + { + rig_debug(RIG_DEBUG_ERR, "%s: close: %s\n", __func__, strerror(errno)); + } +} + +// if msglen=0 msg is assumed to be a string +int multicast_send(RIG *rig, unsigned char *msg, int msglen) +{ + // Construct the message to send + if (msglen == 0) { msglen = strlen((char *)msg); } + + // Send the message to the multicast group + ssize_t num_bytes = sendto(rig->state.multicast->sock, msg, msglen, 0, + (struct sockaddr *)&rig->state.multicast->dest_addr, + sizeof(rig->state.multicast->dest_addr)); + + if (num_bytes < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: sendto: %s\n", __func__, strerror(errno)); + return -RIG_EIO; + } + else + { +// printf("Sent %zd bytes to multicast group %s:%d\n", num_bytes, MULTICAST_ADDR, +// PORT); + } + + return num_bytes; +} + +#define TEST +#ifdef TEST +int main(int argc, char *argv[]) +{ + RIG *rig; + rig_model_t myrig_model; + rig_set_debug_level(RIG_DEBUG_NONE); + + if (argc > 1) { myrig_model = atoi(argv[1]); } + else + { + myrig_model = 1035; + } + + rig = rig_init(myrig_model); + + if (rig == NULL) + { + + } + + strncpy(rig->state.rigport.pathname, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1); + rig->state.rigport.parm.serial.rate = 38400; + rig_open(rig); + multicast_init(rig, NULL, 0); + pthread_join(rig->state.multicast->threadid, NULL); + pthread_exit(NULL); + return 0; +} +#endif diff --git a/src/multicast.h b/src/multicast.h new file mode 100644 index 000000000..dc97015de --- /dev/null +++ b/src/multicast.h @@ -0,0 +1,47 @@ +//include +//#include +//#include +//#include +//#include +#include +//#include +//#include +//#include + +#ifndef MULTICAST_H +#define MULTICAST_H +#if 0 // moved to rig.h +struct multicast_s +{ + int multicast_running; + int sock; + int seqnumber; + int runflag; // = 0; + struct ip_mreq mreq; // = {0}; + pthread_t threadid; + struct sockaddr_in dest_addr; // = {0}; +}; +#endif + +struct multicast_vfo +{ + char *name; + double freq; + char *mode; + int width; + int widthLower; + int widthUpper; + unsigned char rx; // true if in rx mode + unsigned char tx; // true in in tx mode +}; + +struct multicast_broadcast +{ + char *ID; + struct multicast_vfo **vfo; +}; + +// returns # of bytes sent +int multicast_init(RIG *rig, char *addr, int port); +int multicast_send(RIG *rig, unsigned char *msg, int msglen); +#endif //MULTICAST_H diff --git a/tests/multicastclient.c b/tests/multicastclient.c new file mode 100644 index 000000000..d5a6f5a30 --- /dev/null +++ b/tests/multicastclient.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MULTICAST_ADDR "224.0.0.1" +#define PORT 4532 +#define BUFFER_SIZE 1024 + +int main() +{ + // Create a UDP socket + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) + { + perror("socket creation failed"); + exit(EXIT_FAILURE); + } + + // Set the SO_REUSEADDR option to allow multiple processes to use the same address + int optval = 1; + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) + { + perror("setsockopt failed"); + exit(EXIT_FAILURE); + } + + // Bind the socket to any available local address and the specified port + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(PORT); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + // Construct the multicast group address + struct ip_mreq mreq = {0}; + mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDR); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + // Join the multicast group + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + perror("setsockopt failed"); + exit(EXIT_FAILURE); + } + + // Receive multicast packets in a loop + while (1) + { + char buffer[BUFFER_SIZE]; + struct sockaddr_in src_addr = {0}; + socklen_t addrlen = sizeof(src_addr); + ssize_t num_bytes = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0, + (struct sockaddr *)&src_addr, &addrlen); + + if (num_bytes < 0) + { + perror("recvfrom failed"); + exit(EXIT_FAILURE); + } + + buffer[num_bytes] = '\0'; + printf("Received %zd bytes from %s:%d\n%s\n", num_bytes, + inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), buffer); + } + + // Leave the multicast group + if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + perror("setsockopt failed"); + exit(EXIT_FAILURE); + } + + // Close the socket + close(sock); + + return 0; +}