commit f240fe4f6a2d784026a2c7d4dbbfa1ffee84f377 parent af35db1ddbcc4937c6a9b0d2bb9d0f76bb67e2b6 Author: Michael Savage <mikejsavage@gmail.com> Date: Wed Aug 2 18:37:59 -0700 Start sockets overhaul. Only UDP on Linux for now Diffstat:
hm.cc | | | 24 | +++++++++++------------- |
platform_network.h | | | 158 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
server/main.cc | | | 107 | +++++++------------------------------------------------------------------------ |
unix_network.h | | | 9 | +++++---- |
diff --git a/hm.cc b/hm.cc @@ -24,11 +24,11 @@ static const float EYE_HEIGHT = 1.8f; -static Socket sock; +static UDPSocket sock; +static NetAddress server_addr; static u64 sid; static double last_connection_attempt = -5.0; // TODO static bool connected = false; -static struct sockaddr_in addr; static UB ub_view; static UB ub_model; static UB ub_sun; @@ -118,16 +118,15 @@ GAME_INIT( game_init ) { load_trees( &mem->persistent_arena ); - sock_init( &sock, NET_IPV4, NET_UDP, NET_NONBLOCKING ); + sock = sock_new_udp( NET_IPV4, NET_NONBLOCKING ); - struct sockaddr_storage addrs = { }; - if( !dns_first( "localhost", &addrs ) ) { + struct sockaddr_storage sa = { }; + if( !dns_first( "localhost", &sa ) ) { FATAL( "dns_first" ); } - memcpy( &addr, &addrs, sizeof( addr ) ); - if( addr.sin_family == AF_INET6 ) printf( "ipv6\n" ); - addr.sin_port = htons( 13337 ); + server_addr = sockaddr_to_netaddress( sa ); + server_addr.port = 13337; } static m4 camera_to_view( v3 position, v3 angles ) { @@ -426,10 +425,9 @@ GAME_FRAME( game_frame ) { { char buf[ 1400 ]; - sockaddr_storage a; - socklen_t addr_len = sizeof( a ); + NetAddress recv_addr; size_t len; - if( !sock_tryrecv_udp( sock, buf, sizeof( buf ), &a, &addr_len, &len ) ) { + if( !sock_tryrecv_udp( sock, buf, sizeof( buf ), &recv_addr, &len ) ) { len = 0; } @@ -441,7 +439,7 @@ GAME_FRAME( game_frame ) { write( &ws, sid ); write( &ws, game->pos ); - sock_send_udp( sock, ws, addr ); + sock_send_udp( sock, ws.start, ws.len(), server_addr ); u16 player_count = read_u16( &rs ); ReadStreamCheckpoint rsc = rs.checkpoint(); @@ -504,7 +502,7 @@ GAME_FRAME( game_frame ) { write_u64( &ws, u64( -1 ) ); write( &ws, game->pos ); - sock_send_udp( sock, ws, addr ); + sock_send_udp( sock, ws.start, ws.len(), server_addr ); last_connection_attempt = current_time; } diff --git a/platform_network.h b/platform_network.h @@ -1,13 +1,77 @@ #pragma once +#include <arpa/inet.h> + #include "platform.h" #include "log.h" -#include "stream.h" enum TransportProtocol { NET_UDP, NET_TCP }; enum IPvX { NET_IPV4, NET_IPV6 }; enum NonblockingBool { NET_BLOCKING, NET_NONBLOCKING }; +struct IPv4 { u8 bytes[ 4 ]; }; +struct IPv6 { u8 bytes[ 16 ]; }; + +STATIC_ASSERT( sizeof( IPv4 ) == sizeof( struct in_addr ) ); +STATIC_ASSERT( sizeof( IPv6 ) == sizeof( struct in6_addr ) ); + +struct NetAddress { + IPvX type; + union { + IPv4 ipv4; + IPv6 ipv6; + }; + u16 port; +}; + +struct UDPSocket { + int fd; + IPvX ipvx; +}; + +struct TCPSocket { + int fd; + IPvX ipvx; +}; + +inline NetAddress sockaddr_to_netaddress( const sockaddr_storage & ss ) { + NetAddress addr; + if( ss.ss_family == AF_INET ) { + const struct sockaddr_in & sa4 = ( const struct sockaddr_in & ) ss; + + addr.type = NET_IPV4; + addr.port = ntohs( sa4.sin_port ); + memcpy( &addr.ipv4, &sa4.sin_addr.s_addr, sizeof( addr.ipv4 ) ); + } + else { + const struct sockaddr_in6 & sa6 = ( const struct sockaddr_in6 & ) ss; + + addr.type = NET_IPV6; + addr.port = ntohs( sa6.sin6_port ); + memcpy( &addr.ipv6, &sa6.sin6_addr.s6_addr, sizeof( addr.ipv6 ) ); + } + return addr; +} + +inline struct sockaddr_storage netaddress_to_sockaddr( const NetAddress & addr ) { + struct sockaddr_storage ss; + if( addr.type == NET_IPV4 ) { + struct sockaddr_in & sa4 = ( struct sockaddr_in & ) ss; + + ss.ss_family = AF_INET; + sa4.sin_port = htons( addr.port ); + memcpy( &sa4.sin_addr.s_addr, &addr.ipv4, sizeof( addr.ipv4 ) ); + } + else { + struct sockaddr_in6 & sa6 = ( struct sockaddr_in6 & ) ss; + + ss.ss_family = AF_INET6; + sa6.sin6_port = htons( addr.port ); + memcpy( &sa6.sin6_addr.s6_addr, &addr.ipv6, sizeof( addr.ipv6 ) ); + } + return ss; +} + #if PLATFORM_WINDOWS #include "win32_network.h" #elif PLATFORM_UNIX @@ -16,6 +80,79 @@ enum NonblockingBool { NET_BLOCKING, NET_NONBLOCKING }; #error new platform #endif +inline void sock_bind( int fd, IPvX ipvx, u16 port ) { + { + int one = 1; + int ok = setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof( one ) ); + if( ok == -1 ) { + FATAL( "setsockopt" ); + } + } + + if( ipvx == NET_IPV4 ) { + sockaddr_in my_addr; + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons( port ); + my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); + int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); + if( ok == SOCKET_ERROR ) { + FATAL( "bind" ); + } + } + else { + sockaddr_in6 my_addr; + my_addr.sin6_family = AF_INET6; + my_addr.sin6_port = htons( port ); + my_addr.sin6_addr = in6addr_any; + int ok = bind( fd, ( sockaddr * ) &my_addr, sizeof( my_addr ) ); + if( ok == SOCKET_ERROR ) { + FATAL( "bind" ); + } + } +} + +inline void sock_bind( UDPSocket sock, u16 port ) { + sock_bind( sock.fd, sock.ipvx, port ); +} + +inline void sock_bind( TCPSocket sock, u16 port ) { + sock_bind( sock.fd, sock.ipvx, port ); +} + +inline UDPSocket sock_new_udp( IPvX ipvx, NonblockingBool nonblocking ) { + UDPSocket sock; + + int domain = ipvx == NET_IPV4 ? AF_INET : AF_INET6; + sock.fd = socket( domain, SOCK_DGRAM, IPPROTO_UDP ); + sock.ipvx = ipvx; + if( sock.fd == INVALID_SOCKET ) { + FATAL( "socket" ); + } + + if( nonblocking == NET_NONBLOCKING ) { + sock_fd_nonblocking( sock.fd ); + } + + return sock; +} + +inline TCPSocket sock_new_tcp( IPvX ipvx, NonblockingBool nonblocking ) { + TCPSocket sock; + + int domain = ipvx == NET_IPV4 ? AF_INET : AF_INET6; + sock.fd = socket( domain, SOCK_STREAM, IPPROTO_TCP ); + sock.ipvx = ipvx; + if( sock.fd == INVALID_SOCKET ) { + FATAL( "socket" ); + } + + if( nonblocking == NET_NONBLOCKING ) { + sock_fd_nonblocking( sock.fd ); + } + + return sock; +} + inline void sock_init( Socket * sock, IPvX ipvx, TransportProtocol transport, NonblockingBool nonblocking ) { int domain = AF_INET; if( ipvx == NET_IPV6 ) { @@ -53,21 +190,24 @@ inline void sock_init( Socket * sock, IPvX ipvx, TransportProtocol transport, No } } -inline void sock_send_udp( Socket sock, const char * data, size_t len, const sockaddr_in & addr ) { - ASSERT( sock.transport == NET_UDP ); - - ssize_t ok = sendto( sock.fd, data, checked_cast< int >( len ), 0, ( sockaddr * ) &addr, sizeof( addr ) ); +inline void sock_send_udp( UDPSocket sock, const char * data, size_t len, const NetAddress & addr ) { + struct sockaddr_storage sa = netaddress_to_sockaddr( addr ); + socklen_t sa_size = sock.ipvx == NET_IPV4 ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); + ssize_t ok = sendto( sock.fd, data, checked_cast< int >( len ), 0, ( struct sockaddr * ) &sa, sa_size ); if( ok == SOCKET_ERROR ) { FATAL( "sendto" ); } } -inline void sock_send_udp( Socket sock, const WriteStream & ws, const sockaddr_in & addr ) { - ASSERT( ws.ok ); - sock_send_udp( sock, ws.start, ws.len(), addr ); +inline void sock_destroy( UDPSocket * sock ) { + int ok = closesocket( sock->fd ); + if( ok == -1 ) { + FATAL( "closesocket" ); + } + sock->fd = INVALID_SOCKET; } -inline void sock_destroy( Socket * sock ) { +inline void sock_destroy( TCPSocket * sock ) { int ok = closesocket( sock->fd ); if( ok == -1 ) { FATAL( "closesocket" ); diff --git a/server/main.cc b/server/main.cc @@ -22,7 +22,7 @@ struct PlayerState { u64 sid; v3 pos; - sockaddr_storage addr; + NetAddress addr; double last_message_time; u16 next_free; }; @@ -30,100 +30,11 @@ struct PlayerState { static Pool< PlayerState, MAX_PLAYERS > states; static HashTable< PlayerState *, MAX_PLAYERS * 2 > sids_to_states; -enum AddressType { - ADDRESSTYPE_IPV4, - ADDRESSTYPE_IPV6, -}; - -struct IPv4 { u8 bytes[ 4 ]; }; -struct IPv6 { u8 bytes[ 16 ]; }; - -struct Address { - AddressType type; - union { - IPv4 ipv4; - IPv6 ipv6; - }; - u16 port; -}; - -bool receive( int fd, char * buf, size_t * bytes_read, size_t buf_len, sockaddr_storage * addr ) { - socklen_t addr_len = sizeof( *addr ); - ssize_t len = recvfrom( fd, buf, buf_len, 0, ( sockaddr * ) addr, &addr_len ); - if( len == -1 ) { - if( errno == EAGAIN ) return false; - err( 1, "recv" ); - } - - *bytes_read = ( size_t ) len; - - // http://stackoverflow.com/questions/18995361/can-recvfrom-function-from-socket-extract-the-sender-ip-address - // char hbuf[ INET6_ADDRSTRLEN ], sbuf[NI_MAXSERV]; - // if (getnameinfo( ( const sockaddr * ) &client_addr, client_addr_len, hbuf, sizeof(hbuf), sbuf, - // sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) - // printf("host=%s, serv=%s\n", hbuf, sbuf); - - - // char ip[ INET6_ADDRSTRLEN ]; - // inet_ntop( addr->ss_family, - // addr->ss_family == AF_INET ? - // ( void * ) &( ( sockaddr_in * ) &addr )->sin_addr : - // ( void * ) &( ( sockaddr_in6 * ) &addr )->sin6_addr, - // ip, sizeof( ip ) ); - // printf( "%s: %.*s\n", ip, len, buf ); - // if( addr->ss_family == AF_INET ) { - // sockaddr_in * sadin = ( sockaddr_in * ) &addr; - // printf( "%d\n", ntohs( sadin->sin_port ) ); - // } - // else { - // sockaddr_in6 * sadin6 = ( sockaddr_in6 * ) &addr; - // printf( "%d\n", ntohs( sadin6->sin6_port ) ); - // } - - return true; -} - -static void send( int fd, const PlayerState * state, const WriteStream & ws ) { - sendto( fd, ws.start, ws.len(), 0, ( sockaddr * ) &state->addr, sizeof( sockaddr_in ) ); -} - int main() { - int server_fd_ipv4 = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); - int server_fd_ipv6 = socket( AF_INET6, SOCK_DGRAM, IPPROTO_UDP ); - - if( server_fd_ipv4 == -1 ) { - err( 1, "socket ipv4" ); - } - if( server_fd_ipv6 == -1 ) { - err( 1, "socket ipv6" ); - } - - int one = 1; - setsockopt( server_fd_ipv4, SOL_SOCKET, SO_REUSEADDR, &one, sizeof( one ) ); - setsockopt( server_fd_ipv6, SOL_SOCKET, SO_REUSEADDR, &one, sizeof( one ) ); - - sockaddr_in server_addr_ipv4; - server_addr_ipv4.sin_family = AF_INET; - server_addr_ipv4.sin_addr.s_addr = INADDR_ANY; - server_addr_ipv4.sin_port = htons( PORT ); - - sockaddr_in6 server_addr_ipv6; - server_addr_ipv6.sin6_family = AF_INET6; - server_addr_ipv6.sin6_addr = in6addr_any; - server_addr_ipv6.sin6_port = htons( PORT ); - - int ok_ipv4 = bind( server_fd_ipv4, ( sockaddr * ) &server_addr_ipv4, sizeof( server_addr_ipv4 ) ); - int ok_ipv6 = bind( server_fd_ipv6, ( sockaddr * ) &server_addr_ipv6, sizeof( server_addr_ipv6 ) ); - - if( ok_ipv4 == -1 ) { - err( 1, "bind ipv4" ); - } - if( ok_ipv6 == -1 ) { - err( 1, "bind ipv6" ); - } - - sock_fd_nonblocking( server_fd_ipv4 ); - sock_fd_nonblocking( server_fd_ipv6 ); + UDPSocket ipv4 = sock_new_udp( NET_IPV4, NET_NONBLOCKING ); + UDPSocket ipv6 = sock_new_udp( NET_IPV6, NET_NONBLOCKING ); + sock_bind( ipv4, PORT ); + sock_bind( ipv6, PORT ); RNGWell512 rng; rng_well512_init( &rng ); @@ -132,11 +43,11 @@ int main() { printf( "frame\n" ); const double now = get_time(); - sockaddr_storage addr; + NetAddress addr; char read_buf[ 1400 ]; size_t len; - while( receive( server_fd_ipv4, read_buf, &len, sizeof( read_buf ), &addr ) ) { + while( sock_tryrecv_udp( ipv4, read_buf, sizeof( read_buf ), &addr, &len ) ) { ReadStream r( read_buf, len ); // u16 command = read_u16( &r ); @@ -165,7 +76,7 @@ int main() { write( &w, new_sid ); if( w.ok ) { - send( server_fd_ipv4, state, w ); + sock_send_udp( ipv4, w.start, w.len(), state->addr ); } } else { @@ -202,7 +113,7 @@ int main() { if( w.ok ) { for( const PlayerState & state : states.elems ) { - send( server_fd_ipv4, &state, w ); + sock_send_udp( ipv4, w.start, w.len(), state.addr ); } } diff --git a/unix_network.h b/unix_network.h @@ -30,15 +30,16 @@ inline void sock_fd_nonblocking( int fd ) { if( ok == -1 ) FATAL( "fcntl F_SETFL" ); } -inline bool sock_tryrecv_udp( Socket sock, char * buf, size_t len, sockaddr_storage * addr, socklen_t * addr_len, size_t * bytes_received ) { - ASSERT( sock.transport == NET_UDP ); - - ssize_t received = recvfrom( sock.fd, buf, len, 0, ( sockaddr * ) addr, addr_len ); +inline bool sock_tryrecv_udp( UDPSocket sock, char * buf, size_t len, NetAddress * addr, size_t * bytes_received ) { + struct sockaddr_storage sa; + socklen_t sa_size = sock.ipvx == NET_IPV4 ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); + ssize_t received = recvfrom( sock.fd, buf, len, 0, ( struct sockaddr * ) &sa, &sa_size ); if( received == -1 ) { if( errno == EAGAIN ) return false; FATAL( "recvfrom" ); } + *addr = sockaddr_to_netaddress( sa ); *bytes_received = size_t( received ); return true; }