This program enables a number of users (upto MAXPEERS
) to chat with each other using network sockets. Each user has a pre-defined port and IP address and this information is public.
The program files include a makefile, which can be used for compilation:
make p2p
The binary can now be run by providing your (own) username:
./p2p peer-one
This outputs the following:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Hi peer-one! Welcome to this simple peer-to-peer chat program.
Type the name of the peer, followed by a /, and then your message.
Type [broadcast] instead of name to send to all active peers.
[Recompile the program after setting the `LOG` macro to enable logging.]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
As mentioned, the user can start chatting by providing the name of the receiver, following that by / and then the actual message.
A sample input/output is shown here:
peer-one's screen | peer-two's screen |
---|---|
... peer-two/Hey, you online? peer-two\Yeah man, what's cooking? peer-two/Nothing much, wanna play some Halo? peer-two\Sure, let's start! ... |
... peer-one\Hey, you online? peer-one/Yeah man, what's cooking? peer-one\Nothing much, wanna play some Halo? peer-one/Sure, let's start! ... |
MAXPEERS = 5
Port | IP | Name |
---|---|---|
5001 | 127.0.0.1 | peer-one |
5002 | 127.0.0.1 | peer-two |
5003 | 127.0.0.1 | peer-three |
5004 | 127.0.0.1 | peer-four |
5005 | 127.0.0.1 | peer-five |
The program starts by defining the address structs, one for self and one for peers. It then initializes a collection of structs of sockets which will be used to read and write messages. A listener socket is bound to the self address, which listens for incoming connection requests.
At this point, the file descriptor read set is initialized for use in the select()
call. An infinite loop now runs, in which the following take place:
- The standard input and TCP listener sockets are inserted into the read set.
- The collection of sockets we initialized earlier is checked for three things: one, whether a socket has been put in use yet (its
live
value is 1), two, whether the socket has reached a certain period of inactivity (10 minutes by default), and three, whether the connection on that socket is still alive. If all conditions are true, the socket is inserted into the read set. If the connection has been timed out or lost, the socket is reset. - The
select()
function is called with the read set. - Another loop runs on the collection of sockets, basically checking whether they're ready to read from. If yes, the
read()
function is called and the message is displayed on the console. - The listener socket is now checked as to whether it is ready to read from. If yes, the
accept()
function is called and a new connection is saved. - The standard input file descriptor is now checked whether it is ready to read from. If yes, the index of the receiver is extracted from the input. A conditional check runs to see whether we already have a connection to the aforementioned peer index. If not, the
connect()
function is called and a new connection is saved. The message is then written to the new connection using thewrite()
function.