Related
I have been trying to write a client and server program using C socket programming that does 2 tasks
On prompted from server -> OS command is executed in client machine
Output is stored in a file which is sent back to server. Where server reads it and displays the data plus writes the data in a file on server side.
Issue
When client sends file to the server, server does not display whole data and looks like recv() keeps waiting on socket.
Need some help
Here's the code
server code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define SIZE 1024
void write_file(char *filename,int sockfd){
int n;
FILE *fp ;
char buffer[SIZE] ;
fp = fopen(filename, "w");
while (1) {
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0){
break;
return;
}
printf("%s", buffer) ;
fprintf(fp, "%s", buffer);
bzero(buffer, SIZE);
}
fclose(fp) ;
return;
}
int main(int argc, char *argv[]){
int portno = 7416;
int e,n,n1;
char *cmd ;
int sockfd;
struct sockaddr_in server_addr;
struct hostent *server;
char *fle = "/tmp/reposvr.";
FILE *fp;
char *num ;
if (argc < 3) {
fprintf(stderr,"usage %s hostname command \n", argv[0]);
exit(0);
}
cmd=argv[2] ;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
perror("[-]Error in socket");
exit(1);
}
printf("[+]Server socket created successfully.\n");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
//server_addr.sin_family = AF_INET;
//server_addr.sin_port = port;
//server_addr.sin_addr.s_addr = inet_addr(ip);
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portno);
server_addr.sin_addr = *((struct in_addr *)server->h_addr);
memset(server_addr.sin_zero, '\0', sizeof server_addr.sin_zero);
e = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(e == -1) {
perror("[-]Error in socket");
exit(1);
}
printf("[+]Connected to Server.\n");
n = write(sockfd, cmd , strlen(cmd));
if (n < 0)
error("ERROR writing to socket");
char *filename=NULL;
filename = (char*)malloc( 50 * sizeof(char) ) ;
strcpy(filename,fle );
n1=rand();
if (asprintf(&num, "%d", n1) == -1) {
perror("asprintf");
} else {
strcat(filename, num);
printf("%s\n", filename);
free(num);
}
printf("%s\n",filename) ;
write_file(filename,sockfd) ;
printf("[+]File data sent successfully.\n");
printf("[+]Closing the connection.\n");
close(sockfd);
return 0;
}
Client code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#define SIZE 1024
void send_file(FILE *fp, int sockfd){
int n;
char data[SIZE] = {0};
while(fgets(data, SIZE, fp) != NULL) {
if (send(sockfd, data, sizeof(data), 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
bzero(data, SIZE);
}
}
int main(){
int port = 7416;
int e,n1, n ;
FILE *fp ;
char filename[50] = "/tmp/gateway." ;
char command[50];
char *num;
FILE *pf;
char data[SIZE];
int sockfd, new_sock;
struct sockaddr_in server_addr, new_addr;
socklen_t addr_size;
char buffer[SIZE];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
perror("[-]Error in socket");
exit(1);
}
printf("[+]Server socket created successfully.\n");
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
e = bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(e < 0) {
perror("[-]Error in bind");
exit(1);
}
printf("[+]Binding successfull.\n");
if(listen(sockfd, 10) == 0){
printf("[+]Listening....\n");
}else{
perror("[-]Error in listening");
exit(1);
}
addr_size = sizeof(new_addr);
while ( new_sock = accept(sockfd, (struct sockaddr*)&new_addr, &addr_size) )
{
printf("server: got connection from %s port %d\n",
inet_ntoa(new_addr.sin_addr), ntohs(new_addr.sin_port));
n = read(new_sock,buffer,1024);
if (n < 0) error("ERROR reading from socket");
strcpy(command, buffer) ;
printf("%s",command) ;
pf = popen(command,"r");
bzero(data,1024);
n1=rand();
if (asprintf(&num, "%d", n1) == -1) {
perror("asprintf");
} else {
strcat(filename, num);
printf("%s\n", filename);
free(num);
}
printf("%s",filename) ;
fp=fopen(filename,"w" );
while (fgets(data, 2048, pf) != NULL) {
printf("%s", data);
fprintf(fp, "%s",data) ;
}
fclose(fp) ;
fp = fopen(filename, "r");
if (fp == NULL) {
perror("[-]Error in reading file.");
exit(1);
}
send_file(fp, new_sock);
printf("[+]Data written in the file successfully.\n");
}
return 0;
}
Execution output
Edit
Based on the suggestions in this forum, I increased SIZE to 2048 and found that now it returns less rows and hangs after 4-5 rows itself. This is weird. I am sure it's not because of SIZE.
If you see, in client side, we are able to successfully write all in a file. Second part of the program is to read from file and send it to server. Here I am iterating through whole file and sending the data to server. The data send loop is working nicely as I have printf in same loop and we can see all data getting printed.
Looking at server side, here also we are looping till number of bytes read are < 1. Theoretically, this should work. I am sure there's some tiny silly thing that I am missing. Just not able to put my finger on it. Really need some insights in here.
Edit 2
Already tried SIZE , MSG_PEEK as you can see in comments. Any more suggestions ?
I have been trying to get this FTP client/ server to work together but its currently not playing friendly
Bits of code that I have tested and that I couldn't find to work have been commented but also some of these commented recv and write lines work but to make the over all problem simpler i have commented them out for the time being. The problem seems to be in the ConnectionHandler function under the else if(strstr(client_request, "Put")) line of code. i have currently got successful FTP from the server to the client but the client to server part I'm struggling to get to work any advice would be grateful.
I feel as though this line is the problem file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0777); but i need to create the file to run to but file_desc returns -1 even though the file is created and I'm simply unable to determine why this is the case. Ultimately i may be overlooking something else.
Server
/*
* Compile & link : gcc server.c -lpthread -o server
* Execute : ./server
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#define SERVER_PORT 8080
void* ConnectionHandler(void* socket_desc);
char* GetFilenameFromRequest(char* request);
bool SendFileOverSocket(int socket_desc, char* file_name);
int readn(int fd, char *buf, int bufsize)
{
short data_size; // sizeof (short) must be 2
int n, nr, len;
// check buffer size len
if (bufsize < BUFSIZ)
return (-3); // buffer too small
// get the size of data sent to me
if (read(fd, (char *) &data_size, 1) != 1)
return (-1);
if (read(fd, (char *) (&data_size)+1, 1) != 1)
return (-1);
len = (int) ntohs(data_size); // convert to host byte order
// read len number of bytes to buf
for (n=0; n < len; n += nr)
{
if ((nr = read(fd, buf+n, len-n)) <= 0)
return (nr); // error in reading
}
return (len);
}
int main(int argc, char **argv)
{
int socket_desc,
socket_client,
*new_sock,
c = sizeof(struct sockaddr_in);
struct sockaddr_in server,
client;
// Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
perror("Could not create socket");
return 1;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(SERVER_PORT);
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Bind failed");
return 1;
}
listen(socket_desc , 3);
while (socket_client = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c))
{
pthread_t sniffer_thread;
new_sock = malloc(1);
*new_sock = socket_client;
pthread_create(&sniffer_thread, NULL, ConnectionHandler, (void*) new_sock);
pthread_join(sniffer_thread, NULL);
}
if (socket_client<0)
{
perror("Accept failed");
return 1;
}
return 0;
}
void *ConnectionHandler(void *socket_desc)
{
int socket = *(int*)socket_desc;
char server_response[BUFSIZ],
client_request[BUFSIZ],
file_name[BUFSIZ];
recv(socket, client_request, BUFSIZ, 0); //Wait at this point to recieve a message from the client
if(strstr(client_request, "Get"))
{
perror("1");
strcpy(file_name, GetFilenameFromRequest(client_request));
// If requested file exists, notify the client and send it
if (access(file_name, F_OK) != -1){
perror("2");
strcpy(server_response, "OK");
write(socket, server_response, strlen(server_response));
perror("3");
SendFileOverSocket(socket, file_name);
perror("4");
}
else {
// Requested file does not exist, notify the client
strcpy(server_response, "NO");
write(socket, server_response, strlen(server_response));
}
}
else if(strstr(client_request, "Put"))
{
char *data, reply_msg[BUFSIZ];
char buff[BUFSIZ];
int file_size, file_desc;
perror("0.5");
strcpy(file_name, GetFilenameFromRequest(client_request));
perror("0.75");
//recv(socket, reply_msg, 2, 0); //Receive reply message from client if file exists
//fprintf(stderr, "File was found = %s", reply_msg);
//Create File
perror("1");
recv(socket, &file_size, sizeof(int), 0);
perror("1.25");
file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
perror("1.5");
int incomingSize;
while((incomingSize = readn(socket, buff, sizeof(buff))) > 0)
{
perror("2 ");
for(int i = 0; i <= sizeof(buff); i++)
{
buff[incomingSize] = '\0';
write(file_desc, buff, sizeof(buff));
}
}
perror("3");
if(incomingSize < 0)
{
perror("Something went wrong connection broken");
return; // connection broken down
}
else
{
fprintf(stderr, "Connection terminated");
}
perror("4");
close(socket);
/*
perror("1");
data = malloc(BUFSIZ);
file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0777);
perror("1.2");
fprintf(stderr, "fileDesc = %d", file_desc);
perror("1.5");
strcpy(server_response, "OK");
write(socket, server_response, strlen(server_response));
perror("2");
//recv(socket, &file_size, sizeof(int), 0);
perror("3");
//recv(socket, &file_size, sizeof(int), 0);
file_size = BUFSIZ;
perror("4");
fprintf(stderr, "%d", file_size);
recv(socket, data, file_size, 0);
perror("5");
write(file_desc, data, file_size);
perror("6");
close(file_desc);
*/
/*// Start receiving file
if (strcmp(reply_msg, "OK") == 0) //Check that response is "OK"
{
}
else
{
fprintf(stderr, "Bad request\n");
}
*/
}
free(socket_desc);
return 0;
}
char* GetFilenameFromRequest(char* request)
{
char *file_name = strchr(request, ' ');
return file_name + 1;
}
bool SendFileOverSocket(int socket_desc, char* file_name)
{
struct stat obj;
int file_desc,
file_size;
stat(file_name, &obj);
file_desc = open(file_name, O_RDONLY);
file_size = obj.st_size;
send(socket_desc, &file_size, sizeof(int), 0);
sendfile(socket_desc, file_desc, NULL, file_size);
return true;
}
Client
/*
* Compile & link : gcc client.c -o client
* Execute : ./client
*
*/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define FILENAME "a.txt" //Possibly worth changing to make it more dynamic
//Should consider overriding this with command line arguments if passed
#define SERVER_IP "127.0.0.1" //Address of local host
#define SERVER_PORT 8080
bool SendFileOverSocket(int socket_desc, char* file_name);
int writen(int fd, char *buf, int nbytes)
{
short data_size = nbytes; // short must be two bytes long
int n, nw;
if (nbytes > BUFSIZ)
return (-3); // too many bytes to send in one go
// send the data size
data_size = htons(data_size);
if (write(fd, (char *) &data_size, 1) != 1)
return (-1);
if (write(fd, (char *) (&data_size)+1, 1) != 1)
return (-1);
// send nbytes
for (n=0; n<nbytes; n += nw)
{
if ((nw = write(fd, buf+n, nbytes-n)) <= 0)
return (nw); // write error
}
return (n);
}
int main(int argc , char **argv)
{
int socket_desc;
// Variables for the file being received
int file_size, file_desc;
struct sockaddr_in server;
char request_msg[BUFSIZ],
reply_msg[BUFSIZ],
userCommand[BUFSIZ],
userFileName[BUFSIZ],
*data;
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
perror("Could not create socket");
return 1;
}
server.sin_addr.s_addr = inet_addr(SERVER_IP);
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT);
// Connect to server
if (connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Connection failed");
return 1;
}
printf("------------Enter Command-----------\n");
printf("Get, Put, Pwd, lpwd, dir, ldir, cd, lcd, quit\n");
printf("------------------------------------\n");
while(userCommand != "quit")
{
printf(">");
fgets(userCommand, BUFSIZ, stdin);
printf("User Command Variable = %s", userCommand);
if(strstr(userCommand, "Get"))
{
char userFileName[BUFSIZ];
// Get a file from server
strcpy(request_msg, "Get "); //Copy message "Get " into variable request_msg
printf("Enter the file name you would like to send.");
scanf("%s", userFileName);
strcat(request_msg, userFileName); //Add the file name to the end of the varaible that is now "Get "
printf("1");
write(socket_desc, request_msg, strlen(request_msg)); //Changing this to fwrite produces same results with more warnings
printf("2");
recv(socket_desc, reply_msg, 2, 0); //Wait here for response
printf("3");
// Start receiving file
if (strcmp(reply_msg, "OK") == 0) //Check that response is "OK"
{
printf("4");
recv(socket_desc, &file_size, sizeof(int), 0);
printf("5");
data = malloc(file_size);
file_desc = open(userFileName, O_CREAT | O_EXCL | O_WRONLY, 0666);
recv(socket_desc, data, file_size, 0);
printf("6");
write(file_desc, data, file_size);
printf("7");
close(file_desc);
}
else
{
fprintf(stderr, "Bad request\n");
}
}
else if(strstr(userCommand, "Put"))
{
char server_response[BUFSIZ], buff[BUFSIZ];
struct stat obj;
fprintf(stderr,"What is the file name you would like to send?\n");
scanf("%s", userFileName);
fprintf(stderr,"File name entered was: %s", userFileName);
strcpy(request_msg, "Put "); //Copy message "Get " into variable request_msg
strcat(request_msg, userFileName); //Add the file name to the end of the varaible that is now "Get "
perror("1");
write(socket_desc, request_msg, strlen(request_msg)); //Changing this to fwrite produces same results with more warnings
perror("2");
stat(userFileName, &obj);
file_desc = open(userFileName, O_RDONLY);
file_size = obj.st_size;
perror("2.1");
strcpy(reply_msg, "OK");
perror("2.2");
//write(socket_desc, reply_msg, strlen(reply_msg));
perror("2.3");
perror("2.4");
perror("2.45");
writen(socket_desc, buff, sizeof(buff));
//SendFileOverSocket(socket_desc, userFileName);
perror("3");
close(socket_desc);
/*if (access(userFileName + 1, F_OK) != -1)
{
}
else {
printf("File doesnt exist with access code %d\n", access(userFileName, F_OK));
strcpy(reply_msg, "OK");
write(socket, reply_msg, strlen(reply_msg));
}*/
}
}
return 0;
}
/*bool SendFileOverSocket(int socket_desc, char* file_name)
{
struct stat obj;
int file_desc,
file_size;
stat(file_name, &obj);
file_desc = open(file_name, O_RDONLY);
file_size = obj.st_size;
printf("2.5");
printf("%s", file_name);
//send(socket_desc, &file_size, sizeof(int), 0);
printf("2.6");
perror("Pre");
sendfile(socket_desc, file_desc, NULL, file_size);
perror("sendfile");
printf("2.7");
return true;
}*/
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;
}
my assignment was to build a chat server and client with the beej's guide examples so the client can upload a file to the server.
the instructions were to mmap the file and send the data using send().
iv'e tried first just to send simple txt file but it wont work.
for some reason when debug the program and execcutes it line by line it works.
maybe someone can point out what am i missing?
client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT "6667" // the port client will be connecting to
#define MAXDATASIZE 256 // max number of bytes we can get at once
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
struct stat mystat;
char* pmap;
int fdin;
if (argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
FD_SET(sockfd,&master);
FD_SET(0,&master);
for(;;)
{
read_fds = master;
if (select(sockfd+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
if (FD_ISSET(0,&read_fds))
{
//reads messege form user
scanf ("%[^\n]%*c", buf);
//if it is upload
if (0 == strncmp(buf,"/upload ",8)) {
//sendFile(sockfd,buf);
if (send(sockfd,buf ,strlen(buf),0) == -1)
perror("send");
if ((fdin = open(buf + 8,O_RDONLY)) < 0)
perror("open");
fstat (fdin,&mystat);
pmap = mmap (0, mystat.st_size, PROT_READ, MAP_SHARED, fdin, 0);
if (send(sockfd,pmap,(int)mystat.st_size,0))
perror("send");
close(fdin);
}
else if (send(sockfd,buf,strlen(buf),0) == -1)
perror("send");
}
else if (FD_ISSET(sockfd, &read_fds))
{
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
{
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("%s\n",buf);
}
}
close(sockfd);
return 0;
}
server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define PORT "6667" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
struct User *usersPtr = NULL; //ptr to users list
struct File *filesPtr = NULL; //ptr to files list
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
char msg[256]; //string to deal with message
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
char path[100],data[1000];
int fdout;
char* pmap;
int pid;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,(struct sockaddr *)&remoteaddr,&addrlen);
if (newfd == -1) {
perror("accept");
}
else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",inet_ntop(remoteaddr.ss_family,get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),newfd);
}
}
else {
// handle data from a client
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
}
else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else { // we got some data from a client
buf[nbytes] = '\0';
else if (0 == strncmp(buf,"/upload ",8)) {
sprintf(path,"%s",basename(buf + 8));
if((fdout = open (path, O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0)
perror("open");
if ((nbytes = recv(i,data,sizeof data,0)) == -1)
perror("recive");
data[nbytes] = '\0';
lseek (fdout, nbytes - 1, SEEK_SET);
write (fdout, "", 1);
pmap = mmap (0, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0);
memcpy (pmap , data, nbytes);
close(fdout);
}
else {
for(j = 0; j <= fdmax; j++) { // send to everyone!
if (FD_ISSET(j, &master)) {
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)
return 0;
}
Sockets are streams of data, sending 30 bytes then 100 will not result in the server reading 30 bytes then 100, it could read any amount up to 130 bytes. If your file is short, you will probably get the command and the data in one recv.
To get it working for short files containing text (not the complete solution).
The command must be \0 terminated for strlen to give the right size in the server so in the client change:
if (send(sockfd,buf ,strlen(buf),0) == -1)
to
if (send(sockfd,buf ,strlen(buf)+1,0) == -1)
The server will get the command and data together in buf, so in the server change:
char path[100],data[1000];
to
char path[100], *data;
and use data to point to the data in the buffer by changing:
if ((nbytes = recv(i,data,sizeof data,0)) == -1)
perror("recive");
data[nbytes] = '\0';
to
buf[nbytes] = '\0';
data = buf + strlen(buf) + 1;
nbytes = strlen(data);
The full solution is to loop receiving data and to look for the /upload command in stream. You could still look for the \0 as a delimiter, but you should then send the length of the file and use this to determine the number of bytes to read. This will cope with binary files too.
How can I get a request a webpage using an URL instead of a ip address?
when I tried to use a url I got a error message.
I am using an android application called C4droid and it does not have as many libraries. many other examples needed libraries that it did not have, Can i do this using the libraries included in C4droid?
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int socket_handle;
struct sockaddr_in socket_detials;
char *input_buffer;
char *pinput_buffer;
ssize_t bytes_received;
ssize_t bytes_sent;
char *phttpget;
char *httpget = "GET / HTTP/1.0\r\n" "Host: www.google.com\r\n" "\r\n";
phttpget = httpget;
bytes_sent = 0;
input_buffer = malloc(1024);
if (input_buffer == NULL)
{
printf("Sorry, couldnt allocate memory for input buffer\n");
return -1;
}
memset(input_buffer, 0, 1024);
memset(&socket_detials, 0, sizeof(struct sockaddr_in));
socket_handle = socket(AF_INET, SOCK_STREAM, 0);
if (socket_handle == -1)
{
printf("Could not create socket\n");
return -1;
}
socket_detials.sin_family = AF_INET;
socket_detials.sin_addr.s_addr = inet_addr("74.125.224.72"); //Google's ip adress
socket_detials.sin_port = htons(80);
if (connect
(socket_handle, (struct sockaddr *)&socket_detials,
sizeof(struct sockaddr)) == -1)
{
printf("Couldnt connect to server\n");
return -1;
}
printf("Attempting to send %zd bytes to server\n", strlen(httpget));
for (;;)
{
bytes_sent = send(socket_handle, phttpget, strlen(phttpget), 0);
if (bytes_sent == -1)
{
printf("An error occured sending data\n");
return -1;
}
if (httpget + strlen(httpget) == phttpget)
break;
phttpget += bytes_sent;
}
for (;;)
{
bytes_received = recv(socket_handle, input_buffer, 1023, 0);
if (bytes_received == -1)
{
printf("An error occured during the receive procedure \n");
return 0;
}
if (bytes_received == 0)
break;
pinput_buffer = input_buffer + bytes_received;
*pinput_buffer = 0;
printf("%s", input_buffer);
}
printf("\nFinished receiving data\n");
return 0;
}