I have an application for transferring files from clients to a server. A client opens, transfers a file to the server and closes. The server is open and may receive multiple files. Also, when transferring a file, a loader bar is created on both server (the server must have some way to display multiple loaders simultaneously) and client. Also, every file transfer is handled in a separate thread on server. This is a structure that will be used in server function:
struct fileWriteArgs{
int childNumber;
int sockfd;
};
Here is the server function (the main function just parses arguments and calls this):
int serverFileTransfer(int port, char* filename){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd<0)
errorOpeningSocket();
struct sockaddr_in serv_addr, cli_addr;
memset((char*) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
errorOnBinding();
listen(sockfd, 5);
socklen_t cilen = sizeof(cli_addr);
pthread_t newThread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int noOfChilds = 0;
while(1){
int newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cilen);
noOfChilds++;
struct fileWriteArgs args;
args.sockfd = newsockfd;
args.childNumber = noOfChilds;
if(newsockfd < 0)
errorOnAccept();
pthread_create(&newThread, &attr, &fileWrite, &args);
}
pthread_attr_destroy(&attr);
return 0;
}
Basically, it opens a socket and creates threads for file accept (note that I am using a Linux operating system). Also, it calls some error functions that I think are pretty explicit (they just write a message and call exit()
).
Here are the loader
and fileWrite
functions:
void loader(int val, int max, int size, int row){
static int lastPrinted = 0;
int percent = (100.*val)/max;
int count = ((float)percent * size) / 100;
if(count == lastPrinted)
return;
if(row > 0){
printf("\033[s"); //save current position
printf("\033[%dB\n", row);
}
lastPrinted = count;
printf("\r[");
int i;
for(i = 0; i < count; i++)
printf("#");
for(; i < size; i++)
printf(".");
printf("] %d%%", percent);
if(row > 0)
printf("\033[u");
fflush(stdout);
}
static pthread_mutex_t STDOUT_mutex = PTHREAD_MUTEX_INITIALIZER;
void* fileWrite(void *fd){
struct fileWriteArgs *p = (struct fileWriteArgs*)fd;
int sockfd = p->sockfd;
int childNumber = p->childNumber;
char sz[32];
read(sockfd, sz, 32);
int fileSize = atoi(sz);
char fileNameSize;
int readed = read(sockfd, &fileNameSize, 1);
if(readed < 0)
errorReadingFromSocket();
else if(fileNameSize == 0)
errorBadFileName();
char filename[256];
readed = read(sockfd, filename, fileNameSize);
if(readed < 0)
errorReadingFromSocket();
FILE *f = fopen(filename, "wb");
if(f == NULL){
errorOpeningFile();
}
char buffer[BUFFER_SIZE];
int n = 0;
while(n < fileSize){
readed = read(sockfd, buffer, BUFFER_SIZE);
if(readed < 0)
errorReadingFromSocket();
write(fileno(f), buffer, readed);
n += readed;
pthread_mutex_lock(&STDOUT_mutex);
loader(n, fileSize, LOADER_LENGTH, childNumber);
pthread_mutex_unlock(&STDOUT_mutex);
}
fclose(f);
return NULL;
}
This is the client function:
int clientFileTransfer(char *ip, int port, char* filename){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
errorOpeningSocket();
struct hostent* server = gethostbyname(ip);
if(server == NULL)
noServerFound();
struct sockaddr_in serv_addr;
memset((char*)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
bcopy((char*) server -> h_addr, (char*) &serv_addr.sin_addr.s_addr, server -> h_length);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
errorConnecting();
fileRead(filename, sockfd);
return 0;
}
And the fileRead
function:
void fileRead(char *filename, int sockfd){
FILE *f = fopen(filename, "rb");
if(f == NULL){
errorOpeningFile();
return;
}
fseek(f, 0, SEEK_END);
int fileSize = ftell(f);
fseek(f, 0, SEEK_SET);
char size[32];
sprintf(size, "%d", fileSize);
write(sockfd, size, 32);
char *name = strrchr(filename, '/');
if(name == NULL)
name = filename;
else
name++;
char fileNameSize = strlen(name)+1;
write(sockfd, &fileNameSize, 1); //WARNING! it works only for files
//with at most 255 characters
write(sockfd, name, strlen(name)+1);
int n = 0; //total bytes read/written
char buffer[BUFFER_SIZE];
while(!feof(f)){
int read = fread(buffer, 1, BUFFER_SIZE, f);
if(read < 0)
errorReadingFromFile();
write(sockfd, buffer, read);
n+=read;
loader(n, fileSize, LOADER_LENGTH, 0);
}
fclose(f);
}
The clientFileTransfer
and fileRead
functions are used by the client in a similar manner to serverFileTransfer
and fileWrite
, that are used by server.
As the code is pretty large, I would like some parts to be reviewed especially, as I feel there is something wrong there. My primary concern is the loader function. It has a static variable that keeps the value that was written last time when the function was called (to avoid printing the same thing all over again). I do not know if this is a good thing. Also this is my first multithreaded application, so I would like some remarks about the way that multithreading is implemented here.
Of course, I would really appreciate if I get also a review of the other parts of this application and remarks about it (what I have done right and where something is wrong).