// 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 #else #include #include #include #include #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