meta data for this page
  •  

Assignment 4 notes

This page contains notes about assignment 4 as well as some corrections to common problems.

Comments

Student number Comment
0312399 No need to separately set multicast interface, the struct ip_mreq should contain it and all can be set with one socket option when joining to multicast group. Queries (WHOIS) and replies (HOSTINFO) are sent from different ports (query using multicast port, reply using game port). The clients reply to queries but the advertisement of games does not work → the games cannot be shown on clients (your or others). The game packet checks do not seem to be working well. Game somewhat works with other clients.
0316023 Sometimes the first placement message goes to wrong peer – that accepts it but hasn't started the game and the mark stays in the grid. The marks show as reverse, if first player puts 'o' then peer sees it as 'x'. The game starting detection does not seem to be always working. Client does not remove itself from multicast. The indexes are handled in opposite direction (x → y & y → x)
0342246 Client does not drop multicast membership. Only a stub implementation. Otherwise multicast part works ok.
0342327 No game implementation but the multicast part seems to be working and some data can be sent to opened sockets.
0358454 You cannot use strlen() to get a message length that has no characters, the WHOIS message contains only the 0 (NULL) and strlen() would return 0. No game implementation and the multicast somewhat works.
0377037 Automatic game selection makes this a bit confusing and hard to test, user should select it. There are lots of code here, most of it is not used, why you commit all of these? The client replies to WHOIS if playing a game. Game can be started but functionality seems to stop there. Game ends with core dump when calling quit. 
0378227 Some warnings indicating that it will crash at some point. Invalid binary name. Invalid parameters. Does not follow the specification at all.
0404450 The player who connects should have the first turn but otherwise everything works as it should.
0404489 Works ok, except the game can be crashes with too short placement packages.

Multicast

Here is a way to use getaddrinfo() and getnameinfo() with multicast addresses.

// Sockets
int multicastsocket = -1, peersocket = -1;
 
int in_addr_length = sizeof(struct in_addr);
 
// For storing addresses and ports
char hostbuffer[NI_MAXHOST] = {0}; // Set the multicast address here
char portbuffer[NI_MAXSERV] = {0}; // Set the port here
char peerportbuffer[NI_MAXSERV] = {0}; // Set the port for incoming connections here
 
char buffer[MESSAGELENGTH] = {0};
 
// For getaddrinfo()
struct addrinfo *result = NULL, *iter = NULL;
struct addrinfo hints = { .ai_flags = AI_NUMERICHOST,
                          .ai_family = AF_INET,
                          .ai_socktype = SOCK_DGRAM,
                          .ai_protocol = IPPROTO_UDP};
 
// The multicast address is stored here
struct sockaddr mc_sending_addr:
memset(&mc_sending_addr,0,sizeof(mc_sending_addr));
 
// Who sent us data
struct sockaddr receiving_addr;
memset(&receiving_addr,0,sizeof(receiving_addr));
 
// Multicast structure
struct ip_mreq multicast;
memset(&multicast,0,sizeof(multicast));
 
// First fill multicast & sending address
if(getaddrinfo(hostbuffer,portbuffer,&hints,&result)) perror("Cannot resolve multicast address");
 
for(iter = result; iter != NULL; iter = iter->ai_next) {
 
  // Set the multicast address (since we need the struct in_addr this is not so portable...)
  memcpy(&multicast.imr_multiaddr, &((struct sockaddr_in*)iter->ai_addr)->sin_addr,in_addr_length);
 
  // Send address - the multicast address
  memcpy(&mc_sending_addr,iter->ai_addr,iter->ai_addrlen);
}
freeaddrinfo(result);
 
// Then get the listening address (any), using the port defined for multicast
hints.ai_flags = AI_PASSIVE;
if(getaddrinfo(NULL,portbuffer,&hints,&result)) perror("Cannot get listening address");
 
for(iter = result; iter != NULL; iter = iter->ai_next) {
 
  // Create socket and bind it
  if((multicastsocket = socket(iter->ai_family,iter->ai_socktype,iter->ai_protocol)) < 0) perror("Cannot create socket");
  if(bind(multicastsocket,iter->ai_addr,iter->ai_addrlen)) perror("Cannot bind to address");
 
  // Set the any-address as interface for multicast
  memcpy(&multicast.imr_interface,&((struct sockaddr_in*)iter->ai_addr)->sin_addr,sin_length);
}
freeaddrinfo(result);
 
// And with same approach get the peer listening address, using the peer port (and previously set hints with AI_PASSIVE)
if(getaddrinfo(NULL,peerportbuffer,&hints,&result)) perror("Cannot get peer listening address");
 
for(iter = result; iter != NULL; iter = iter->ai_next) {
  // Create socket and bind it
  if((peersocket = socket(iter->ai_family,iter->ai_socktype,iter->ai_protocol)) < 0) perror("Cannot create socket");
  if(bind(peersocket,iter->ai_addr,iter->ai_addrlen)) perror("Cannot bind to address");
}
freeaddrinfo(result);
 
// Finally we can join (add membership) to multicast group
if(setsockopt(multicastsocket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&multicast,sizeof(multicast)) < 0)
  perror("Error joining multicastgroup");
 
...
 
// when we receive something from multicast
n = recvfrom(multicastsocket,buffer,MESSAGELENGTH,0,&receiving_addr,&length);
 
// Get the address
if((getnameinfo((struct sockaddr*)&recv_addr,length,hostbuffer,NI_MAXHOST,portbuffer,NI_MAXSERV,NI_NUMERICSERV)) != 0) perror("Cannot get IP");
 
// 1. Get the port from message
// 2. Replace the port in portbuffer with the one in the message
// 3. Use getaddrinfo to fill in struct
hints.ai_flags = 0;
if(getaddrinfo(NULL,portbuffer,&hints,&result)) perror("Cannot fill peer address");
 
// 4. Use the struct sockaddr in the result to send data to new port on that host
 
...
 
// And drop the membership before quitting
if(setsockopt(multicastsocket,IPPROTO_IP,IP_DROP_MEMBERSHIP,&multicast,sizeof(multicast)) < 0)
  perror("Error leaving multicastgroup");