meta data for this page


Task for week 42

The first task is about UDP (RFC).

  1. UDP is stateless, so
    1. How a server can keep track of the clients?
      • Use the udpexample as base and add functionality to support multiple clients on the server

  • Answer: Using both address and port information, which can be retrieved by, e.g.,
    char hostbuffer[NI_MAXHOST] = { 0 }; // Host
    char portbuffer[NI_MAXSERV] = { 0 }; //Port
    struct sockaddr_storage client_addr; // Big structure
    memset(&client_addr,0,sizeof(client_addr); // Zero it
    struct sockaddr* client_address = (struct sockaddr*) &client_addr; // For passing to recvfrom()
    socklen_t addrlen = sizeof(client_addr);
    dgramlen = recvfrom(socketfd, &recvbuffer, SIZE, 0, client_address, &addrlen); // Get the packet, address info in client_address
    getnameinfo(client_address, addrlen, hostbuffer, NI_MAXSERV, portbuffer, NI_MAXSERV, 0); //Get the information from client_address to buffers
    //Alternatively, you can use the fields of appropriate structure
    if(client_addr.ss_family == AF_INET) { // IPv4
      if(((struct sockaddr_in*)&client_addr)->sin_addr.s_addr = ((struct sockaddr_in*)&other_addr)->sin_addr.s_addr) do_something(); // IPv4 Address
      if(((struct sockaddr_in*)&client_addr)->sin_port = ((struct sockaddr_in*)&other_addr)->sin_port) do_something(); // Port
    if(client_addr.ss_family == AF_INET6) {
     if(((struct sockaddr_in6*)&client_addr)->sin6_addr.s6_addr = ((struct sockaddr_in6*)&other_addr)->sin6_addr.s6_addr) do_something(); // IPv6 Address
     if(((struct sockaddr_in6*)&client_addr)->sin6_port = ((struct sockaddr_in6*)&other_addr)->sin6_port) do_something(); // Port
  • Also a shared secret can be used to identify a client: an identification number, selected by server and reported to the client that the client should use in further messages

  1. How to verify that the receiver got the packet?
    • Are there different approaches?

  • Answer: Utilizing sequence numbers (or time stamps) in packets with acknowledgements, alternatives:
    • one at a time (ack everything, stop and wait)
      if(correct_ack_received) send_next()
      else resend()
    • group of packets, e.g, server:
      while(receive_timer_not_expired()) {


      while(no-ack_limit_reached()) {
  • a range of packets, e.g, server:
    while(receive_timer_not_expired()) {


    while(no-ack_limit_reached()) {
  • It can be also used that you use Negative ACK (NACK) for telling the peer what packets have not been sent (i.e., which have to be resent). E.g. after receiving certain count or range of packets the peer could respond that what packets are either missing or corrupt and before continuing the sender then resends these missing ones. If no packet is missing an empty NACK can be sent (or just omit that - no news is good news).

  1. How the order of received packets could be checked?
    • What could be the ways of reacting to out of order packets (or packet loss)?

  • Answer: Sequence numbers for packets, protocol states that they start from one and, certain length integer is used within packets to indicate the sequence number – small numbers last for only a small amount of time. It, however, depends of the packet frequency.
    • Each end has own sequences (client send +1, server ack+1)
    • One sequence for both, odd numbers for client, even numbers for server
    • It is possible to wait for out of order packets to arrive (fill the “gap”)
    • React to out of order packets:
      • Larger: request the previous with an ACK that has smaller sequence → requires buffering of the messages, and the sender resends all packets from that sequence number onwards
      • Smaller than expected: can be caused by ACKing multiple packets at once or by losing the previous ACK. Receiver sends ACK for the received sequence and continues by verifying that the re-sent packet has same content as the one that was already received.
      • Acking is slow, but can guarantee no data loss. For fast paced games this is not useful, has to be compensated otherwise (periodic game state updates acting as corrections)

  1. Filling the buffer
    1. What approaches there are for filling a buffer with data that contains binary numbers, null byte separators and char strings?

  • Answer: : Set numbers in network order (see answer for task 3 or use memcpy() - just remember to convert numbers to network order before send) and use either strcpy()/strncpy()/memcpy()/memmove() for char strings - see manual pages!

  1. What are the ways of sending the previously mentioned data with UDP? What does the receiver have to account for?

  • Answer: : Use a single sendto() [recommended] or send each as a separate packet with sendto() [not really practical]. Remember that with UDP the packet boundaries are strict and guaranteed - one send is one packet.

  1. What is the packet size limit for UDP? What other limits there can be?

  • Answer:
    • The specification (RFC) states a 16bit length variable → 2^16 = 64kB = 65536 bytes
    • Path MTU can, however, limit this further (not limit, but fragment – bad for UDP, what if one fragment is lost?): usually 1500 bytes for LAN (jumbo frames 9216)
    • In Internet it is guaranteed that 68 byte long messages are sent as one with IPv4 (guaranteed receiving of 576 bytes) and 1280 with IPv6 without fragmentation
      • Fragmentation – if successful and no packet loss occurs, it is invisible for the developer and user but if a fragment is lost → the packet is discarded, ergo, packet loss happens – IP layer doesn't allow packet with invalid checksum to be delivered to higher layer. (problem with bad connections)
      • UDP guarantees packet boundaries – you will not receive a fragment of a packet. Only full ones are given to UDP layer. However, note that if your reading buffer is less than the packet size you will lose the exceeding data. After reading the TCP/IP stack discards the rest.
      • But, also the buffers used on the computers do have an effect

  1. Byte ordering (32bit vs 16bit vs 8bit)
    1. Pack numbers 715517, 53850 and 42 into one packet, send (sendto()) and receive (recvfrom()) it using UDP (use the udpexample), do it by using
      1. Appropriate integers (waste no space!) in correct format
      2. As char string with null byte (\0) as separator between them. Remember to terminate (why?).

  • Answer:
     char dgram[100] =  { 0 };
    int position = 0;
    *(uint32_t*)&dgram[position] = htonl(715517);
    position += sizeof(uint32_t);
    *(uint16_t*)&dgram[position] = htons(53850);
    position += sizeof(uint16_t);
    *(uint8_t*)dgram[position] = 42;
    position += sizeof(uint8_t);
    //All into one buffer:
    uint32_t first = 715517;
    uint16_t second = 53850;
    uint8_t third = 42;

  1. Which one is the most optimal?
  2. Try to get the length of the created charstring with strlen() - why it fails?

  • Answer: See the man-pages (man strlen)!

Tools for the task

htons(), htonl(), ntohs(), ntohl(), socket(), sendto(), recvfrom(), bind(), getaddrinfo(), getnameinfo(), shutdown(), close()

memcpy(), memset(), strcpy(), strncpy(), strlen(), sizeof()

For getnameinfo() use see the man pages or look at the TCP example server.c

Task for week 44: TCP #1

CT30A5002 - Games and Networking