for some reason, it's not transferring content over. I had strlen(file) in the send() function and it was working for txt file. It won't work with bin files though, so I changed it to bytes_read. Now nothing's transferring properly. I'm getting blank files on the client side. Also, in debugging with print statements, I noticed the bin file is skipping the read() while loop. Any ideas?
Here's my client:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "packetErrorSend.h"
#include <sys/time.h>
#define MAX_LINE 1000
ssize_t recvx(int sockfd, void *buf, size_t len) {
int var = recv(sockfd, buf, len, 0);
if(var != -1)
{
return var;
} else {
printf("%s \n","Did not receive.");
exit(1);
}
}
int main(int argc, char *argv[])
{
char buf[MAX_LINE];
struct addrinfo hints;
struct addrinfo *rp, *result;
int bytes_received = 1;
int s;
char *server;
char *port;
char *file;
int fd = -1; //file descriptor
int bytes_written = 1;
if (argc==4)
{
server = argv[1];
port = argv[2];
file = argv[3];
}
else
{
fprintf(stderr, "invalid # of arguments\n");
exit(1);
}
/* Translate host name into peer's IP address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
if ((s = getaddrinfo(server, port, &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to connect */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1)
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-client: connect");
exit(1);
}
freeaddrinfo(result);
/*send lines of text */
send(s, file, sizeof(file)+30, 0);
int status = recv(s,buf,1,0);
if(status == 0 || buf[0] == 'e')
{
fprintf(stderr, "Server Error: unable to access file %s \n", file);
close(s);
exit(0);
}
if(buf[0] == 's')
{
while(bytes_received != 0)
{
bytes_received = recvx(s, buf, 20);
if(bytes_received == -1)
{
fprintf(stderr, "Client Error: Error receiving file \n");
exit(0);
} else {
if(fd == -1)
{
fd = open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1)
{
fprintf(stderr,"Client Error: Open failed \n");
break;
}
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
break;
}
} else {
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
break;
}
}
}
}
}
if(close(fd) != 0)
{
printf("%s \n", "Client Error: File did not close successfully");
exit(0);
}
close(s);
return 0;
}
Server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/sendfile.h>
#include "packetErrorSend.h"
#define SERVER_PORT "5432"
#define MAX_LINE 2000
#define MAX_PENDING 5
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *rp, *result;
char file[MAX_LINE];
int s, new_s;
int bytes_transferred = 0;
int fd; //file descriptor
char status[1];
int bytes_read = 0;
/* Build address data structure */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
/* Get local address info */
if ((s = getaddrinfo(NULL, argv[1], &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to perform passive open */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (!bind(s, rp->ai_addr, rp->ai_addrlen))
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-server: bind");
exit(1);
}
if (listen(s, MAX_PENDING) == -1)
{
perror("stream-talk-server: listen");
close(s);
exit(1);
}
freeaddrinfo(result);
/* Wait for connection, then receive and print text */
while(1)
{
for(int i = 0; i < sizeof(file); i++)
{
file[i] = '\0';
}
if ((new_s = accept(s, rp->ai_addr, &(rp->ai_addrlen))) < 0)
{
perror("stream-talk-server: accept");
close(s);
exit(0);
}
while(bytes_transferred == recv(new_s,file,sizeof(file),0))
{
if(bytes_transferred == -1)
{
close(new_s);
exit(0);
}
}
fd =open(file,O_RDONLY);
if(fd < 0)
{
status[0] = 'e';
send(new_s,status,sizeof(status),0);
close(new_s);
exit(0);
}
else
{
status[0] = 's';
send(new_s,status,sizeof(status),0);
while(bytes_read == read(fd,file,sizeof(file)) > 0)
{
if(bytes_read < 0)
{
close(new_s);
exit(0);
}
}
while((bytes_transferred = send(new_s,file,bytes_read,0)) > 0)
{
if(bytes_transferred == -1)
{
close(new_s);
exit(0);
}
}
if(close(fd) != 0)
{
close(new_s);
exit(0);
}
else{
break;
}
}
}
close(new_s);
return 0;
}
Usual copy loop problem:
while((bytes_transferred == send(new_s,file,sizeof(file),0)) > 0)
should be
while((bytes_transferred = send(new_s,file,bytes_read,0)) > 0)
Detailed commentary on your code:
send(s, file, sizeof(file)+30, 0);
should be
send(s, file, strlen(file)+1, 0);
Then:
while(bytes_received != 0)
{
bytes_received = recvx(s, buf, 20);
if(bytes_received == -1)
{
fprintf(stderr, "Client Error: Error receiving file \n");
exit(0);
} else {
if(fd == -1)
{
fd = open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1)
{
fprintf(stderr,"Client Error: Open failed \n");
break;
}
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
break;
}
} else {
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
break;
}
}
}
}
A much simpler way to write all this would be:
fd = open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1)
{
fprintf(stderr,"Client Error: Open failed %s\n", strerror(errno));
break;
}
while((bytes_received = recvx(s, buf, sizeof buf)) > 0)
{
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s %s\n", "Client Error: Write error", strerr(errno));
break;
}
}
}
}
if(bytes_received == -1)
{
fprintf(stderr, "Client Error: Error receiving file %s\n", strerr(errno));
exit(0);
}
In the server, your code inside the accept loop is totally bizarre. It can be reduced to:
// receive a target file name
if ((bytes_transferred = recv(new_s,file,sizeof(file),0) > 0)
{
fd = open(file,O_RDONLY);
if(fd < 0)
{
perror("open");
status[0] = 'e';
send(new_s,status,1,0);
close(new_s);
exit(0);
}
status[0] = 's';
send(new_s,status,1,0);
while ((bytes_read = read(fd, file, sizeof file)) > 0)
{
if (send(new_s,file,bytes_read,0) < 0)
{
perror("send");
break;
}
}
if (bytes_transferred == 0)
{
break;
}
else if(bytes_transferred == -1)
{
perror("read");
close(new_s);
exit(0);
}
}
close(new_s);
E&OE
Note that you don't have to length-check send() in blocking mode. Posix requires that it blocks until all data has been transferred.
In your code, sizeof(file) is always MAX_LINE, hence the garbage at the end. Because sizeof cares nothing about what you put into variables; it only cares about their in-memory size:
#define MAX_LINE 2000
// ...
char file[MAX_LINE]; // sizeof(file) == 2000
Also, this line is writing beyond the length of the file array, and probably zeroing out some other variable that you care about:
file[sizeof(file)]='\0'; // very bad idea
To fix your initial problem, measure what your are sending (use strlen, or even better, strnlen) - and send only those bytes, including the terminating '\0' , instead of always sending MAX_LEN.
EDIT: after multiple downvotes, apparently due to not warning against the use of strlen & co. when used on non-textual inputs, I will now warn against the use of strlen for non-textual inputs:
do not use strlen unless your input is 100% text.
do not use it anyway, even with text: strnlen is safer, as it will never overflow the buffer (as long as the maximum-length parameter is correctly set):
char line[MAX_LINE];
// ... read into line somehow
int line_length = strnlen(line, MAX_LINE-1);
// make sure that it is 0-terminated
line[line_length] = 0;
To send binary data, you need to measure it first. File sizes can be measured using fseek and ftell. Do not try to use string functions (such as strnlen) or print (printf) binary data, as many bytes will map to control characters and mess up the display. If you need to print binary data, use code similar to this one.
Related
Im trying to make a distributed client / server application for file sharing. I firstly made a LIST_FILES function which reads and prints all the files of your directory, but Im having trouble deleting and renaming .txt files. If I manually use the name of the .txt in the functions, it works for both, but I dont know how to properly read from the server which file I want to use. Im using cygwin, C code and Windows.
Example: ./server.exe --> ./client.exe 127.0.0.1 DELETE_FILE test.txt or MOVE_FILE test.txt newtest.txt
SERVER
#include <stdio.h>
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h> // Close sockets
#include <dirent.h>
#define MAXDATASIZE 5000
#define _BUFFER_SIZE 2000
#define ECHO_PORT 5675
#define FAIL "SEND_FAILED"
long fileSize(const char fileNom[]);
int getFiles(char *cadena, int longitud);
int main(int argc, char *argv[])
{
char *namefile;
int i, del, ren, leng, length2, length3;
int socket_desc, accepted_socket, connfd = 0;
struct sockaddr_in server_addr; // Direccion del servidor
char down[_BUFFER_SIZE];
char response[_BUFFER_SIZE];
char reply[_BUFFER_SIZE] = "SUCCESS";
char reply2[_BUFFER_SIZE] = "ERROR";
char buffer[_BUFFER_SIZE];
char buffer2[_BUFFER_SIZE];
char buffer3[_BUFFER_SIZE];
char ack[_BUFFER_SIZE] = "ACK";
printf("Initializing echo server\n");
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0); // Sock stream --> SOCKET TCP
if (socket_desc < 0)
{
printf("Could not create socket");
return 0;
}
printf("Socket created.\n");
char *address = "0.0.0.0"; // Accept connections from the whole Internet
if (argc > 1)
address = argv[1];
server_addr.sin_addr.s_addr = inet_addr(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(ECHO_PORT);
bind(socket_desc, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(socket_desc, 10);
int exit = 0;
while (!exit)
{
printf("Echo server. Waiting for incoming connections from clients.\n");
accepted_socket = accept(socket_desc, (struct sockaddr *)NULL, NULL);
printf("Accepted a connection from client\n");
// First wait for the command of the client
ssize_t len = recv(accepted_socket, buffer, _BUFFER_SIZE, 0);
// If the client sends two or more arguments, the second is the file's name
if (len < 0)
{
printf("recv failed\n");
}
else
{
char fileName[_BUFFER_SIZE];
char command[_BUFFER_SIZE];
char command2[_BUFFER_SIZE];
printf("Received command from client: %s\n", buffer); // TODO: If the message is too long --> may print garbage
/*
We check the length of buffer2 and buffer3, if the length is
>= 1, it means that the client sent those
*/
length2 = strlen(buffer2);
if (length2 >= 1)
printf("Received file's name from client: %s\n", buffer2);
length3 = strlen(buffer3);
if (length3 >= 1)
printf("Received new file's name from client: %s\n", buffer3);
if (strcmp(buffer, "LIST_FILES") == 0)
{
printf("Received LIST_FILES command --> stopping server\n");
getFiles(buffer, _BUFFER_SIZE);
printf("Directory list: \n %s", buffer);
len = strlen(buffer);
if (send(accepted_socket, buffer, len, 0) < 0)
{
printf("Send failed\n");
return 1;
}
}
else if (strcmp(buffer, "DOWNLOAD_FILE") == 0)
{
i = 10;
long length = 0;
length = fileSize(buffer2);
printf("Received DOWNLOAD_FILE command %s, size: %ld \n", buffer2, length);
if (length >= 0)
{
//We enter in the If command just in case the size of the file is positive
sprintf(down, "%ld", length);
send(accepted_socket, down, _BUFFER_SIZE, 0);
recv(accepted_socket, ack, _BUFFER_SIZE, 0);
if (strcmp(ack, "ACK") == 0) //Checking the ACK message from the client
{
printf("ACK RECEIVED \n");
/* Open the file that we wish to transfer */
FILE *fp = fopen(buffer2, "rb");
if (fp == NULL)
{
printf("Error opening the file");
return 1;
}
else
{
while (1)
{
//Reading and sending the file the client wants to download
unsigned char buff[_BUFFER_SIZE] = {0};
int nread = fread(buff, 1, _BUFFER_SIZE, fp);
printf("Bytes read %d \n", nread);
if (nread > 0)
{
printf("Sending \n");
write(accepted_socket, buff, nread);
}
if (nread < _BUFFER_SIZE)
{
if (feof(fp))
printf("\nEnd of file\n");
if (ferror(fp))
printf("\nError reading\n");
break;
}
}
}
}
}
else
{
strcpy(buffer, reply2); //If ack doesn't is received, we send ERROR
printf("Error: unable to download the file\n");
send(accepted_socket, reply2, len, 0);
}
}
else if (strcmp(buffer, "DELETE_FILE") == 0)
{
del = remove(buffer2);
if (del == 0)
{
strcpy(buffer, reply);
printf("File deleted sucessfully\n");
send(accepted_socket, reply, len, 0);
}
else
{
strcpy(buffer, reply2);
printf("Error: unable to delete the file\n");
send(accepted_socket, reply2, len, 0);
}
}
else if (strcmp(buffer, "MOVE_FILE") == 0)
{
ren = rename("prueba.txt", "nuevaprueba.txt");
if (ren == 0)
{
strcpy(buffer, reply);
printf("File renamed successfully\n");
send(accepted_socket, reply, len, 0);
}
else
{
strcpy(buffer, reply2);
printf("Error: unable to rename the file\n");
send(accepted_socket, reply2, len, 0);
}
}
close(accepted_socket);
printf("Accepted connection closed.\n");
sleep(1);
}
}
printf("Closing binded socket\n");
close(socket_desc);
return 0;
}
int getFiles(char *cadena, int longitud)
{
int cont = 0;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
while ((dir = readdir(d)) != NULL)
{
cont += sprintf(cadena + cont, "%s\n", dir->d_name); // ¿Cuántos caracteres escribe la función? Contador, salir
}
closedir(d);
}
else
{
return -1;
}
return cont;
}
long fileSize(const char fileNom[])
{
long size = -1;
FILE *fich;
fich = fopen(fileNom, "rb");
if (fich != NULL)
{
fseek(fich, 0L, SEEK_END);
size = ftell(fich);
fclose(fich);
}
return size;
}
CLIENT
#include <stdio.h>
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
#include <stdlib.h>
#define ECHO_PORT 5675
#define _BUFFER_SIZE 20000
int main(int argc, char *argv[])
{
int len = 0, len2 = 0, bytesReceived = 0;
int socket_desc;
struct sockaddr_in server;
char received[_BUFFER_SIZE];
char *message, server_reply[_BUFFER_SIZE], server_status[_BUFFER_SIZE], file_size[_BUFFER_SIZE];
char *newNameOfFile = "";
char *nameOfFile = "";
char *comp;
char *ack = "ACK";
printf("Initializing socket\n");
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
char *address = "127.0.0.1";
if (argc > 1)
address = argv[1]; // Si existe, el primer argumento es la IP
server.sin_addr.s_addr = inet_addr(address);
server.sin_family = AF_INET;
server.sin_port = htons(ECHO_PORT);
printf("Trying to connect to address: %s\n", address);
//Connect to remote server
if (connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Connect error\n");
return 1;
}
printf("Connected Successfully\n");
//Send some data
message = "ECHO";
if (argc > 2)
message = argv[2]; // If exists, the second argument is the message
if (send(socket_desc, message, strlen(message) + 1, 0) < 0) // Importante el +1 para enviar el finalizador de cadena!!
{
printf("Send failed\n");
return 1;
}
printf("Message sent: %s\n", message);
//Send the file we want to
if (argc > 3)
nameOfFile = argv[3]; // If exists, the third argument is the file name
if (send(socket_desc, nameOfFile, strlen(nameOfFile) + 1, 0) < 0)
{
printf("Send failed\n");
return 1;
}
//We check if NameOfFile's size is >= 1
len = strlen(nameOfFile);
if (len >= 1)
printf("File name sent: %s\n", nameOfFile);
//Send the new file's name
if (argc > 4)
newNameOfFile = argv[4]; // If exists, the fourth argument is the new file's name
//Sending data to the server
if (send(socket_desc, newNameOfFile, strlen(newNameOfFile) + 1, 0) < 0)
{
printf("Send failed\n");
return 1;
}
//We check if newNameOfFile's size is >= 1
len2 = strlen(newNameOfFile);
if (len2 >= 1)
printf("New file's name sent: %s\n", newNameOfFile);
//Receive a reply from the server
if (recv(socket_desc, server_status, _BUFFER_SIZE, 0) >= 1)
{
printf("%s\n", server_status);
}
//We change the type of the data
long size = strtol(server_status, &comp, 10);
if (size >= 1)
{
send(socket_desc, ack, _BUFFER_SIZE, 0);
}
/*If our order is DOWNLOAD_FILE, its create the file
where we are storing the data*/
if ((strcmp(message, "DOWNLOAD_FILE") == 0) && size >= 1)
{
FILE *fp;
fp = fopen(nameOfFile, "wb");
if (NULL == fp)
{
printf("Error opening file");
return 1;
}
/* Receive data in chunks of 2000 bytes */
while ((bytesReceived = read(socket_desc, received, _BUFFER_SIZE)) > 0)
{
printf("Bytes received %d\n", bytesReceived);
fwrite(received, 1, bytesReceived, fp);
}
if (bytesReceived < 0)
{
printf("\n Read Error \n");
}
else
{
printf("SUCCESS");
}
}
//Help Command, general and particullary for each command
if (strcmp(message, "HELP") == 0)
{
printf("List of available commands: \n-LIST_FILES\n-DOWNLOAD_FILE <filename>\n-DELETE_FILE <filename>\n-MOVE_FILE <old_filename> <new_filename>\n \n \n For more help, type 'HELP_command'\n");
}
else if (strcmp(message, "HELP_LIST_FILES") == 0)
{
printf("Command: LIST_FILES \n");
printf("-Shows every existing file in the folder. \n");
}
else if (strcmp(message, "HELP_DOWNLOAD_FILE") == 0)
{
printf("Command: DOWNLOAD_FILES <filename> \n");
printf("-Downloads the requested file. \n");
}
else if (strcmp(message, "HELP_DELETE_FILE") == 0)
{
printf("Command: DELETE_FILE <filename> \n");
printf("-Deletes the requested file. \n");
}
else if (strcmp(message, "HELP_MOVE_FILE") == 0)
{
printf("Command: MOVE_FILE <old_filename> <new_filename> \n");
printf("-Renames the old_filename for the new_filename. \n");
}
close(socket_desc);
return 0;
}
I wrote a program to send the contents of a file between a computer and a raspberry pi 4; on localhost it works but when I try it between the two hosts it does not work.
The output is "Send Success, NumBytes = 7" from the sender part and "Receive Success, NumBytes = 0" from the receiver part.
I created the certificate following this guide: https://www.simba.com/products/SEN/doc/Client-Server_user_guide/content/clientserver/configuringssl/signingca.htm
sender.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "transfer.h"
#include <openssl/ssl.h>
void sendfile(FILE *fp, SSL *ssl);
ssize_t total = 0;
SSL_CTX *ctx;
int main(int argc, char *argv[])
{
SSL *ssl;
if (argc != 3)
{
perror("usage:send_file filepath <IPaddress>");
exit(1);
}
SSL_library_init();
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("Can't allocate sockfd");
exit(1);
}
if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
{
fprintf(stderr, "ERROR: failed to create SSL_CTX\n");
return -1;
}
SSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1_3);
if (SSL_CTX_load_verify_locations(ctx, "./certs/CA-cert.pem", 0) != 1)
{
fprintf(stderr, "Error loading ./certs/cacert.pem, please check the file.\n");
exit(EXIT_FAILURE);
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVERPORT);
if (inet_pton(AF_INET, argv[2], &serveraddr.sin_addr) < 0)
{
perror("IPaddress Convert Error");
exit(1);
}
if (connect(sockfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("Connect Error");
exit(1);
}
if ((ssl = SSL_new(ctx)) == NULL)
{
fprintf(stderr, "ERROR: failed to create SSL object\n");
return -1;
}
/* Attach SSL to the socket */
SSL_set_fd(ssl, sockfd);
/* Connect to SSL on the server side */
if (SSL_connect(ssl) < 0)
{
fprintf(stderr, "ERROR: failed to connect to SSL\n");
return -1;
}
char *filename = basename(argv[1]);
if (filename == NULL)
{
perror("Can't get filename");
exit(1);
}
char buff[BUFFSIZE] = {0};
strncpy(buff, filename, strlen(filename));
if (send(sockfd, buff, BUFFSIZE, 0) == -1)
{
perror("Can't send filename");
exit(1);
}
FILE *fp = fopen(argv[1], "rb");
if (fp == NULL)
{
perror("Can't open file");
exit(1);
}
sendfile(fp, ssl);
//puts("Send Success");
printf("Send Success, NumBytes = %d\n", total);
fclose(fp);
close(sockfd);
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
void sendfile(FILE *fp, SSL *ssl)
{
int n;
char sendline[MAX_LINE] = {0};
while ((n = fread(sendline, sizeof(char), MAX_LINE, fp)) > 0)
{
total += n;
if (n != MAX_LINE && ferror(fp))
{
perror("Read File Error");
exit(1);
}
if (SSL_write(ssl, sendline, strlen(sendline)) <= 0)
{
perror("Can't send file\n");
exit(1);
}
memset(sendline, 0, MAX_LINE);
}
}
receiver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "transfer.h"
#include <openssl/ssl.h>
void writefile(SSL *ssl, FILE *fp);
ssize_t total = 0;
int main(int argc, char *argv[])
{
SSL_CTX *ctx;
SSL *ssl;
SSL_library_init();
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("Can't allocate sockfd");
exit(1);
}
ctx = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
if (ctx == NULL)
{
fprintf(stderr, "ERROR: failed to create SSL_CTX\n");
return -1;
}
if (SSL_CTX_load_verify_locations(ctx, "./certs/CA-cert.pem", 0) != 1)
{
fprintf(stderr, "Error loading ./certs/ca-cert.pem, please check the file.\n");
return -1;
}
if (SSL_CTX_use_certificate_file(ctx, "./certs/server-cert.pem", SSL_FILETYPE_PEM) != 1)
{
fprintf(stderr, "Error loading ./certs/servercert.pem, please check the file.\n");
return -1;
}
if (SSL_CTX_use_PrivateKey_file(ctx, "./certs/server-key.pem", SSL_FILETYPE_PEM) != 1)
{
fprintf(stderr, "Error loading ./certs/serverkey.pem, please check the file.\n");
return -1;
}
//SSL_CTX_set_options(ctx,SSL_OP_NO_TICKET);
struct sockaddr_in clientaddr, serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERVERPORT);
if (bind(sockfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
{
perror("Bind Error");
exit(1);
}
if (listen(sockfd, LINSTENPORT) == -1)
{
perror("Listen Error");
exit(1);
}
socklen_t addrlen = sizeof(clientaddr);
int connfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
if (connfd == -1)
{
perror("Connect Error");
exit(1);
}
close(sockfd);
if ((ssl = SSL_new(ctx)) == NULL)
{
fprintf(stderr, "ERROR: failed to create SSL object\n");
return -1;
}
SSL_set_fd(ssl, connfd);
/* Establish TLS connection */
int ret = SSL_accept(ssl);
if (ret < 0)
{
fprintf(stderr, "SSL_accept error = %d\n", SSL_get_error(ssl, ret));
return -1;
}
char filename[BUFFSIZE] = {0};
if (recv(connfd, filename, BUFFSIZE, 0) == -1)
{
perror("Can't receive filename");
exit(1);
}
FILE *fp = fopen("output.txt", "wb");
if (fp == NULL)
{
perror("Can't open file");
exit(1);
}
char addr[INET_ADDRSTRLEN];
printf("Start receive file: %s from %s\n", filename, inet_ntop(AF_INET, &clientaddr.sin_addr, addr, INET_ADDRSTRLEN));
writefile(ssl, fp);
printf("Receive Success, NumBytes = %d\n", total);
SSL_free(ssl);
SSL_CTX_free(ctx);
fclose(fp);
close(connfd);
return 0;
}
void writefile(SSL *ssl, FILE *fp)
{
ssize_t n;
char buff[MAX_LINE] = {0};
clock_t t;
t = clock();
while ((n = SSL_read(ssl, buff, sizeof(buff))) > 0)
{
total += n;
if (n == -1)
{
perror("Receive File Error");
exit(1);
}
if (fwrite(buff, sizeof(char), n, fp) != n)
{
perror("Write File Error");
exit(1);
}
memset(buff, 0, MAX_LINE);
}
t = clock() - t;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("%f seconds to receive data \n", time_taken);
}
Thanks
The receiver calls recv() on connfd after this file descriptor has been given to the ssl connection, so there is nothing more to be read on the ssl connection.
I'm having difficulties with this project. Everything's working except for binary files, probably because they're pretty big and would expect timeouts. The packetErrorSends are similar to send but they randomly drop packets. I only put them in the client side to isolate my problem. The algorithm I'm going for is send a piece of the file, if it receives it, send an 's' and continue reading and send the send piece. If it times out, send an 'e' and then hop in the loop, resend the current piece and recv the next status update. "While" checks the status, decides whether to move on or resend that piece. The problem is when is times out, server gets stuck in that loop resending the last chunk of data and client keeps timing out and sending back 'e's (the purpose of the print statements) Any clue what's happening?
client:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "packetErrorSend.h"
#include <sys/time.h>
ssize_t recvx(int sockfd, void *buf, size_t len) {
int var = recv(sockfd, buf, len, 0);
if(var != -1)
{
return var;
} else {
printf("%s \n","Did not receive.");
exit(1);
}
}
int main(int argc, char *argv[])
{
char buf[MAX_PACKET_DATA_SIZE];
struct addrinfo hints;
struct addrinfo *rp, *result;
int bytes_received = 1;
int s;
char *server;
char *port;
char *file;
int fd = -1; //file descriptor
int bytes_written = 1;
fd_set readfds;
struct timeval tv;
int rv = 0;
int status;
char sendStatus[1];
if (argc==4)
{
server = argv[1];
port = argv[2];
file = argv[3];
}
else
{
fprintf(stderr, "invalid # of arguments\n");
exit(1);
}
/* Translate host name into peer's IP address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
if ((s = getaddrinfo(server, port, &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to connect */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1)
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-client: connect");
exit(1);
}
freeaddrinfo(result);
FD_ZERO(&readfds);
FD_SET(s, &readfds);
packetErrorSend(s, file, strlen(file)+1, 0);
tv.tv_sec = 2;
rv = select(s+1, &readfds, NULL, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
} else if (rv == 0) {
printf("Timeout occurred on filename! No data after 2 seconds.\n");
close(s);
exit(0);
} else {
status = recvx(s,buf,1);
}
if(status == 0 || buf[0] == 'e')
{
fprintf(stderr, "Server Error: unable to access file %s \n", file);
close(s);
exit(0);
}
if(buf[0] == 's')
{
fd =open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1)
{
fprintf(stderr,"%s \n","Client Error: Open failed");
close(s);
exit(0);
}
FD_ZERO(&readfds);
FD_SET(s, &readfds);
bytes_received = MAX_PACKET_DATA_SIZE;
// while(bytes_received >= MAX_PACKET_DATA_SIZE)
while(bytes_received > 0)
{
tv.tv_sec = 3;
rv = select(s+1, &readfds, NULL, NULL, &tv);
if (rv == -1) {
perror("select");
} else if (rv == 0) {
printf("bytes_received in timeout %d \n", bytes_received );
printf("Timeout occurred! No data after 3 seconds.\n");
sendStatus[0] = 'e';
send(s,sendStatus,1,0);
} else {
bytes_received = recvx(s, buf, MAX_PACKET_DATA_SIZE);
printf("%d\n", bytes_received);
sendStatus[0]='s';
packetErrorSend(s,sendStatus,1,0);
}
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
break;
}
}
if(bytes_received == -1)
{
fprintf(stderr,"%s \n", "Client Error: Error receiving file");
exit(0);
}
if(close(fd) != 0)
{
printf("%s \n", "Client Error: File did not close successfully");
exit(0);
}
close(s);
}
return 0;
}
server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "packetErrorSend.h"
#include <sys/time.h>
#define SERVER_PORT "5432"
#define MAX_PENDING 5
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *rp, *result;
char file[MAX_PACKET_DATA_SIZE];
int s, new_s;
int bytes_transferred = 0;
int fd; //file descriptor
char status[1];
/* Build address data structure */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
/* Get local address info */
if ((s = getaddrinfo(NULL, argv[1], &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to perform passive open */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (!bind(s, rp->ai_addr, rp->ai_addrlen))
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-server: bind");
exit(1);
}
if (listen(s, MAX_PENDING) == -1)
{
perror("stream-talk-server: listen");
close(s);
exit(1);
}
freeaddrinfo(result);
/* Wait for connection, then receive and print text */
while(1)
{
for(int i = 0; i < sizeof(file); i++)
{
file[i] = '\0';
}
if ((new_s = accept(s, rp->ai_addr, &(rp->ai_addrlen))) < 0)
{
perror("stream-talk-server: accept");
close(s);
exit(0);
}
if ((bytes_transferred = recv(new_s,file,sizeof(file),0)) > 0)
{
fd = open(file,O_RDONLY);
if(fd < 0)
{
perror("open");
status[0] = 'e';
send(new_s,status,1,0);
close(new_s);
exit(0);
}
status[0] = 's';
send(new_s,status,1,0);
int datasent = 0;
bytes_transferred = 1;
while(bytes_transferred > 0)
{
status[0] = s;
bytes_transferred = read(fd,file,MAX_PACKET_DATA_SIZE);
printf("%d\n",bytes_transferred);
datasent = send(new_s,file,bytes_transferred,0);
if (datasent < 0)
{
perror("send");
break;
}
recv(new_s,status,1,0);
printf("before while: %c \n", status[0]);
while(status[0] == 'e')
{
recv(new_s,status,1,0);
send(new_s,file,bytes_transferred,0);
printf("in while: %c \n", status[0]);
}
}
if (bytes_transferred == 0)
{
break;
}
else if(bytes_transferred == -1)
{
perror("read");
close(new_s);
exit(0);
}
}
}
close(fd);
close(new_s);
return 0;
}
I'm creating a program that requests a file from a server and sends back the file, creating a new file and writing the contents of that file into the new file. Everything is working perfectly except server side error messages (i.e. file doesn't exist) should be printed on the client side. Can anyone give me a clue on how to accomplish this? I'm new to C, but willing to learn more. My main problem is that if I send the message directly, the client side doesn't know that it's an error and is going to finish out and create a file and write the message into that file. I was thinking I need to send back a -1, so when client calls recv, it gets back -1 as the length and prints a message.
Here's my client:
ssize_t recvx(int sockfd, void *buf, size_t len) {
int var = recv(sockfd, buf, len, 0);
if(var != -1)
{
return var;
} else {
printf("%s \n","Did not receive.");
exit(1);
}
}
int main(int argc, char *argv[])
{
char buf[MAX_LINE];
struct addrinfo hints;
struct addrinfo *rp, *result;
int bytes_received;
int s;
char *server;
char *port;
char *file;
int fd = -1; //file descriptor
int bytes_written;
if (argc==4)
{
server = argv[1];
port = argv[2];
file = argv[3];
}
else
{
fprintf(stderr, "invalid # of arguments\n");
exit(1);
}
/* Translate host name into peer's IP address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
if ((s = getaddrinfo(server, port, &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to connect */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1)
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-client: connect");
exit(1);
}
freeaddrinfo(result);
/*send lines of text */
send(s, file, sizeof(file), 0);
while(bytes_received != 0)
{
bytes_received = recvx(s, buf, 20);
if(bytes_received == -1)
{
fprintf(stderr, "Client Error: Error receiving file \n");
exit(1);
} else {
if(fd == -1)
{
fd = open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1)
{
fprintf(stderr,"Client Error: Open failed \n");
exit(1);
}
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
exit(1);
}
} else {
bytes_written = write(fd,buf,bytes_received);
if(bytes_written == -1)
{
fprintf(stderr,"%s \n", "Client Error: Write error");
exit(1);
}
}
}
}
if(close(fd) != 0)
{
printf("%s \n", "Client Error: File did not close successfully");
exit(1);
}
close(s);
return 0;
}
Here's my server:
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *rp, *result;
char filename[MAX_LINE];
int s, new_s;
int bytes_transferred;
int fd; //file descriptor
struct stat statBuffer; //to hold file info
off_t offset = 0;
/* Build address data structure */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
/* Get local address info */
if ((s = getaddrinfo(NULL, argv[1], &hints, &result)) != 0 )
{
fprintf(stderr, "%s: getaddrinfo: %s\n", argv[0], gai_strerror(s));
exit(1);
}
/* Iterate through the address list and try to perform passive open */
for (rp = result; rp != NULL; rp = rp->ai_next)
{
if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1 )
{
continue;
}
if (!bind(s, rp->ai_addr, rp->ai_addrlen))
{
break;
}
close(s);
}
if (rp == NULL)
{
perror("stream-talk-server: bind");
exit(1);
}
if (listen(s, MAX_PENDING) == -1)
{
perror("stream-talk-server: listen");
close(s);
exit(1);
}
freeaddrinfo(result);
/* Wait for connection, then receive and print text */
while(1)
{
if ((new_s = accept(s, rp->ai_addr, &(rp->ai_addrlen))) < 0)
{
perror("stream-talk-server: accept");
close(s);
exit(1);
}
while(bytes_transferred == recv(new_s,filename,sizeof(filename),0))
{
if(bytes_transferred == -1)
{
fprintf(stderr, "Server Error: Error receiving filename \n");
exit(1);
}
}
printf("%s \n", filename);
fd =open(filename,O_RDONLY);
if(fd < 0)
{
fprintf(stderr,"Server Error: file doesn't exist\n");
exit(1);
}
else
{
printf("%s \n","file opened successfully");
}
/*get info from file descriptor (fd) and store it in statBuffer struct */
fstat(fd, &statBuffer);
bytes_transferred = sendfile(new_s,fd,&offset,statBuffer.st_size);
if(bytes_transferred == -1)
{
fprintf(stderr, "Server Error: File not transferred successfully");
}
else
{
printf("%s \n", "File transferred successfully");
}
if(close(fd) != 0)
{
fprintf(stderr, "Server Error: File not close successfully");
}
else{
break;
}
}
close(new_s);
return 0;
}
Essentially what you're implementing is something along the lines of what HTTP and FTP servers do. Both have specific, but rather different, protocol steps for handling errors. The HTTP case is easy to understand -- the server doesn't just send back the file: it sends back a bunch of headers, then the file. The client can inspect the headers to know whether there is any file data to follow.
In a very simple case, I suspect you could implement something similar in your code without too much work -- just have the server send a one-byte response code before the rest of the file. You could have 0 (for example) to indicate that a valid file follows, and other numbers to indicate particular error conditions. The client would just read the data as it currently does, but if the first byte isn't zero, it knows that nothing else that follows (if anything does) is valid.
Or you could arrange that if the first byte was not zero, the text that follows is not the expected file, but an error message, which should be displayed by the client.
You could extent such a scheme with as much complexity as you like, until you end up with HTTP :)
This is my client program that requests files from the server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
void setstring(char *str){
str[MAX_LINE-1]='\0';
}
int main(int argc, char * argv[]){
FILE *fp;
struct hostent *hp;
struct sockaddr_in sin;
char *host;
char filename[MAX_LINE],buf[MAX_LINE],reply[MAX_LINE],rec_line[MAX_LINE];
int s;
char msg[MAX_LINE];
int len,new_len,rec_file;
if (argc==2) {
host = argv[1];
}
else {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
/* translate host name into peer's IP address */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
else
printf("Client's remote host: %s\n", argv[1]);
/* build address data structure */
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
/* active open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
else
printf("Client created socket.\n");
int send_file_name,rec_msg;
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
else{
printf("Client connected.\n");
/* main loop: get and send lines of text */
printf("Hello from server\n");
while(!(strcmp(reply,"bye")==0)){
printf("Enter the file name:\n");
scanf("%s",filename);
setstring(filename);
send_file_name=send(s,filename,strlen(filename)+1,0);
if(send_file_name<0)
fputs("Error sending filename",stdout);
rec_msg=recv(s,msg,sizeof(msg),0);
if(strcmp(msg,"File not found")==0)
printf("File not found\n");
else{
printf("%s\n",msg);
fp=fopen(filename,"w");
printf("CP1\n");
if(rec_file=recv(s,rec_line,sizeof(rec_line),0)>0){
printf("CP2");
printf("String recieved:%s\n",rec_line);
if(len=fwrite(rec_line,1,rec_file+1,fp)>0)
printf("Recieved file\n");
else
printf("Error writing to file\n");
}
else
printf("Not recieved\n");
}
printf("Enter 'bye' to terminate requesting files\n");
scanf("%s",reply);
}
}
return 0;
}
This is my server program that accepts request for files from the client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 256
void setstring(char* str){
str[MAX_LINE-1]='\0';
}
int main(){
FILE *fp;
struct sockaddr_in sin;
char buf[MAX_LINE],msg[MAX_LINE],*rec_line;
int len;
int s, new_s,count;
char str[INET_ADDRSTRLEN];
int error_file,send_msg,read_line,send_file;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("0.0.0.0");
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
inet_ntop(AF_INET, &(sin.sin_addr), str, INET_ADDRSTRLEN);
printf("Server is using address %s and port %d.\n", str, SERVER_PORT);
if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0) {
perror("simplex-talk: bind");
exit(1);
}
else
printf("Server bind done.\n");
listen(s, MAX_PENDING);
/* wait for connection, then receive and print text */
while(1) {
if ((new_s = accept(s, (struct sockaddr *)&sin, &len)) < 0) {
perror("simplex-talk: accept");
exit(1);
}
printf("Server Listening.\n");
printf("Greetings\n");
int rec_file_name=recv(new_s,buf,sizeof(buf),0);
if(rec_file_name>0)
printf("File requested:%s\n",buf);
fp=fopen(buf,"r");
if(fp==NULL)
{
fputs("File not found\n",stdout);
strcpy(buf,"File not found");
if(error_file=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("Successfully send error message to client\n",stdout);
}
else{
bzero(buf,MAX_LINE);
printf("File found :) \n");
strcpy(buf,"OK");
if(send_msg=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("File found message sent to client\n",stdout);
fseek(fp,0,SEEK_END);
int file_size=ftell(fp);
fseek(fp,0,SEEK_SET);
printf("File size:%d\n",file_size);
rec_line=(char *)malloc(sizeof(char)*(file_size));
read_line=fread(rec_line,1,file_size+1,fp);
printf("File read: %s\n",rec_line);
if(send_file=send(new_s,rec_line,strlen(rec_line)+1,0)>0)
printf("File string sent to client\n");
}
}
close(new_s);
}
The problem is that in the client, my second recv() call, where it is supposed to receive the contents of a file, shows nothing. The programs halts at that point, but the server programs displays that it has sent the file contents. The client doesn't receive it.
The basic problem is that you're not checking the return values to see how much data you actually sent and received. So when the client calls:
rec_msg=recv(s,msg,sizeof(msg),0);
it will receive up to sizeof(msg) (512) bytes, which is probably both the OK message the server is sending AND the file contents (after the NUL). Which means when it does a second recv call to get the contents, it blocks, because it already read the contents in the first call and there's no more data waiting in the receive buffer.
Your error-checking is haphazard, and consequently you're certainly missing a problem that occurs before the behavior you're observing. I recommend you follow RW Steven's idiom:
int n;
if( (n = recv(new_s, buf, sizeof(buf), 0)) < 0 ) {
err(EXIT_FAILURE, "recv %d", __LINE__);
}
Test every function call, and handle every error. For simple programs, just call err(3) on error. Do that consistently, and the program's behavior will be much less mysterious (if still occasionally surprising). And don't be afraid of the spacebar! It's easy to hit, and easier still to read.
My other bit of advice, if I may, concerns
int send_file_name,rec_msg;
Names like that are confusing. A name is almost never an integer. For I/O sizes, just use one simple name like n, len, or size. Even if you don't care for yourself, you want to care before publishing your question in an open forum. Otherwise, when people see
send_file_name=send(s,filename,strlen(filename)+1,0);
they may think send is some function other than send(2), or that the person asking the question was careless.
The main problem I see is that neither the client nor the server are handling socket I/O correctly in general. They are not handling the cases where reads and writes transfer fewer bytes then requested, you need to loop the I/O. And the client is reading too many bytes from the server anyway, which is why your second recv() is blocking. And you are relying on a disconnect to indicate the end of the file has been reached, but that does not allow the client to do adequate error checking to know if the full file was actually received or not.
Also, when sending the content of a file, the server is attempting to read the entire file into memory (bad!), not doing adequate error checking on the file I/O, and it is treating the file content as text instead of as binary (don't use strlen() on binary data!).
Try something more like this instead:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
int sendstring(int sock, const char *str) {
if (!str) str = "";
int len = strlen(str) + 1;
do {
int ret = send(sock, str, len, 0);
if (ret <= 0) return -1;
str += ret;
len -= ret;
}
while (len > 0);
return 0;
}
int readbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (buflen > 0) {
int len = recv(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int readfile(int sock, int fd) {
int filesize;
char buf[MAX_LINE];
if (readbuf(sock, &filesize, sizeof(filesize)) < 0) return -1;
filesize = ntohl(filesize);
while (filesize > 0) {
int len = readbuf(sock, buf, min(sizeof(buf), filesize));
if (len < 0) return -1;
if (fwrite(buf, len, 1, fp) != 1) return -2;
filesize -= len;
}
return 0;
}
int main(int argc, char * argv[]) {
char filename[MAX_LINE], reply[MAX_LINE];
if (argc != 2) {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
char *host = argv[1];
/* translate host name into peer's IP address */
struct hostent *hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
if (hp->h_addrtype != AF_INET) {
fprintf(stderr, "simplex-talk: unsupported address type %d for host: %s\n", hp->h_addrtype, host);
exit(1);
}
/* build address data structure */
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
printf("Host's remote IP: %s\n", inet_ntoa(&sin.sin_addr));
/* active open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Client created socket.\n");
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
printf("Client connected.\n");
/* main loop: get and send lines of text */
do {
printf("Enter the file name ('bye' to quit):\n");
if (scanf("%512s", filename) != 1) {
printf("Error reading filename\n");
break;
}
if (strcmp(filename, "bye") == 0) {
sendstring(s, "bye");
break;
}
if (sendstring(s, filename) < 0) {
printf("Error sending filename\n");
break;
}
if (readstring(s, reply, sizeof(reply)) < 0) {
printf("Error reading reply\n");
break;
}
if (strcmp(reply, "OK") != 0) {
printf("%s\n", reply);
if (strcmp(reply, "bye") == 0) break;
continue;
}
FILE *fp = fopen(filename, "wb");
if (!fp) {
printf("Error opening file\n");
break;
}
printf("Receiving file\n");
int ret = readfile(s, fd);
fclose(fp);
if (ret < 0) {
if (ret == -2)
printf("Error writing file\n");
else
printf("Error reading file\n");
break;
}
printf("Received file\n");
}
while (1);
close(s);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 512
int sendbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (len > 0) {
int len = send(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int sendstring(int sock, const char *str) {
if (!str) str = "";
return sendbuf(sock, str, strlen(str) + 1);
}
int sendfile(int sock, int fd) {
char buf[MAX_LINE];
struct stat s;
if (fstat(fd, &s) < 0) return -2;
int pos = ftell(fp);
if (pos == -1) return -2;
int file_size = s.st_size - pos;
int tmp_file_size = htonl(file_size);
if (sendbuf(sock, &tmp_file_size, sizeof(tmp_file_size)) < 0) return -1;
while (file_size > 0) {
int len = fread(buf, 1, min(sizeof(buf), file_size), fp);
if (len < 1) return -2;
if (sendbuf(sock, buf, len) < 0) return -1;
file_size -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int main() {
char msg[MAX_LINE];
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Server is using address %s and port %d.\n", inet_ntoa(&(sin.sin_addr)), SERVER_PORT);
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("simplex-talk: bind");
close(s);
exit(1);
}
printf("Server bind done.\n");
if (listen(s, MAX_PENDING) < 0) {
perror("simplex-talk: listen");
close(s);
exit(1);
}
printf("Server Listening.\n");
/* wait for connection, then receive and print text */
do {
int len = sizeof(sin);
int cli_s = accept(s, (struct sockaddr *)&sin, &len);
if (cli_s < 0) {
perror("simplex-talk: accept");
close(s);
exit(1);
}
printf("Client connected\n");
do {
if (readstring(cli_s, msg, sizeof(msg)) < 0) {
printf("Error reading request\n");
break;
}
if (strcmp(msg, "bye") == 0) break;
printf("File requested: %s\n", msg);
FILE *fp = fopen(msg, "rb");
if (!fp)
{
printf("Cannot open file\n");
if (sendstring(cli_s, "Cannot open file") < 0) {
printf("Error sending reply\n");
break;
}
continue;
}
printf("File found :) \n");
if (sendstring(cli_s, "OK") < 0) {
printf("Error sending reply\n");
fclose(fp);
break;
}
ret = sendfile(cli_s, fp);
fclose(fp);
if (ret < 0) {
printf("Error sending file\n");
break;
}
printf("File sent to client\n");
}
while (1);
close(cli_s);
}
while (1);
close(s);
return 0;
}