Concurrent File Transfer using BSD Socket with pThread 

History

I have discussed multiple file transfers using a BSD socket in that post: https://www.arupsarker.com/blog/multiple-file-transfer-using-bsd-socket 

On the server side, multiple connections are established for each file. accept function runs in an infinite while loop. Whenever a new connection comes from the client, it will be handled accordingly. But, if the client creates multiple connections concurrently, a server is unable to handle them.



In this article, I will explain, how concurrency works and how we can implement it.



Here is the basic step: 

On the server side,

On the client side,


Server-side Changes:

We are creating threads based on the number of connections requested from the clients. After creating threads, we will use those to receive the file name and its contents.


int *new_sock;

pthread_t tid[60];

int idx = 0;

while(1) {


// 7. Accept connection for creating multi-thread

socklen_t addrlen = sizeof(nwClientAddr);

int connfd = accept(sockfd, (struct sockaddr *) &nwClientAddr, &addrlen);

if (connfd == -1) {

perror("Error in Accepting Connection");

exit(1);

}


new_sock = (int*)malloc(1);

*new_sock = connfd;

//printf("Connection ID: %d\n", *new_sock);

// 8. Multi-Thread creation

if(pthread_create(&tid[idx], NULL, socketThreadFT, (void*)new_sock) < 0 ) {

printf("Failed to create thread\n");

exit(1);

}


idx++;

}

In the sockThreadFT callback functions, we have implemented all the logic of receiving files.

void* socketThreadFT(void *arg)

{

int connfd = *((int *)arg);

unsigned int checkSum = 0;


pthread_mutex_lock(&lock);

// 8. Receive incoming file from client

char filename[MAX_NAME] = {0};


if (recv(connfd, filename, MAX_NAME,0) == -1) {

perror("Unable to receive file due to connection or file error");

exit(1);

}


int fileLength = strlen(filename);

pthread_mutex_unlock(&lock);

sleep(1);


if(fileLength > 0) {

// 9. After receiving from client, create file to save data


//printf("File Name : %s\n", filename);

FILE *fp = fopen(filename, "wb");

if (fp == NULL) {

perror("Unable to create File pointer");

exit(1);

}


// 10. Start receiving file Data and print in console

char addr[INET_ADDRSTRLEN];


// 11. Write the data into file.


checkSum = fileWriter(filename, connfd, fp);


// 12. Print success message into console

time_t t = time(NULL);

struct tm *tm = localtime(&t);

char s[64];

assert(strftime(s, sizeof(s), "%c", tm));


printf("%s is received with checkSum %d at :  %s\n",filename, checkSum,s);


}


close(connfd);        

pthread_exit(NULL);


}


Client Side changes:

On the client side, 

1. We have to read each file from the directory.

2. We are creating threads based on the number of concurrency values from user inputs.

3. We will send all the files by using those threads. Let's say, we have 4 concurrent threads and 10 files to send from a directory. Then we will send 4 files in parallel with 4 concurrent threads at first.

4. When one file is finished to send, then the free worker thread will pick another file to send.



int j = 0;

while(j != val) {


// 11. Create multi-thread for each files

if(pthread_create(&tid[j++], NULL, threadForFileTransfer, (void*)sParams) != 0 ) {

printf("Failed to create thread\n");

exit(1);

}

printf("Thread ID : %d\n", j);

}


// 12. Execute Thread

j = 0;

while(j != val)

{

pthread_join(tid[j++],NULL);

printf("pthread join %d:\n",j);

}


Here is the callback function threadForFileTransfer. This will be used to read file name and file data to server.


void *threadForFileTransfer(void *vargp) 

SP *sockParamList = (SP *) vargp;


if(sockParamList == NULL) {

printf("Socket Param Error");

exit(1);

}


for(int i = 0; i < sockParamList->fileCount; i++) {


if(!sockParamList->threadParamList[i]->isUsed) {

pthread_mutex_lock(&lock);

sockParamList->threadParamList[i]->isUsed = true;

pthread_mutex_unlock(&lock);

//sleep(1);

// 2. Create TCP Socket

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd < 0) {

perror("Unable to create Socket");

exit(1);

}


// 3. Setup information about server

struct sockaddr_in serverAddress;

memset(&serverAddress, 0, sizeof(serverAddress));


// 4. IP address should be IPv4

serverAddress.sin_family = AF_INET;

serverAddress.sin_port = htons(SERVER_PORT);


// 5. Check IP adddres and convert it with inet_pton

if (inet_pton(AF_INET, sockParamList->threadParamList[0]->serverIP, &serverAddress.sin_addr) < 0) {

perror("Conversion Error in IP Address");

exit(1);

}


// 6. Client will connect after server bind

if (connect(sockfd, (const struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {

perror("Unable to Connect");

exit(1);

}

// 11. Send file name from buffer data through socket so that server can create file with same name.

time_t t = time(NULL);

struct tm *tm = localtime(&t);

char s[64];

assert(strftime(s, sizeof(s), "%c", tm));


printf("%s file Sending is started at : %s\n",sockParamList->threadParamList[i]->fileName,s);


if (send(sockfd, sockParamList->threadParamList[i]->fileName, MAX_NAME, 0) == -1) {

perror("Unable to send Filename");

exit(1);

}


// 12. Create File pointers

FILE *filePointer = fopen(sockParamList->threadParamList[i]->filePath, "rb");

if (filePointer == NULL) {

perror("Unable to open the file");

exit(1);

}

unsigned int checkSum = 0;

// 13. Send file through socket.

checkSum = fileSender(filePointer, sockfd);

struct tm *tm1 = localtime(&t);

char endTime[64];

assert(strftime(endTime, sizeof(endTime), "%c", tm1));

printf("%s file is sent successfully with checkSum : %d at %s\n",sockParamList->threadParamList[i]->fileName, checkSum, endTime);

close(sockfd);


}

}


// 18. Close socket after sending single file.

pthread_exit(NULL);

}


Prepare

$ git clone https://github.com/arupcsedu/SocketFileTransfer.git

$ cd SocketFileTransfer

Build and Run Server

$ cd NWServer

$ g++ Server.cpp -o Server -pthread

$ ./Server 

Build and Run Client

$ cd ..
$ cd NWClient
$ dd if=/dev/zero of=res/s1 bs=10MB count=1 //This is for creating 10MB file in res directory. Execute multiple times for creating multiple files.
$ g++ Client.cpp -o Client -pthread
$ ./Client res 127.0.0.1 4

Source code URL:

https://github.com/arupcsedu/SocketFileTransfer