Description
Overview In this assignment you will develop a pair of programs that will interact to model a game, which is described below. The game is simple, but the assignment will give you hands-on practice with creating a multi-process application, processing command line arguments, setting up and monitoring network communication channels between the processes (using TCP sockets), and reading/writing information between processes across sockets. The game that will be modeled is called hot potato, in which there are some number of players who quickly toss a potato from one player to another until, at a random point, the game ends and the player holding the potato is “it”. The object is to not be the one holding the potato at the end of the game. In this assignment, you will create a ring of “player” processes that will pass the potato around. Thus, each player process has a left neighbor and a right neighbor. Also, there will be a “ringmaster” process that will start each game, report the results, and shut down the game. To begin, the ringmaster creates a “potato” object, initialized with some number of hops and sends the potato to a randomly selected player. Each time a player receives the potato, it will decrement the number of hops and append the player’s ID to the potato. If the remaining number of hops is greater than zero, the player will randomly select a neighbor and send the potato to that neighbor. The game ends when the hop counter reaches zero. The player holding the potato sends it to the ringmaster, indicating the end of the game. The ringmaster prints a trace of the game to the screen (using the player identities that are appended to the potato), and shuts the game down (by sending a message to each player to indicate they may shut down as the game is over). Each player will establish three network socket connections for communication with the player to the left, the player to the right, the ringmaster. The potato can arrive on any of these three channels. Commands and important information may also be received from the ringmaster. The ringmaster will have N network socket connections. At the end of the game, the ringmaster will receive the potato from the player who is “it”. The assignment is to create one ringmaster process and some number of player processes, then play a game and terminate all the processes gracefully. You may explicitly create each process from an interactive shell; however, the player processes must exit cleanly at the end of the game in response to commands from the ringmaster. Communication Mechanism In this assignment, you will use TCP sockets as the mechanism for communication between the ringmaster and player processes. Your programs must use exactly the command line arguments described here. The ringmaster program is invoked as shown below, where num_players must be greater than 1 and num_hops must be greater than or equal to zero and less than or equal to 512 (make sure to validate your command line arguments!). The server program is invoked as: ringmaster (example: ./ringmaster 1234 3 100) The player program is invoked as: player (example: ./player vcm-xxxx.vm.duke.edu 1234) where machine_name is the machine name (e.g. login-teer-03.oit.duke.edu) where the ringmaster process is running and port_num is the port number given to the ringmaster process which it uses to open a socket for player connections. If there are N players, each player will have an ID of 0, 1, 2, to N-1. A player’s ID and other information that each player will need to connect to their left and right neighbor can be provided by the ringmaster as part of setting up the game. The players are connected in the ring such that the left neighbor of player i is player i-1 and the right neighbor is player i+1. Player 0 is the right neighbor of player N-1, and Player N-1 is the left neighbor of player 0. Zero is a valid number of hops. In this case, the game must create the ring of processes. After the ring is created, the ringmaster immediately shuts down the game. Resources: Refer to our lecture notes and example code on TCP sockets for establishing communication between the ringmaster and players. You can find 05 – tcp_example.zip under Sakai resources to study the general process of establishing TCP communication. Beej’s Guide to Network Programming is an excellent reference resource for this assignment. You will also find that you will need to use the “select” call over a set of file descriptors from both the ringmaster and player processes in order to know when some information has been written to one of a set of the socket connections. You will likely also find the functions “gethostname” and “gethostbyname” helpful in establishing the connections between neighboring players in the ring and between players and the ringmaster. “getsockname” would help find the port on which your client program has bound to listen (See FAQ Q2). Finally, you will need to create random numbers (e.g. between 0 to N). To do so, you may use the rand() call. Your code should first seed the random number generator: srand((unsigned int)time(NULL)+player_id); Then you may generate a random number between 0 and N-1 using: int random = rand() % N; Output: The programs you create must follow the description below precisely. If you deviate from what is expected, it will impact your grade. The following describes all the output of the ringmaster program. Do not have any other output. Initially: Potato Ringmaster Players = Hops = Upon connection with a player (i.e. each player should send some initial message to the ringmaster to indicate that it is ready and possibly provide other information about that player): Player is ready to play When launching the potato to the first randomly chosen player: Ready to start the game, sending potato to player When it gets the potato back (at the end of the game). The trace is a comma separated list of player numbers. No spaces or newlines in the list. Trace of potato: ,, … Sample Ringmaster Output: Potato Ringmaster Players = 3 Hops = 200 Player 1 is ready to play Player 0 is ready to play Player 2 is ready to play Ready to start the game, sending potato to player 2 Trace of potato: 2,1,2,0,2,1,0,2,… The following describes all the output of the player program. Do not have any other output. After receiving an initial message from the ringmaster to tell the player the total number of players in the game, and possibly other information (e.g. info about that player’s neighbors): Connected as player out of total players When forwarding the potato to another player: Sending potato to When number of hops is reached: I’m it Sample Player Output: Player 0 (in this example not the last player): Connected as player 0 out of 3 total players Sending potato to 2 Sending potato to 1 Sending potato to 1 Sending potato to 2 Sending potato to 2 Sending potato to 2 … Sending potato to 1 Similar for Player 2 Player 1 (in this example the last player, i.e. receives potato on last hop): Connected as player 0 out of 3 total players Sending potato to 0 Sending potato to 2 Sending potato to 0 Sending potato to 0 Sending potato to 2 Sending potato to 2 Sending potato to 2 … Sending potato to 0 I’m it Detailed Submission Instructions Your submission will include source code files and a Makefile that you create. Your source code files should contain at least the following: 1. ringmaster.c – The source code for your ringmaster program as described above. 2. player.c – The source code for your player program as described above. 3. Makefile – A makefile that will compile ringmaster.c and player.c into executable programs named ringmaster and player, respectively 4. potato.h – A source code file containing a potato structure that can be sent across TCP sockets between players and between players and the ringmaster. You will submit a single zip file named “netid_proj3.zip” to Gradescope, e.g.: zip netid_proj3.zip ringmaster.c player.c potato.h Makefile Grading Rubrics Establishing connection between server and players: 20% Test cases for programming part: 50% Writing part: 30% FAQ 1) Should the game work across the network on different machines? a) Yes. Network sockets must work on the network, therefore, running the ringmaster and player on different machines should still allow a functional game. 2) Will our code be checked against Valgrind for memory errors? a) Not directly. However, these issues may show up in other testing as practical problems. Also, it is always recommended to write leak free code. Some common issues and how to fix them: ● Sending uninitialized bytes on the socket: Use memset() to initialize the data buffer. ● Forgetting to free objects allocated on the heap: e.g. forgetting freeaddrinfo() after getaddrinfo(), free() after malloc(), etc. How to use valgrind to catch above issues: valgrind -v –leak-check=full –track-origins=yes 3) How to use getaddrinfo()? a) Look at the man page. Make sure to loop through the results of getaddrinfo()for the bind etc. as seen in the man page. This is to help overcome practical constraints of invalid hostnames, etc. 4) Which address and port should I bind to for accepting incoming connections? ● The address you bind to will be the address that clients will connect to. For example, if you bind to 127.0.0.1, others can only connect by connecting to 127.0.0.1, which limits the clients to be on the same machine. (See FAQ Q1) ● However, if you bind to 0.0.0.0 (aka INADDR_ANY), clients can connect to you using any address, including 127.0.0.1 and the public IP of the machine (if any). Therefore the most general solution is to bind to address 0.0.0.0. ● If you have a specific port number to bind to, use that. Otherwise, if you specify 0 in sin_port when calling bind, the OS will assign a port for you. After that, you can use getsockname() to see which port the socket is bound to. 5) How do I know my own IP address? ● The command ip addr can show you the IP address of each of your network interface. ● For the purpose of Project 3, neither the ringmaster nor the player has to do that. The player can know the ringmaster’s address by resolving its hostname, which is given in the arguments. The ringmaster can know the player’s address as well; see the next question. 6) If a client connects to me, how do I know its IP address? ● The accept syscall will fill in the addr argument, which is a sockaddr struct containing the client’s IP address. 7) My laptop cannot accept incoming connections. ● Your laptop may not have a public IP address and/or a hostname that is recognized by others. Solving these problems is beyond the scope of Assignment 3. You might be interested in the following readings, though: Network address translation, Port forwarding, DMZ. ● For the purpose of this assignment, it is a good idea to test your programs on machines that have public IPs and/or hostnames. VCM is a good choice, and I believe you should be able to get at least 2 VMs. 8) My system has a max limit on number of incoming socket connections I can accept. Is this okay? a) Yes. On the accept call of ringmaster, you may only be able to support 1020 incoming connections. This can also be checked using ulimit -n. You do not have to modify or worry about this. 9) Do I care about what send() and recv() return? a) Yes. See the man pages for both. Also, make sure to handle the error conditions and socket closure cases, e.g. -1 for error, 0 for closed socket on recv, etc. See the man pages for more details. 10) What is select()? Do I care about it? a) See man select. select() is a function that allows you to see which of your file descriptors is ready to send you content. Since this game involves each player being connected to two other players and you may get a potato from any side, you need to use this function to check where to recv data from. b) Also, make sure to reset the readfds structure used by this function every time since after calling select, it only contains the fds of sockets that are currently sending you data. 11) send() fails to send my complete message. a) This is not a bug. This is how send() is designed. In C/C++ without using additional libraries, the basic send() does not guarantee that all of the bytes specified in message buffer size will be sent. On success, these calls return the number of bytes sent. See man page for send. You may need to use some loops to make sure your entire message buffer is sent. 12)recv() fails to receive complete message. a) Same as above. However, you can use MSG_WAITALL if you’re certain about the number of bytes you need to receive. 13)What data structure should I send in the socket buffer? a) The socket buffer takes a byte stream. It may be good enough to have a protocol that sends the size of a string followed by a string, i.e. char stream in your buffer. The size will let you know how much data to receive, since these communications may be of varied length. Feel free to play around and invent your own protocol. Sending structs on a socket buffer will need more work as opposed to a string. 14)Can number of hops be 0? a) Yes. “Zero is a valid number of hops. In this case, the game must create the ring of processes. After the ring is created, the ringmaster immediately shuts down the game.” – Described in Communication Mechanism No trace of hops is printed in this case. 15) What is the first hop? a) The first hop is from the ringmaster to the first player, selected at random. 16) What happens on the last hop? / When should the game end? a) When the potato is passed to a player on the last hop, the game ends. The full trace of the potato should then be sent to the ringmaster — this does not count as a hop. The game ends and all processes must close/complete. The order in which processes exit does not matter. 17) When I try to run the ringmaster consecutively using the same port it gives me an error that the port is in use although I ended the program. What should I do? a) It takes a while for the port to be set as free again. You can let the OS know to set it to free immediately by using the setsockopt() with SO_REUSEADDR option. See ref. 18) Can I add some time delay in my code for synchronization? a) No, you may not. Synchronization must be achieved using a good logical series of steps and any loops as required to ensure success of connections. Timing based connections are arbitrary and not acceptable solutions. This will also cause the grading time for your code to get out of hand and may result in a penalty. 19) Do I have to deal with a malicious/arbitrary process connecting to my players/ringmaster and sending random messages? a) No. You can design your own communication protocol and adhere to it. We will not test how you handle arbitrary connections in your game instead of your own players, etc. 20) Is there a specification on how to handle issues such as player disconnecting in the middle of the game, loss of network, etc.? a) No, you may choose how you deal with these situations. However, silent failure is a bad software engineering practice.