By implementing the program specified in this assignment, you will familiarize yourselves with the Unix network programming API (specifically, the Berkeley socket API). The assignment is to be done in groups of size at most 2. You are responsible for finding your partner (if you need one), for finishing off the assignment in case your partner drops the class or backs out from completing the assignment for whatever reason. Please carefully read the sample codes I've given, and build your program on top of the examples. Doing so is not absolutely required, but it will likely save you a lot of time.
The program
is to be written in C under a Unix platform such as Linux, SunOS,
Solaris, or FreeBSD, or even Mac OS X. Under Windows, it is possible to
develop your program with cygwin
but you must make sure that:
Makefile
I provided as a template, so
that we can compile your program under these two platforms (with a
simple change).
The program
to be written is called pingpong
(or your favorite name), whose features are described below.
Note that there is only
one program to be written. Many students in
the past tried to write a 'client' and a 'server', for unknown
reasons.
pingpong
takes as arguments a TCP port and a UDP port on which it will listen to
incoming TCP connection requests and incoming UDP packets,
respectively:
pingpong <tcp-port> <udp-port>
pingpong
must handle improper inputs graciously, i.e. some kind of error report
and usage information should be printed if the user does not type the
correct input parameters, or if the ports cannot be bound, etc. I will
post my implementation of pingpong
within a week so that you know what to expect. In fact, your
implementation of pingpong
should inter-operate with mine if you implement the following
specification correctly.
pingpong
operates somewhat like a Unix shell. It keeps taking user's commands,
at the same time watches incoming connection requests and data messages
and packets.
When appropriate pingpong
prints out diagnostic information to stdout
and then goes back to the "prompt mode"
to accept user's inputs.
We will call each incarnation (process) of pingpong
a peer.
Below is a description of all commands pingpong
is supposed to handle, along with the description of associated
actions:
info
:
print the peer's (canonical) IP address, host name, TCP (listening)
port,
UDP port, and process ID. The output of the info
command looks like this:IP address | hostname | udp port | tcp port | PID
------------------------------------------------------------------
192.168.0.3 | funny.cse.buffalo.edu | 4892 | 43444 | 513105
connect
<ip-address> <tcp-port>
:
try to establish a TCP connection to <ip-address>
at port
<tcp-port>
. For exampleconnect 192.168.0.3 99999
both sides of the connection then report if the connection has indeed been established or not (i.e. been denied, or IP/port does not exist). The error messages have to be informative on what happened
show
:
show all existing TCP connections in the following format:
cID | IP | hostname | local port | remote port
------------------------------------------------------------------
1 | 192.168.0.1 | abc.cse.buffalo.edu | 1234 | 1235
2 | 192.168.0.2 | def.cse.buffalo.edu | 1453 | 98234
3 | 192.168.0.3 | ghi.cse.buffalo.edu | 1233 | 09823
4 | 192.168.0.4 | xyz.cse.buffalo.edu | 1235 | 0823
send <string>
:
send a ping message
containing the <string>
to all TCP connections. Here, <string>
is the entire
text (including blank spaces) that follows the command send
(discounting the white spaces right after send
).
For example, if the user types
send Oh! This assignment is a piece of cake.then
<string>
is the string "Oh! This assignment is
a piece
of cake.".
The precise format of the ping message will be described later.
Briefly, the ping message
will contain the ping message's 10-byte ID, the sender's IP, listening
TCP port, and UDP port, along with the <string>
.
Upon receiving a ping message from a neighbor, a peer first checks
if it had seen the message before by comparing the message ID against
the IDs it has stored in a "routing table". Each routing table is a
FIFO queue having at most RTS
entries in it. RTS
is a constant set to 3, but as a good
programming practice we define a constant and use its name throughout
the program instead of its value.
If the peer had seen the ping message before, it will simply ignore the message.
Otherwise, the peer inserts the message ID into the routing table and forward the ping message as is (i.e. the message content unchanged) to all neighbors except for the neighbor where the message came from. Furthermore, the peer will also send a UDP pong packet back to the source peer of the ping message (the IP address and UDP port of the source are contained in the ping message), informing the source that it has received the ping message. The format of the UDP pong packet, to be specified, basically consists of the peer's IP, the message's string, TCP listening port, UDP port, and the process ID. Finally, the peer also prints the content of the ping message it just received (for the first time). For instance,
PING received: Oh! This assignment is a piece of cake.The source reports the content of any pong packet it receives to the screen, in the same format as above, replacing PING by PONG, without the connection #. For example,
From connection #: 2
source's IP: 192.168.1.104
source's TCP port: 12345
source's UDP port: 54321
source's PID: 55555
PONG received: Oh! This assignment is a piece of cake.
sender's IP: 192.168.1.110
sender's TCP port: 12345
sender's UDP port: 54321
sender's PID: 55555
disconnect
<conn-id>
:
disconnect the TCP connection whose id is <conn-id>.quit
:
quit the current processunknown
command
"MAX_NO_CONNS
TCP connections at the same time.
This constant should be set to 3 by default.
When the maximum is reached, pingpong
will either display an error message (if the extra connection request
comes from the command line), or deny the connection establishment
request (if the extra request comes from another peer).
By "denying a request" I mean simply accept()
and then close()
the socket right
away, which makes your life simpler. Feel free to find other ways to do
this,
like designing a simple 3-way handshake protocol for pingpong
.
MAX_NO_CONNS
is not
reached) every TCP connection has to be kept alive until either
one end quits or closes the connection.
connection number 5 was closed by peerThe side which received the
disconnect
command from the
user
reports something like:
connection number 3 is closed
select()
for
I/O multiplexing of TCP sockets, UDP socket, and stdin
.uname -a
.)
bytes | field | content |
---|---|---|
0-1 | type | set to PING = 0x00 . (you might think that this
is redundant, but what if we amend our protocol to include messages
other than ping messages? This sort of "place holder" design is very
common in practice!). This is in big-endian |
2-11 | message ID | a globally unique identifier for the message. You probably should use the IP, ports, some counter, and random values to construct this ID. Different messages sent by the same or different peers should have different IDs with extremely high probability. |
12-15 | IP | the source's IP address, in big-endian |
16-17 | TCP port | the source's listening TCP port, in big-endian |
18-19 | UDP port | the source's UDP port, in big-endian |
20-23 | Process ID | the source's PID, in big-endian |
24-27 | Payload length | Length of the data that follows. |
28- | Payload | This is the <string> that the user typed,
including the '\0' character at the end.
Thus, the payload length is the string length plus one! |
bytes | field | content |
---|---|---|
0-1 | type | set to PONG = 0x01 . This is in big-endian.
|
2-5 | IP | the sender's IP address, in big-endian |
6-7 | TCP port | the sender's listening TCP port, in big-endian |
8-9 | UDP port | the sender's UDP port, in big-endian |
10-13 | Process ID | the sender's PID, in big-endian |
14- | Payload | This is the <string> from the ping message
that this pong packet replies to, including
the '\0' character at the end.
|