File Transfer Using BSD Socket
File Transfer Using BSD Socket
I have discussed basic socket operation by sending a simple message in the post : https://www.absouls.com/2021/02/bsd-socket-programming.html .
Now, with that socket, I am going to send a single file from the client to the server. Primarily, I am gonna send a text file by reading each line from that file to make it simple. Later, we will discuss sending multiple files or other types. Here are the basic steps:
On the server side,
Create a server socket.
Bind socket with server address.
Open a listener to accept the client connection request and wait for an incoming request
Accept client connection requests.
Now the connection is established. Implement logic for handling incoming files.
Receive a file name from the client and create an empty file with that name.
Start receiving file data line by line and write to the file.
On client side,
Create a client socket.
Connect with the server socket where there is a listener.
After the connection is successful, send the File Name to the server.
Send data line by line using the socket file descriptor.
Steps in the Server Side:
int sockfd = socket(domain_name, com_type, protocol)
sockfd: socket descriptor, an integer (File Descriptor)
domain_name: integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)
com_type:
1. SOCK_STREAM: TCP(reliable, connection oriented)
2. SOCK_DGRAM: UDP(unreliable, connectionless)
protocol: Protocol value for Internet Protocol(IP), which is 0. This is the same number which appears on protocol field in the IP header of a packet
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("Unable to create Socket");
exit(1);
}
Bind
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
After creation of the socket, bind() function binds the socket to the address and port number specified in nwServerAddr(custom data structure).
// 4. Bind to Server Address
if (bind(sockfd, (const struct sockaddr *) &nwServerAddr, sizeof(nwServerAddr)) == -1) {
perror("Unable to Bind");
exit(1);
}
Listen
int listen(int sockfd, int listen_port);
It puts the server socket in a passive mode, where it waits for the client to approach the server to make a connection. The listen_port, defines the maximum length to which the queue of pending connections for sockfd may grow.
// 5. Listen Socket for incoming File
if (listen(sockfd, LISTEN_PORT) == -1) {
perror("Error in Socket Listening");
exit(1);
}
Accept
int new_fd = accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.
// 6. Accept connection for concurrency number
socklen_t adlen = sizeof(nwClientAddr);
int cfd = accept(sockfd, (struct sockaddr *) &nwClientAddr, &adlen);
if (cfd == -1) {
perror("Error in Accepting Connection for ");
exit(1);
}
At this point, the connection is established between the client and server, and they are ready to transfer data.
Receive (File name & data) :
int recv(sockfd, buf, len, 0);
sockfd : Socket file descriptor which already connected with server listen port.
buf : character buffer which is suppose to send to server
len : buffer length
1. At first, we will receive the file name.
// 7. Receive incoming file from client
char filename[MAX_NAME] = {0};
if (recv(cfd, filename, MAX_NAME,0) == -1) {
perror("Unable to receive file due to connection or file error");
exit(1);
}
2. After receiving the file name, we will create a file pointer and create an empty file with same name.
3. Open the file with "wb" mode. Receive the file data from client and write into the file
void fileWriter(char *fileName, int sockfd, FILE *fp) {
// 10.1 Declare data length
int n;
// 10.2 Declare and Initialize buffer size
printf("%s file Receiving is started in Connection ID: %d\n", fileName, sockfd);
// 10.3 Receive data from socket and save into buffer
char buff[BUFFER_SIZE] = {0};
while(1) {
n = recv(sockfd, buff, BUFFER_SIZE,0);
if (n == -1) {
perror("Error in Receiving File");
exit(1);
}
if(n <= 0) {
break;
}
// 10.4 Read File Data from buffer and Write into file
if (fwrite(buff, sizeof(char), n, fp) != n) {
perror("Error in Writing File");
exit(1);
}
// 10.5 Allocate buffer
memset(buff, '\0', BUFFER_SIZE);
}
fclose(fp);
}
Steps in the Client Side:
Create Socket
int sockfd = socket(domain_name, com_type, protocol)
sockfd: socket descriptor, an integer (File Descriptor)
domain_name: integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)
com_type:
1. SOCK_STREAM: TCP(reliable, connection oriented)
2. SOCK_DGRAM: UDP(unreliable, connectionless)
protocol: Protocol value for Internet Protocol(IP), which is 0. This is the same number which appears on protocol field in the IP header of a packet.
// 2. Create TCP Socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Unable to create Socket");
exit(1);
}
Connect
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr. Server’s address and port is specified in addr.
// 6. Client will connect after server bind
if (connect(sockfd, (const struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
perror("Unable to Connect");
exit(1);
}
Send :
int send(sockfd, buf, len, 0);
sockfd : Socket file descriptor which is already connected with the server listen port.
buf : character buffer which is supposed to send to server
len : buffer length
1. At first, we will send the file name just like the opposite of server-side
printf("%s file Sending is started \n",fileName);
if (send(sockfd, fileName, MAX_NAME, 0) == -1) {
perror("Unable to send Filename");
exit(1);
}
// 12. Create File pointers
FILE *filePointer = fopen(fileName, "rb");
if (filePointer == NULL) {
perror("Unable to open the file");
exit(1);
}
2. After sending the file name, we will open the file with "rb" mode
3. Send the file data from client.
void fileSender(FILE *filePointer, int sockfd) {
// 13.2 Declare and Initalize the buffer for a single file
char lineBuffer[BUFFER_SIZE] = {0};
int maxLen = 0;
// 13.3 Read the data from file in a loop
while ((maxLen = fread(lineBuffer, sizeof(char), BUFFER_SIZE, filePointer)) > 0) {
if (maxLen != BUFFER_SIZE && ferror(filePointer)) {
perror("Unable to read file");
exit(1);
}
// 13.4 Send each line from buffer through TCP socket
if (send(sockfd, lineBuffer, maxLen,0) == -1) {
perror("Unable to send file");
exit(1);
}
memset(lineBuffer, '\0', BUFFER_SIZE);
}
fclose(filePointer);
}
Prepare
$ git clone https://github.com/arupcsedu/BSDSocketProgramming.git
$ cd BSDSocketProgramming
Build and Run Server
$ cd SockSFileTransfer/Server
$ g++ Server.cpp -o Server
$ ./Server
Build and Run Client
$ cd ..
$ cd Client
$ dd if=/dev/zero of=s1 bs=10MB count=1 //This is for creating 10MB file
$ g++ Client.cpp -o Client
$ ./Client s1 127.0.0.1