I need to RESUME the upload files from client to server after connection loss using tcp/ip socket in c program I'm not sure how to go about this.(Do not want the originally sent data to be resent) I would really appreciate it if anybody could give me suggestions as to how i should implement the file resume functionality in socket server client?
There is no such mechanism built into TCP or UDP. You need to make your server and client agree on specific terms. If you are thinking there is no such protocol, and you want to start it from scratch, then drop the idea. You can choose HTTP
, which provides support for Partial-Content
. For learning purposes, you can definitely try writing one on your own. Below I implemented one such bare minimal protocol:
Rules:
1) The client sends the file offset encoded as a 32-bit unsigned int
in network byte order (At the start of the download, 0 is sent).
2) The server reads the offset and converts to host byte order and sends the data from that particular offset.
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
int main()
{
/* Initialize the file */
int fd = open("download", O_RDWR | O_CREAT);
struct stat statbuf;
fstat(fd, &statbuf);
uint32_t cli_fsize = statbuf.st_size;
/* Initialize the connection with the server */
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
servaddr.sin_port = htons(55555);
if (connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) != 0) {
exit(1);
}
/* Write the file offset to the server */
uint32_t n_cli_fsize = htonl(cli_fsize);
int off_write = 0;
for ( ; ; ) {
int wr_return = write(sockfd, (void*) &n_cli_fsize + off_write, sizeof(n_cli_fsize) - off_write);
if (wr_return <= 0)
exit(1);
off_write = off_write + wr_return;
if (off_write == sizeof(n_cli_fsize))
break;
}
/* Read the data from the socket and write to the file */
char fbuf[100];
for ( ; ; ) {
int rd_return = read(sockfd, fbuf, sizeof(fbuf));
if (rd_return < 0)
exit(1);
else if (rd_return == 0)
break;
int wr_status = 0;
for ( ; ; ) {
int wr_return = pwrite(fd, fbuf + wr_status, rd_return - wr_status, cli_fsize);
if (wr_return < 0)
exit(1);
wr_status = wr_status + wr_return;
cli_fsize = cli_fsize + wr_return;
if (wr_status == rd_return)
break;
}
}
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
/* File initializations */
FILE* fp = fopen("data.bin", "r+");
if (fp == NULL)
exit(1);
struct stat statbuf;
if (fstat(fileno(fp), &statbuf) != 0)
exit(0);
uint32_t fsize = statbuf.st_size;
/* Initialize the server */
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), clifd, addr_len;
struct sockaddr_in servaddr, cliaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(55555);
if (bind(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) != 0) {
exit(1);
}
listen(sockfd, 5);
for ( ; ; ) {
addr_len = sizeof(cliaddr);
if ((clifd = accept(sockfd, (struct sockaddr*) &cliaddr, &addr_len)) < 0) {
goto next_cli;
}
/* Read the 32 bit offset */
uint32_t n_cli_fsize = 0;
int off_read = 0;
for ( ; ; ) {
int rd_status = read(clifd, (void*) &n_cli_fsize, sizeof(n_cli_fsize) - off_read);
if (rd_status == -1)
goto next_cli;
else if (rd_status == 0 && off_read != sizeof(n_cli_fsize))
goto next_cli;
off_read = off_read + rd_status;
if (off_read == sizeof(n_cli_fsize))
break;
}
uint32_t cli_fsize = ntohl(n_cli_fsize);
/* Read from the file and write to the socket */
char fbuf[100];
for ( ; ; ) {
int rd_return = pread(fileno(fp), fbuf, sizeof(fbuf), cli_fsize);
if (rd_return <= 0)
goto next_cli;
cli_fsize = cli_fsize + rd_return;
int wr_status = 0;
for ( ; ; ) {
int wr_return = write(clifd, fbuf + wr_status, rd_return - wr_status);
if (wr_return <= 0)
goto next_cli;
wr_status = wr_status + wr_return;
if (wr_status == rd_return)
break;
}
}
next_cli:
if (clifd >= 0)
close(clifd);
}
return 0;
}
data.bin
$ du -h data.bin && md5sum data.bin
149M data.bin
6f188c0f60376fed5cfa00f55681b436 data.bin
Note: The server is always running in all the terminal sessions.
Terminal Session 1 (The client is allowed to completely download the file):
$ du -h download
du: cannot access 'download': No such file or directory
$ ./client
$ du -h download && md5sum download
149M download
6f188c0f60376fed5cfa00f55681b436 download
Terminal Session 2 (The client is interrupted in the midst of the download and resumed back):
$ du -h download
du: cannot access 'download': No such file or directory
$ ./client
^C
$ du -h download && md5sum download
80M download
0f77e56c7a512abc4ccafdb890f451a1 download
$ ./client
$ du -h download && md5sum download
149M download
6f188c0f60376fed5cfa00f55681b436 download
Terminal Session 3 (Running the client on a fully fetched file):
$ du -h download && md5sum download
149M download
6f188c0f60376fed5cfa00f55681b436 download
$ ./client
$ du -h download && md5sum download
149M download
6f188c0f60376fed5cfa00f55681b436 download
You can tweak around this base implementation, and add more features.