ampler/udp.c

180 lines
4.8 KiB
C

// udp.c
/* TODO:
* convert error codes to strings and print them, we could just make a table
* option to change port, handle when we cant get gur port
* be able to send packets when sounds / recording starts / ends ?
* store from ip + port in message ?
*/
#ifdef __WIN32__
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include "ampler.h"
// sadly, these mess with gur FD_SET macro ; - ;
#undef do
#undef if
#undef for
#undef while
// G l o b a L E v i L //
#ifdef __WIN32__
static SOCKET udp_sock;
#else
static int udp_sock;
#endif
#ifdef __WIN32__
void udp_init(Ampler_state *state) {
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsastartup
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa)) // we want version 2.2
printf("error: WSAStartup() != 0, WSAGetLastError() = %i\n", WSAGetLastError());
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket
// SOCKET s = socket(
udp_sock = socket(
AF_INET, // ipv4
SOCK_DGRAM, // udp
0 // protocol, 0 = unspecified
);
if (udp_sock == INVALID_SOCKET)
printf("error: socket is invalid, WSAGetLastError() = %i\n" , WSAGetLastError());
// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
struct sockaddr_in server; // sockaddr_in = ipv4
const int PORT = 8888;
server.sin_family = AF_INET; // ipv4
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT); // convert host to network byte order for short
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind
int bind_result = bind(
udp_sock, // unbound socket, to be bound
&server, // local address of socket
sizeof(server) // sizeof address struct
);
if (bind_result == SOCKET_ERROR)
printf("error: bind returned SOCKET_ERROR, WSAGetLastError() = %i\n" , WSAGetLastError());
}
#else
void udp_init(Ampler_state *state) {
//
}
#endif
#ifdef __WIN32__
void udp_quit(Ampler_state *state) {
closesocket(udp_sock);
WSACleanup();
}
#else
void udp_quit(Ampler_state *state) {
// close(udp_sock);
}
#endif
#ifdef __WIN32__
void recv_packet(Ampler_state *state) {
char discard_buf[32] = { 0 }; // for dropped packets
char *buf = discard_buf;
int buf_len = sizeof discard_buf;
for (int i = 0; i < arraylen(state -> messages); i += 1) {
Udp_msg *msg = &(state -> messages[i]);
if (msg -> state == MSG_FREE) {
msg -> state = MSG_TRIGGER;
buf = msg -> text;
buf_len = sizeof msg -> text;
SDL_memset(buf, 0, buf_len);
break;
}
}
if (buf == discard_buf)
puts("dropped udp packet.");
struct sockaddr_in from_addr;
int from_addr_size = sizeof from_addr;
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom
int recv_len = recvfrom(
udp_sock, // socket to recive on
buf, // buffer for recv'd data
buf_len - 1, // size of buffer in bytes, '- 1' preserves null terminator
0, // flags
&from_addr, // address struct to recieve source of packet
&from_addr_size // POINTER to size of address struct
);
if (recv_len == SOCKET_ERROR)
if (WSAGetLastError() != WSAEMSGSIZE) // packet was truncated to fit buffer
printf("error: recvfrom returned SOCKET_ERROR, WSAGetLastError() = %i\n" , WSAGetLastError());
// convert to ipv4 address string, stored in static buffer, next call overwrites
const char *from_ip_str = inet_ntoa(from_addr.sin_addr);
int from_port = ntohs(from_addr.sin_port); // convert network to host byte order for short
if (0) // FOR DEBUGGING
printf("%s:%d\t%s\n", from_ip_str, from_port, buf);
}
#else
void recv_packet(Ampler_state *state) {
//
}
#endif
#ifdef __WIN32__
void udp_frame(Ampler_state *state) {
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select
fd_set recv_fd_set;
FD_ZERO(&recv_fd_set); // init / clear set
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-timeval
TIMEVAL timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 1000; // 1 ms
while (1) {
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select
FD_SET(udp_sock, &recv_fd_set); // add server socket to set
int can_recv = select( // returns gur number of sockets, we can recv on, 0 on timeout
0, // ignored on win, used for compatability
&recv_fd_set, // soc set to be checked for reading
NULL, // set to be checked for writing
NULL, // set to be checked for errors
&timeout // max time to wait for
);
if (can_recv == SOCKET_ERROR)
printf("error: select returned SOCKET_ERROR, WSAGetLastError() = %i\n", WSAGetLastError());
if (FD_ISSET(udp_sock, &recv_fd_set)) {
// puts("soc in set, we can recv data!");
}
else {
// puts("timeout");
return;
}
recv_packet(state);
}
}
#else
void udp_frame(Ampler_state *state) {
//
}
#endif