How to use the distributed code for assignment 2
The code distributed for assignment 2 consists of a straight-forward
Go-back-N implementation and a select-loop that has been made more
generically usable.
Some files are only distributed as object code. Those are the parts that
implement the functionality that was required in assignment 1, an
assignment that we would like to be able to reuse next year.
The files that are distributed as source code are:
?? events.h - Declares function pointer data types, some structs for
????????????? managing things that select can wait for, the declaration of
????????????? the select-loop function that is named wait_for_event,
????????????? several functions for registering and unregistering things
????????????? that you can wait for, and a function that checks whether any
????????????? socket has become invalid and that you can call from wherever
????????????? you want.
?? events.c - The definitions of the select-related functions. They are
????????????? included in source code because it is quite probable that
????????????? functions pointer and structs that have been registered in
????????????? order to wait for events will give you a hard time when
????????????? you're not used to this kind of code. Having the code allows
????????????? you to use the debugger or printf() and see what's really
????????????? going on.
????????????? More about the mechanisms in "select loop".
?? link.h - Declares my link layer header and the link layer functions that
??????????? must be visible for other modules. It defines the macro
??????????? PAYLOAD_LEN as well, which tells you how many bytes your
??????????? packets can be long.
??????????? You need to do something with (nearly) every function that is
??????????? declared in this file!
??????????? More under "Using link layer functions".
?? phys.h - Declares my linksession structure and declares the functions
??????????? Lopen, Lopenenable and Lopendone. It's called phys.h because
??????????? the Lopen* functions are used to set up the emulated physical
??????????? layer underneath the link layer. More about using the functions
??????????? with multiple linksessions under "Select with listen sockets".
??????????? Don't change anything in this file. You will get into trouble.
??????????? You will have to write code that maps the output of your
??????????? routing table to the linksession.
?? net.h - Declares the prototypes of the network layer functions. The link
?????????? layer is calling the functions with these prototypes, so you
?????????? should make sure that you implement functions that fit to them.
?????????? You can ignore all of the parameters if you don't need them, but
?????????? you must return 0 or 1 from Ndemux.
?? bool.h - Defines the type bool and the values true and false as they
??????????? exist in C++. Be careful when you use true. In C, everything
??????????? that is not 0 is true.
??????????? You should not change this file.
?? debug.h - A debug macro to switch debug messages in small programs on
???????????? and off in a simple way. To use it you write lines like the
???????????? following:
???????????? DEBUG((stderr,"print the number %d\n", 12));
???????????? and by changing #if 0 to #if 1 you can get this printed at
???????????? runtime.
???????????? You can change this file as you like.
?? crc.h - The prototype of my CRC-19 function. It's used internally by the
?????????? link layer. You don't have to touch the file at all.
?? burst_write.h - Declaration of burst_write. Since burst_write is used by
?????????????????? link.o, you should not touch it.
?? burst_write.c - Definition of burst_write. Since link.o cares only about
?????????????????? the interface, you can change and recompile
?????????????????? burst_write.c. For example if this is going to slow for
?????????????????? your taste, you can increase the SPEED from 1000 to
?????????????????? 10000. Or you can simply call write() directly to get
?????????????????? rid of the complications.
Select loop
? - wait_for_event() should be the code in your program that waits.
??? Whenever you think that something else in your program should wait, it
??? is probably better to register a timeout with select and get back to
??? whatever it is you are waiting for after a while.
? - wait_for_event() does mainly fill in the timeout and read set for
??? select, call select and wait until something happens, then check what
??? has happened, and call one function for every event.
? - Before calling wait_for_event, all timeouts are checked against the
??? current time. If a timeout has expired, it's callback function is
??? called right away.
? - Then the timeout values for select are set. Usually, select will block
??? infinitely when no timeouts are pending, and otherwise it will wait for
??? a socket event or a timeout, whichever comes first.
??? However, you can also register a function of your own that is called
??? before initializing the timer for select. If that function returns
??? something else than 0, select will only check the sockets and return
??? immediately. You use the function registerBusyWaitCheck() to register
??? such a test function.
? - When select returns, errors are checked first, and if there are any,
??? every socket is checked for errors. For every socket that has an error,
??? Nerror is called with its linksession*. (if you have registered Lerror
??? correctly, otherwise the program crashes)
? - If there are no errors, all registered active and passive sockets are
??? checked and their registered functions are called when something
??? happened for then.
? - Then stdin is checked, and if there was an event, the registered
??? keyboard function is called if there is any.
? - Afterwards, the timers are checked again.
? - Then, wait_for_event() returns, and you should probably called it again
??? right away without doing anything else.
Using link layer functions
extern void LupcallTimeout( linksession* link, const char* started_how );
extern void LretransTimeout( linksession* link, const char* started_how );
? - These function are called whenever link layer timers expire. You don't
??? have to do anything with them.
extern int? Lpush(char buf[], linksession* destination, int length);
? - This function must be called by your network layer to send packets over
??? the link that is identified by the linksession 'destination'. It
??? returns 0 when the 20-frame sliding window on the sender side is full,
??? and 1 if it was able to stored another frame in the window.
extern void Lreceive(linksession* link );
? - This function implements reception of frames at the link layer.
??? YOU HAVE TO REGISTER IT with the select loop for every linksession that
??? you create.
??? The function checks the CRC of the frames, checks the sequence number,
??? send ACKs and in case of success, tries to deliver a packet to the
??? network layer. It calls Ndemux for delivering the packet to the
??? network layer. If Ndemux returns 0, it starts a timeout for trying
??? again.
??? For every single linksession that you create you have to register this
??? function once. This works as follows:
???? - you have a brand new linksession and a pointer to it, named for
?????? example l_ptr
???? - allocate a new OpenSocket structure dynamically, you get a pointer
?????? to it, named for example sockptr
???? - you do
???????? sockptr->sock?????????? = l_ptr->sock;
???????? sockptr->function?????? = &Lreceive;
???????? sockptr->error_function = &Lerror;
???????? sockptr->link?????????? = l_ptr;
???????? sockptr->next?????????? = NULL;
???????? registerOpenSocket( sockptr );
???? - and from now on your socket is registered for the select loop to
?????? check every time select is called. sockptr->sock will be included in
?????? the fd_set readset.
???? - If FD_ISSET(sockptr->sock, &readset) is true, the function
?????? sockptr->function will be called with the parameter sockptr->link.
?????? Since sockptr->function points to Lreceive and sockptr->link points
?????? to the same data as l_ptr, this means really that Lreceive(l_ptr) is
?????? called.
???? - If anything wrong is detected with the socket sockptr->sock,
?????? (sockptr->error_function)(sockptr->link) is called, which means
?????? really that Lerror(l_ptr) is called. It calls Ndemux(l_ptr) in turn.
?????? You can do whatever you want in that situation, but most probably
?????? you want to close the socket, unregister it and forget about the
?????? linksession, as follows:
???? - You find the pointer sockptr that belongs to the OpenSocket
?????? structure for the socket l_ptr->sock and call
???????? cancelTimeout(&l_ptr->retransTimeout);
???????? cancelTimeout(&l_ptr->upcallTimeout);
???????? close(l_ptr->sock);
???????? unregisterOpenSocket(sockptr);
???????? free(sockptr);
???????? free(l_ptr);
extern void Lerror(linksession* link );
? - This function is called when something goes wrong with the socket
??? link->sock, for example if it is closed by the other side. It calls
??? Nerror in turn and you can do whatever you want.
Select with listen sockets
The function wait_for_event() can wait for all kinds of things. When you
tell it what to do, it can also wait for active opens from other machines.
This works as follows:
? - You write a function with the following declaration that can handle
??? passive connections:
????? void execute_passive_open( struct LinkSession* fakesession );
? - You write a function for handle errors, even though it is not very
??? likely that it is ever called in this situation:
????? void error_in_passive_open( struct LinkSession* fakesession );
? - You create a socket suited for a passive open, pseudo-code as follows
????? listen_socket = Lopenenable( port );
? - You register the listen_socket for select:
????? struct OpenSocket* listenptr;
????? linksession*?????? l_ptr;
????? listenptr = (struct OpenSocket*)malloc(sizeof(struct OpenSocket));
????? l_ptr???? = (linksession*)malloc(sizeof(linksession));
????? l_ptr->sock?????????????? = listen_socket;
????? listenptr->sock?????????? = listen_socket;
????? listenptr->function?????? = &execute_passive_open;
????? listenptr->error_function = &error_in_passive_open;
????? listenptr->link?????????? = l_ptr;
????? listenptr->next?????????? = NULL;
????? registerOpenSocket(listenptr);
? - For every active open by another machine, your function
??? execute_passive_open will be called once. In that function, you
??? do the following
????? void execute_passive_open( struct LinkSession* fakesession )
????? {
????????? struct OpenSocket* sockptr;
????????? linksession*?????? link;
????????? link = Lopendone(fakesession->sock);
????????? sockptr = (struct OpenSocket*)malloc(sizeof(struct OpenSocket));
????????? sockptr->sock?????????? = link->sock;
????????? sockptr->function?????? = &Lreceive;
????????? sockptr->error_function = &Lerror;
????????? sockptr->link?????????? = l_ptr;
????????? sockptr->next?????????? = NULL;
????????? registerOpenSocket( sockptr );
????????? /* do something more */
????? }
? - That could be it. From now on, Ndemux will be called when packets over
??? that brand new connection arrive. However, you need to know the name
??? and port that made the active connection. You can do that in
????????? /* do something more */
??? in the following way:
????????? struct sockaddr_in addr;
????????? unsigned int?????? len;
????????? int??????????????? err;
????????? len = sizeof(addr);
????????? err = getsockname( link->sock, &addr, &len );
????????? if( err < 1 ) { ... /* handle error */ }
??? and if you have no error, you can find the other machine's address in
????? addr.sin_addr
??? and other machine's port in
????? addr.sin_port
? - Of course the port is in network byte order and the address is in a
??? format that must be converted to a readable format. For the address
??? conversion, you could use the functions inet_ntop or inet_ntoa (use man
??? to find out more about them). Alternatively you could use gethostbyname
??? to convert the IP address that you have read from file for comparing.
? - Now you have found out which one-character-address belongs to which
??? linksession* and you can arrange you routing table accordingly.
? - Then you can return from the function execute_passive_open, and go back
??? to waiting for new events using select.