2. Abstract
The purpose of this project was to create a peer to peer server that was capable of
registering content, deregistering content, and downloading content from a peer that is able to be
both a server and a peer at the same time. The motivation for the implementation of this project
was to familiarize with socket programming and TCP communication channels. The approach
towards this project was to first create and implement a working server, then to create and
implement a working client that was able to connect and send requests to the server. The results
obtained was that when the server used processes, although nothing was shared between the
clients or processes, the server and client was able to execute and handle all required commands.
But when the server implemented threads, the connection between the server and client causes
the server to reach a segmentation fault and crash, where this was fixed by using a function from
a separate C library to allocate the index integer. From the results shown, it can be seen that
processes can be implemented easily, but processes lack utilities. On the other hand threads are
more complex to implement, but thread contain much more utilities than processes.
3. 1. Introduction
What is a socket? A socket is one of the end points in a peer to peer server connection,
this connection is also known as a point to point server connection. The socket allows programs
to have a two way connection with another program running on a network connection. The
process of programming a socket to establish a two way connection between two programs on a
network is known as socket programming. There are two main types of connections, or
communication channels, in existence today, one is known as TCP, the other is known as UDP.
The TCP connection allows and provides a reliable communication channel that allows a peer to
peer or point to point connection over a network, such as the internet. The TCP connection is set
up by having each program, which wants to communicate through a network, bind a socket to the
end of their connection, then read and write from the bound socket to communicate with other
programs. In this project, the TCP connection type was used to establish the connections between
the peers and the server.
The objective of this project was to implement a peer to peer server connection between
at least two peers and a central server. The central server was required to be able to accept
content registration requests from the peers and register the peers’ requested contents into a
central storage of addresses and content names. The server was also required to be able to accept
content search requests, content de-registration requests, and list of online registration content
requests. These commands from the peer to the server are given in the form of PDUs. PDUs are a
byte of data that was concatenated to the front of the content data. This byte of data is known as
the header, this header was used to differentiate the different types of data being sent to and from
the server. These different data types are defined and shown in table 1.
Table 1: PDU Types
2. Implementation and Specifications
The specifications for this project were to have a working peer to peer server that was
composed of a central server, and at least two working clients. The server required for this
project was required to be able to remain open and wait for clients or peers to connect. After a
client or peer connects, the server was required to be able to listen for input commands from the
clients or peers. The commands that the server is required to listen for are the register content
command, content search command, content de-register command, list of online registration
content command, and the content download command. These commands each have a type
header that is one byte long in size and concatenated to the beginning of each data word being
4. sent over the network. These data type headers are shown in table 1, the server must be able to
decode these data types and perform the required action to the attached data according to the
command specified by the header byte. When the “R” PDU type is received, the server was
required to register the content name, which was given together with the “R” PDU type, and the
contents’ address, which was also given together with the “R” PDU type. When the server
receives the “D” PDU type, the server was required to first conduct a search through the
registered contents, then upon finding the registered content requested by the name given with
the “D” type, the server will then send back the address and port number of the requested
contents back to the client or peer that requested for it. When the server receives the “S” PDU
type, the server was required to conduct a search through the contents register with the given
name and sends back information stating if the content exists or not back to the client or peer that
requested the search. When the server receives the “T” PDU type, the server was required to de-
register, or delete, all contents requested to be erased by the client or peer. When the server
receives the “O” PDU type, the server was required to send back a list of all the registered
contents back to the client or peer that requested such an item.
The client required for this project was required to have the ability to both be a server and
a client simultaneously. The client is doubled as a server for the client is responsible for their
own data contents and must be able to handle download requests from other peers. The client
must be able to allow the user of the client program to be able to register data contents, de-
register data contents, request for registered contents list, search for requested content, and to
download contents from another peer. When the user of the client program requests for content
registration, the client program must be able to concatenate the “R” PDU type as the header of
the data containing the content name and send the whole data to the server for processing. When
the user requests for a de-register of content, the client program must be able to concatenate the
“T” PDU type to the header of the content name and send the data with the header to the server
to complete the request. When the user requests for the registered contents list, the client
program must be able to concatenate the “O” PDU type to the data to be sent to the server as the
header, then send the data to the server to complete the request given by the user. When the user
uses the search command, the client program must be able to concatenate the “S” PDU type to
the data containing the requested content name as header, then send the data to the server to
determine if the requested content exists or not. When the user requests for a download, the
client program must first send a search request to the server using the search command to
determine the existence of the request contents, then after receiving the address and port number
of the location of the contents back from server, the client program must be able to establish a
connection with the peer that holds the content. After the client establishes the connection with
the peer that holds the requested contents, the client program must then send data with the header
PDU type “D” to begin the download process. The peer then must send back the content data
with the “D” header as well, to specify that the content being sent back is the actual content data
of the requested file.
The way that was used to implement the peer to peer server was to use socket
programming. The type of socket programming used was the TCP type. In the server program,
the server binds a socket to the local host IP address at either a predefined port number or a user
defined port number at the start of the program. The server program used threads to handle all
incoming client connections. The server was set to be able to handle five clients at a time.
5. The client program was programmed to have both the server code and the client code, for
the client needs to have the ability to be both the server and the peer. The peer was programmed
to use processes to handle the different sockets that exist in the peer program. The peer was
programmed to have a process handle the socket bound for server, another process to handle the
connection to the register server, and the last process to handle the socket bound for client to
client communication.
3. Observations and Analysis
From observing the code, the peer to peer server has troubles clearing the buffer used to
transfer the data and command headers. Where the buffer would not clear itself, even though it
was programmed to do so at the end of every command. The problem with the buffer not
clearing was that occasionally, the buffer would save a newline character and not erase it, thus all
new commands that arrives afterwards would be treated as invalid. The way found to bypass this
was to program the client to continuously inquire for a command while the buffer contained a
newline character. Another observation observed was that when the server was programmed
using processes, the content list between each processes was not shared. This was a problem
because when a peer registers a content, it was required for all the peers to be able to see the
registered content on the content list. But since processes do not share any variables or
structures, the content list for all processes were different, thus not shared and this was
absolutely not acceptable. The solution found to fix the unshared contents list was to introduce
threads to the program. Where instead of using processes to handle new connections in the
server, threads were used instead. This was mainly due to the fact that threads share global
variables of the parent, where processes do not.
From analyzing the implemented code, it was found that whenever the client requests for
a connection with the server, the server would accept the request, but due to an unknown error,
the server would reach a segmentation fault and shut down. It was speculated that one of the
variables created was reaching outside its boundaries, but the source has not yet been found. This
problem was introduced when threads were used for the server, thus it was believed that one of
the thread arrays were causing the segmentation fault. After strenuous research, it was found that
the index value used for the thread array was not allocated properly in the format that was
acceptable for the threads to use, thus resulting in the segmentation faults. The way used to fix
this error was to use the malloc() function on the integer used for the index of the threads array.
Another analysis made reviled that the buffer contained an unwanted newline character. This was
easily fixed by making adjustments to the while loop condition that the buffer was located in.
4. Conclusions
The current code now meets the specifications. The peers connected to the register server
are able to register their own files for other peers to download. When the peers request for the
registered contents list, all peers will see the same registered contents list which shows the names
of the contents and which peer registered the content. The peers are also able to download
contents from each other, and each peer is able to de-register the contents the said peer
registered. The client program also contains a quit command where the end user may close the
peer to his or her discretion as specified in the specifications. The server, once run, will stay
running forever until it is force closed, and the server is able to connect to peers, handle the
registration command, handle the de-registration command, and handle the registered contents
6. list command according to specifications. Thus both the client and server codes are working up
to specifications.
7. 5. References
[1] Docs.oracle.com, (2014). Lesson: All About Sockets (The Java™ Tutorials > Custom
Networking). [online] Available at: https://docs.oracle.com/javase/tutorial/networking/sockets/
[Accessed 14 Nov. 2014].
8. 6. Appendix
A. Client and Server Program Demo
first start server
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final$ ./server 20000
then peer1
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer1$ ls
client o_canada
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer1$ cat o_canada
O Canada!
Our home and native land!
True patriot love in all thy sons command.
With glowing hearts we see thee rise,
The True North strong and free!
From far and wide,
O Canada, we stand on guard for thee.
God keep our land glorious and free!
O Canada, we stand on guard for thee.
O Canada, we stand on guard for thee.
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer1$ ./client localhost 20000
Enter name: peer1
Initializing P2P server.
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
9. >r
Enter file name to register: o_canada
File 'o_canada' registered.
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>
then peer 2
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer2$ ls
client
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer2$ ./client localhost 20000
Enter name: peer2
Initializing P2P server.
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>o
List:
o_canada by: peer1
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
10. >d
Enter file name to download: sassadf
File unavailable!
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>d
Enter file name to download: o_canada
100 B of data received
100 B of data received
100 B of data received
23 B of data received
File 'o_canada' downloaded.
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>
back to peer1
>
100 B of data sent
100 B of data sent
100 B of data sent
23 B of data sent
>t
Enter file name to de-register: o_canada
'R' to register file.
11. 'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>
back to peer2
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>o
List:
'R' to register file.
'T' to deregister.
'D' to download.
'O' to list files available.
'Q' to exit.
>q
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer2$ ls
client o_canada
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer2$ cat o_canada
O Canada!
Our home and native land!
True patriot love in all thy sons command.
With glowing hearts we see thee rise,
The True North strong and free!
From far and wide,
O Canada, we stand on guard for thee.
12. God keep our land glorious and free!
O Canada, we stand on guard for thee.
O Canada, we stand on guard for thee.
izb@crunchbang:~/Dropbox/U/COE 768/lab6 File transfer project/project
final/peer2$
13. B. Client Code
/* client.c
COE768 P2P project by Ionathan Zauritz and Yuming Guo */
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <strings.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <unistd.h>
#define SERVER_TCP_PORT 18000 /* default port */
#define BUFLEN 101 /* buffer length */
#define MAX_PEERS 15 //max # of peers to download at the same time.
int clientToServer(int sd);
int download(char contName[], char ip[]);
int upload(int sd);
int getIp();
int p2pServer();
void r(char s[]);
void reaper(int sig);
char name[20];
char localIP[NI_MAXHOST];
int main(int argc, char **argv)
14. {
int n, i, bytes_to_read;
int sd, port;
struct hostent *hp;
struct sockaddr_in server;
char *host, *bp, rbuf[BUFLEN], sbuf[BUFLEN];
switch(argc){
case 2:
host = argv[1];
port = SERVER_TCP_PORT;
break;
case 3:
host = argv[1];
port = atoi(argv[2]);
break;
default:
fprintf(stderr, "Usage: %s host [port]n", argv[0]);
exit(1);
}
/* Create a stream socket */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "Can't creat a socketn");
exit(1);
}
bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(port);
if (hp = gethostbyname(host))
15. bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
else if ( inet_aton(host, (struct in_addr *) &server.sin_addr) ){
fprintf(stderr, "Can't get server's addressn");
exit(1);
}
/* Connecting to the server */
if (connect(sd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "Can't connect n");
exit(1);
}
printf("Enter name: "); //as user for name
r(name); //reads name
for(i=0; i<20; i++){
if(name[i] == 'n'){
name[i] = '0';
break;
}
}
int pid = fork();
if(pid == 0){ //child
p2pServer();
}
else{ //parent
usleep(250000); //wait 0.25 seconds for p2p to initialize
clientToServer(sd);
close(sd);
}
16. return(0);
}
int clientToServer(int sd)//function for client to server
{
char sbuf[BUFLEN];
char option, opStr[20];
char contName[20];
int n, i;
while(1){
printf("nt'R' to register file.nt'T' to deregister.nt'D' to
download.nt'O' to list files available.nt'Q' to exit.nnt>");
do{
r(opStr);
option = opStr[0];
} while (option == 'n' || option == '0');
if(option == 'R' || option == 'r'){
do{
printf("Enter file name to register: ");
r(contName);
} while(contName[0] == 'n' || contName[0] == '0');
for(i=0; i<20; i++){
if(contName[i] == 'n'){
contName[i] = '0';
}
}
//check if file exists
FILE *p;
p=fopen(contName, "r");
27. return(0);
}
int getIp(){
struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
family = ifa->ifa_addr->sa_family;
if (family == AF_INET) {
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),
host, NI_MAXHOST, NULL, 0,
NI_NUMERICHOST);
if (s != 0) {
printf("getnameinfo() failed: %sn",
gai_strerror(s));
exit(EXIT_FAILURE);
}
// printf("<Interface>: %s t <Address> %sn", ifa-
>ifa_name, host); //this prints local ip addresses
}
}
int i;
for (i=0; i<NI_MAXHOST; i++){
28. localIP[i] = host[i];
}
return 0;
}
/* I created this function because I was having problems with the input
buffer */
void r(char s[]){
int s_size = 20;
char tmp = '0';
int i = 0;
while(tmp != 'n'){
tmp = getchar();
s[i] = tmp;
if(s[i] == 'n' || i >= s_size-1){
s[i] = '0';
return;
}
i++;
}
}
/* reaper */
void reaper(int sig)
{
int status;
while(wait3(&status, WNOHANG, (struct rusage *)0) >= 0);
}
29. C. Server Code
/* server.c - compile with -lpthread
COE768 P2P project by Ionathan Zauritz and Yuming Guo */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <strings.h>
#define SERVER_TCP_PORT 18000 /* default port */
#define BUFLEN 101 /* buffer length */
#define MAX_CONT 100 //max # of contents
#define MAX_PEERS 10 //max # of peers
void *connection(void *sdd);
void clearContent();
void reaper(int);
pthread_mutex_t mutex;// = PTHREAD_MUTEX_INITIALIZER;
char content[MAX_CONT][3][20]; //list of content 0-content name, 1-
address, 2-peer name. 20 char each.
int sdIndex[MAX_CONT]; //stores sd for connections
int main(int argc, char **argv)
{
30. int sd, new_sd, client_len, port;
struct sockaddr_in server, client;
switch(argc){
case 1:
port = SERVER_TCP_PORT;
break;
case 2:
port = atoi(argv[1]);
break;
default:
fprintf(stderr, "Usage: %d [port]n", argv[0]);
exit(1);
}
/* Create a stream socket */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "Can't creat a socketn");
exit(1);
}
/* Bind an address to the socket */
bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sd, (struct sockaddr *)&server, sizeof(server)) == -1){
fprintf(stderr, "Can't bind name to socketn");
exit(1);
}
31. /* queue up to MAX_PEERS connect requests */
listen(sd, MAX_PEERS);
int iret[MAX_PEERS];
pthread_t thread[MAX_PEERS];
(void) signal(SIGCHLD, reaper);
clearContent(); //this clears content list
int i = 0;
while(1) {
if(i>=MAX_PEERS){break;}
client_len = sizeof(client);
new_sd = accept(sd, (struct sockaddr *)&client, &client_len);
if(new_sd < 0){
fprintf(stderr, "Can't accept client n");
exit(1);
}
//this fixes segmentation fault caused by passing "(void*) new_sd"
to thread
int* new_sdp = malloc( sizeof( *new_sdp )) ;
*new_sdp = new_sd ;
//create thread
iret[i] = pthread_create(&thread[i], NULL, connection, new_sdp);
if(iret[i] != 0){
printf("thread[%d] not generated!n", i);
32. }
i++;
}
}
void *connection(void *sdd)
{
int sd =* (int *) sdd;
char *bp, buf[BUFLEN];
int i, n, bytes_to_read;
char type; //stores PDU type
char data[BUFLEN-1]; //stores data
while(1){
//clear buffer
for(i=0; i<BUFLEN; i++){
buf[i] = '0';
}
n = read(sd, buf, BUFLEN); //receive request
if(n <= 0){break;} //exits loop if n == 0(disconnect), or n <
0(error)
type = buf[0]; //save PDU type requested
for(i=0; i<BUFLEN-1; i++){ //save data
data[i] = buf[i+1];
}
// options
//Content Registration
pthread_mutex_lock(&mutex);
33. if(type == 'R' || type == 'r'){
char tmpName[20];
int z = 0;
for(i=0; i<20; i++){
tmpName[i] = data[i+20];
}
for(i=0; i<MAX_CONT; i++){
if(strcmp(tmpName, content[i][0]) == 0){
z =1;
}
}
if(z == 0){
int x = -1; //for content index available
for(i=0; i<MAX_CONT; i++){ //find content index available
if(content[i][0][0] == '0'){
x = i;
break;
}
}
for(i=0; i<20; i++){ //load content list
if(x == -1){break;}//if list is full, exit loop
content[x][2][i] = data[i]; //name of peer
content[x][0][i] = data[i+20]; //name of content
content[x][1][i] = data[i+40]; //address
}
sdIndex[x] = sd;
//A/E
if(x == -1){ //content list is full
printf("Content list is full!n");
write(sd, "EContent list is full!", 23); //send E
}
34. else{ //if no error
write(sd, "A", 2); //send A
}
}
else{
printf("A file with this name is already registered!n");
write(sd, "EA file with this name is already registered!",
46); //send E
}
}
//Content Download
else if(type == 'D' || type == 'd'){
printf("Server doesn't send files!n");
continue; //jump to start of loop
}
//Search Content Server
else if(type == 'S' || type == 's'){
int ind = -1; //stores index
for(i=0; i<MAX_CONT; i++){ //to find index
if(strcmp(content[i][0], data) == 0){
ind = i;
break;
}
}
if(ind == -1){ //not found
printf("File requested not in list!n");
write(sd, "EFile requested not in list!", 29); //send E
}
else{
//clear buffer
for(i=0; i<BUFLEN; i++){
36. strcat(buf, "tby: ");
strcat(buf, content[i][2]);
if(i >= MAX_CONT-1){break;} // to skip the last 'n'
strcat(buf,"n");
}
write(sd, buf, BUFLEN);
}
//Acknowledgement
else if(type == 'A' || type == 'a'){
printf("Server should not receive acknowledgement!n");
}
//Error
else if(type == 'E' || type == 'e'){
printf("Error received!n");
}
else{
printf("Invalid PDU type!n");
write(sd, "EInvalid PDU type!", 19); //send E
}
pthread_mutex_unlock(&mutex);
} // loop ends here
pthread_mutex_lock(&mutex);
close(sd);
// this deletes from list every index that that was registered by this
sd
for(i=0; i<MAX_CONT; i++){
if(sdIndex[i] == sd){
content[i][0][0] = '0';