Related
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;
}*/
I wrote a simple protocol in which Im able to exchange files / text messages between client and server. If the client send a text to server, server should simply echo it back. On the other hand, when client send a special command (for example, SEND_TXT_FILE) server should receive a file uploaded by client to the server.
I almost got it work. However, there's still problem with sending files. Sever does not save the whole file, it only creates it and disconnects.
Here's the protocol:
CLIENT ---------- text1 ----------> SERVER
CLIENT <---------- text1 ---------- SERVER
CLIENT ---------- text2 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
CLIENT ---------- SENDTXTFILE ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- FILENAME ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- file content ----------> SERVER
CLIENT <--------- FILE_UPLOADED --------- SERVER
CLIENT ---------- text3 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
How can I solve this?
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
int recv_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "wb");
int total = 0, b = 0;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp != NULL)
{
while (recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
}
printf("Received byte: %d\n",total);
if (b < 0)
perror("Receiving");
fclose(fp);
}
else
{
perror("File");
}
close(sockfd);
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char buffer[BUFFER_SIZE], filename[BUFFER_SIZE];
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *)&client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (1)
{
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
if ((res = strcmp(buffer, "SENDFILE_TXT\n") == 0))
{
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(filename, '\0', BUFFER_SIZE);
strcpy(filename, buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
recv_txt_file(client_fd, BUFFER_SIZE, filename);
}
else
{
printf("FROM CLIENT: %.*s\n", BUFFER_SIZE, buffer);
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
}
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const char *buf, size_t len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
long int get_file_size(char filename[])
{
// opening the file in read mode
FILE *fp = fopen(filename, "r");
// checking if the file exist or not
if (fp == NULL) {
printf("File Not Found!\n");
return -1;
}
fseek(fp, 0L, SEEK_END);
// calculating the size of the file
long int res = ftell(fp);
// closing the file
fclose(fp);
return res;
}
int send_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "r");
int b;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp == NULL)
{
perror("Error while openning file");
return 0;
}
while ((b = fread(buffer, 1, BUFFER_SIZE, fp)) > 0){
send_all(sockfd, buffer, BUFFER_SIZE);
memset(buffer, '\0', BUFFER_SIZE);
}
fclose(fp);
return 1;
}
int main(int argc, char **argv)
{
char *hostname = "127.0.0.1";
int port = 6666;
char buffer[BUFFER_SIZE], fname[BUFFER_SIZE];
int sockfd, err, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (1)
{
memset(buffer, BUFFER_SIZE, '\0');
memset(fname, BUFFER_SIZE, '\0');
printf("> ");
if (!fgets(buffer, BUFFER_SIZE, stdin))
break;
if (strstr(buffer, "SENDFILE_TXT") != NULL)
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
memset(buffer, BUFFER_SIZE, '\0');
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
if ((res = strcmp(buffer, "OK") == 0))
{
printf("-------FROM SERVER: %s-------\n", buffer);
printf("Give filename> ");
memset(fname, BUFFER_SIZE, '\0');
if (!fgets(fname, BUFFER_SIZE, stdin))
break;
if (send_all(sockfd, fname, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
fname[strlen(fname)-1] = 0;
printf("----%s----\n", fname);
send_txt_file(sockfd, BUFFER_SIZE, fname);
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
else
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
}
close(sockfd);
return 0;
}
The way you are using send_txt_file() and recv_txt_file() to transfer a file is not correct.
On the server side:
When a client connects, the server waits for the client to send exactly BUFFER_SIZE (1024) bytes for each command, no more, no less (which is a waste of bandwidth for short commands). When a SENDFILE_TXT command is received, the server reads the filename from the client (which will cause a buffer overflow if the filename is exactly BUFFER_SIZE bytes in length), and then calls recv_txt_file().
recv_txt_file() attempts to read from the client in a loop, reading in exactly BUFFER_SIZE chunks. However, the while loop being used is coded incorrectly. It is checking the return value of recv_all() for failure, not for success. The != check needs to be changed to == instead. And also, the b variable that is being used to increment total, and tell fwrite() how many bytes to write, is never set to any value other than 0. It needs to be set to BUFFER_SIZE instead, since that is how many bytes have actually been read if recv_all() returns 1.
However, even if the while loop were coded properly, the transfer would still not operate properly, because it requires the file to be sent in even multiples of BUFFER_SIZE. If the file is not an even multiple in size, recv_all() will end up waiting for data that the client does not send, until an error occurs on the socket.
Also, recv_txt_file() is closing the connection after the transfer is finished. It should not do that, as that will prevent the client from being able to send further commands after sending a file. The client is not closing its end of the connection after sending a file, so the server should not be closing its end after receiving a file.
On the client side:
When the client sends a SENDFILE_TXT command and gets an acknowledgement back, it calls send_txt_file(), which reads the file in a loop, sending it to the server in BUFFER_SIZE sized chunks. If the file size is not an even multiple of BUFFER_SIZE, the last block sent will just waste bandwidth and send random garbage to the server. Also, you are ignoring the return value of send_all() to break the loop if an error occurs on the socket.
The client should send the actual file size to the server before sending the file data. The server can then read that size value first so it knows when to stop reading.
That being said, try something more like the following:
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const void *buf, int len)
{
ssize_t n;
const char *pbuf = (const char*) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Client write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
if (send_all(sockfd, &value, sizeof(value)) < 0)
return -1;
return 0;
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void * buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_uint64(int sockfd, uint64_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohll(*value); // <-- use any implementation of your choosing...
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int recv_txt_file(int sockfd)
{
char *filename;
uint64_t filesize;
if (recv_str(sockfd, &filename) <= 0)
return -1;
int res = recv_uint64(sockfd, &filesize);
if (res <= 0)
{
free(filename);
return -1;
}
printf("-------FROM CLIENT: %s-------\n", filename);
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
perror("Could not create file");
free(filename);
send_str(sockfd, "NO");
return 0;
}
free(filename);
// optional: pre-size the new file to the specified filesize...
if (send_str(sockfd, "OK") < 0)
return -1;
char buffer[BUFFER_SIZE];
int b;
uint64_t total = 0;
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
res = recv_all(sockfd, buffer, b);
if (res <= 0)
{
fclose(fp);
return -1;
}
if (fwrite(buffer, b, 1, fp) < 1)
{
perror("Could not write to file");
fclose(fp);
send_str(sockfd, "ERROR");
return -1;
}
total += b;
filesize -= b;
}
fclose(fp);
printf("Received bytes: %lu\n", total);
if (send_str(sockfd, "OK") < 0)
return -1;
return 1;
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char filename[BUFFER_SIZE], *cmd;
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *) &server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (recv_str(client_fd, &cmd) > 0)
{
printf("-------FROM CLIENT: %s-------\n", cmd);
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (recv_txt_file(client_fd) < 0)
break;
}
else
{
if (send_str(client_fd, cmd) < 0)
{
free(cmd);
break;
}
}
free(cmd);
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const void *buf, size_t len)
{
ssize_t n;
const char *pbuf = (const char *) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Server write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
return send_all(sockfd, &value, sizeof(value));
}
int send_uint64(int sockfd, uint64_t value)
{
value = htonll(value); // <-- use any implementation of your choosing...
return send_all(sockfd, &value, sizeof(value));
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void *buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Server read failed");
else
printf("Server disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int send_txt_file(int sockfd, const char *filename)
{
char *resp;
int res;
long int filesize;
char buffer[BUFFER_SIZE];
int b;
FILE* fp = fopen(filename, "rb");
if (fp == NULL)
{
printf("Could not open file!\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (filesize < 0)
{
fclose(fp);
return 0;
}
if (send_str(sockfd, "SENDFILE_TXT") < 0)
{
fclose(fp);
return -1;
}
if (send_str(sockfd, filename) < 0)
{
fclose(fp);
return -1;
}
if (send_uint64(sockfd, filesize) < 0)
{
fclose(fp);
return -1;
}
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
if (strcmp(resp, "OK") != 0)
{
free(resp);
fclose(fp);
return 0;
}
free(resp);
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
b = fread(buffer, 1, b, fp);
if (b < 1)
{
fclose(fp);
return -1;
}
if (send_all(sockfd, buffer, b) < 0)
{
fclose(fp);
return -1;
}
filesize -= b;
}
fclose(fp);
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
free(resp);
return 0;
}
int prompt(const char *text, char **input)
{
*input = NULL;
size_t size = 0;
printf("%s> ", text);
ssize_t len = getline(input, &size, stdin);
if (len < 0)
return len;
if ((*input)[len-1] == '\n')
{
--len;
(*input)[len] = '\0';
}
return len;
}
int main()
{
char *hostname = "127.0.0.1";
int port = 6666;
char *cmd, *resp, *fname;
size_t size;
ssize_t len;
int sockfd, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *) &server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (prompt("", &cmd) >= 0)
{
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (prompt("Give filename", &fname) < 0)
break;
if (send_txt_file(sockfd, fname) < 0)
break;
free(fname);
}
else
{
if (send_str(sockfd, cmd) < 0)
{
free(cmd);
close(sockfd);
return 1;
}
if (recv_str(sockfd, &resp) <= 0)
break;
printf("FROM SERVER: %s\n", resp);
free(resp);
}
free(cmd);
}
close(sockfd);
return 0;
}
regarding:
int total = 0, b = 0;
and
while(recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
the total and b never move from their initial value of 0. So 0 bytes are written to the output file.
I have a client and server, the server is setup this way:
int listenS = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
bind(listenS, (struct sockaddr*)&s, sizeof(s));
listen(listenS, QUEUE_LEN);
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
//......
(There are tests I just removed them to make the code more readable)
The client is just:
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{ //......
I get a connection, and everything is working great, the server recv a message the first time I send it from the client, but when I try to send another message to the server the server wont block the recv call and get nothing (returning the buffer size, not 0)
Here is the client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#define PORT 0x0da2
#define IP_ADDR 0x7f000001
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, size_t length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
int main(int argc, char** argv)
{
if (argc > 1)
{
if ((strcmp(argv[1], "list-files") != 0) &&
(strcmp(argv[1], "upload-file") != 0) &&
(strcmp(argv[1], "download-file") != 0) &&
(strcmp(argv[1], "search") != 0))
{
perror("The arguments are incorrect.");
}
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("connect");
return 1;
}
printf("Successfully connected.\n");
char sendBuffer[MAX_BUFFER_SIZE];
int lenOfArgv = strlen(argv[1]);
int sendBufferIndex = 0;
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[1][i];
}
if (argc == 3)
{
sendBuffer[sendBufferIndex++] = ' ';
int lenOfArgv = strlen(argv[2]);
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[2][i];
}
}
sendBuffer[sendBufferIndex] = 0;
// + 1 for terminating null
if (send_all(sock, sendBuffer, strlen(sendBuffer) + 1) < 0)
{
perror("send buffer to server failed");
return 1;
}
if(strcmp(argv[1], "download-file") == 0)
{
char sizeBuffer[256];
recv(sock, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
FILE* recievedFile = fopen(argv[2], "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(sock, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}
}
else if(strcmp(argv[1], "upload-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "%s", argv[2]);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(sock, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(sock, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else
{
char someBuffer[MAX_BUFFER_SIZE];
// nrecv is the number of bytes that we recieved
if ((nrecv = recv(sock, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("%s\n", someBuffer);
}
close(sock);
return 0;
}
else
{
perror("The arguments are incorrect.");
}
}
here is the server code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <math.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#define PORT 0x0da2 // 3490
#define IP_ADDR 0x7f000001 // 127.0.0.1
#define QUEUE_LEN 20
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, int length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
void list_dir()
{
DIR * directory;
struct dirent* dir;
directory = opendir(".");
if (directory)
{
while ((dir = readdir(directory)) != NULL)
{
printf("%s\n", dir->d_name); // /home/text.txt, text.txt
// get filesize (in bytes0 with dir->d_name
}
}
}
void list_files(char* buffer, int withBytes = 0)
{
DIR* d;
struct dirent* dir;
d = opendir("data");
int bufferIndex = 0;
while((dir = readdir(d)) != NULL)
{
char tempFilename[256] = "data/";
int tempIndex = 5;
char* scan = dir->d_name;
while(*scan)
{
tempFilename[tempIndex++] = *scan;
buffer[bufferIndex++] = *scan++;
}
tempFilename[tempIndex] = 0;
struct stat st = {0};
stat(tempFilename, &st);
int fileSize = st.st_size;
if(withBytes == 1)
{
// Adding file size to the buffer
bufferIndex += sprintf(&buffer[bufferIndex], " %d bytes", fileSize);
}
buffer[bufferIndex++] = '\n';
}
buffer[bufferIndex] = 0;
closedir(d);
}
int main(void)
{
int listenS = socket(AF_INET, SOCK_STREAM, 0);
if (listenS < 0)
{
perror("socket");
return 1;
}
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (bind(listenS, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("bind");
return 1;
}
if (listen(listenS, QUEUE_LEN) < 0)
{
perror("listen");
return 1;
}
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
struct stat st = {0};
if(stat("data", &st) == -1)
{
mkdir("data", 0700);
}
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
if (newfd < 0)
{
perror("accept");
return 1;
}
int pid = fork(); // creating new thread
if (pid == 0)
{
close(listenS); // duplicate=> thats why we need to close the socket
char someBuffer[MAX_BUFFER_SIZE];
int nrecv;
if ((nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("Message recieved: %s\n", someBuffer);
// Here we read the command the argument and split them
// into seperate variables
char command[256];
char argument[256];
int commandHasBeenSet = 0;
char* token = strtok(someBuffer, " ");
while(token != NULL)
{
if(commandHasBeenSet == 0)
{
strcpy(command, token);
commandHasBeenSet = 1;
}
else
{
strcpy(argument, token);
}
token = strtok(NULL, " ");
}
if (strcmp(command, "list-files") == 0)
{
char buffer[MAX_BUFFER_SIZE];
list_files(buffer, 1);
if (send_all(newfd, buffer, strlen(buffer) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
else if (strcmp(command, "upload-file") == 0)
{
printf("Uploading file %s\n", argument);
char sizeBuffer[256];
recv(newfd, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
printf("Downloading to %s", filePath);
FILE* recievedFile = fopen(filePath, "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(newfd, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}else
{
perror("Failed to get file size for download\n");
}
}
else if (strcmp(command, "download-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(newfd, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(newfd, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("Server sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else if (strcmp(command, "search") == 0)
{
char buffer[MAX_BUFFER_SIZE];
char result[MAX_BUFFER_SIZE];
int resultIndex = 0;
list_files(buffer);
result[0] = 0;
char tempBuffer[MAX_BUFFER_SIZE];
strcpy(tempBuffer, buffer);
token = strtok(tempBuffer, "\n");
while(token != NULL)
{
char* scanToken = token;
char* scanArgument = argument;
int found = 1;
while(*scanToken && *scanArgument)
{
if(*scanToken++ != *scanArgument++)
{
found = 0;
break;
}
}
if(found == 1)
{
if(resultIndex > 0)
{
result[resultIndex++] = ' ';
}
strcpy(&result[resultIndex], token);
resultIndex += strlen(token);
result[resultIndex] = 0;
}
token = strtok(NULL, "\n");
}
if (send_all(newfd, result, strlen(result) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
close(newfd);
exit(0);
}
else
close(newfd);
}
close(listenS);
return 0;
}
If you run the server, and then run the client with commands like:
./client list-files
./client download-file test.txt
it will work fine, the client will receive messages from the server and vice versa.
The problem occurs when I try to run:
./client upload-file test.txt
which is essentially the same as download-file command, just copied and pasted to the server from the client (same logic, should work the same), except it doesn't.
Specifically the program fail at line 175 of the server (recv(newfd, sizeBuffer, 256, 0);), it gets 0 instead of the value the client is sending it.
Any idea what I am missing?
(I tried searching online but didn't find anything)
TCP is a streaming protocol. There is no message boundaries, and server's recvs do not correspond to client's sends.
The client sends the command with
send_all(sock, sendBuffer, strlen(sendBuffer) + 1)
OTOH, the server tries to receive it with
nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0))
recv does not care whether the stream contains a '\0' or not. It blindly waits for MAX_BUFFER_SIZE bytes to come in. Some (valuable) data sent by the client is in someBuffer just past the command, but ignored by the server.
The server must parse the reply more diligently. For that you likely need a more elaborated protocol (e.g. prefix each string with its length).
You are assuming the stream socket will preserve your message boundaries. It will not. So, you're sending something like:
upload-file filename\0
NNN\0\0\0...\0 [256 bytes containing filesize]
<content of filename>
So probably the first recv you do on the server side (of MAX_BUFFER_SIZE bytes) is receiving not just the command string and the filename, but also the file size block and the entire file content. That is, you've already received it all and it's sitting in someBuffer. Your initial command string is null-terminated so you would not be aware of the rest of it unless you check nrecv. Hence, your next recv call gets end-of-file because the client is finished and has closed its end of the connection.
To send defined records over TCP, you need to know exactly how many bytes are expected at each point, and receive exactly those (or be prepared to parse the received data). See also https://stackoverflow.com/a/47440054/1076479
I'm playing around with C although I am fairly new to it. to find out how threads and locks work, connections work, within an IRC.
However I have come across and error, and I'm not sure how to fix it.
Bellow are the errors I get.
sample.c: In function ‘clientThreadEntry’:
sample.c:343:5: warning: passing argument 1 of ‘connectionMain’ makes pointer from integer without a cast [enabled by default]
connectionMain(t->sock);
^
sample.c:216:5: note: expected ‘struct client_thread *’ but argument is of type ‘int’
int connectionMain(struct client_thread *t) {
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
struct client_thread {
pthread_t thread;
int thread_id;
int sock;
char nickname[32];
int state;
#define DEAD 1
#define ALIVE 2
int user_command_seen;
int user_has_registered;
time_t timeout;
char line[1024];
int line_len;
int next_message;
};
/**
Allocate static structure for all client connections.
*/
#define MAX_CLIENTS 50
struct client_thread threads[MAX_CLIENTS];
/**
Number of connections we have open right now.
*/
int clientCount = 0;
pthread_rwlock_t message_log_lock = PTHREAD_RWLOCK_INITIALIZER;
#define MAX_MESSAGES 10000
char messageLogRecipients[MAX_MESSAGES];
char *messageLog[MAX_MESSAGES];
int messageCount = 0;
int messageAppend(char *recipient, char *message) {
/*
If used up all message space, exit.
*/
if (messageCount >= MAX_MESSAGES) return -1;
// Append message.
pthread_rwlock_wrlock(&message_log_lock);
messageLogRecipients[messageCount] = strdup(recipient);
message[messageCount] = strdup(message);
messageCount++;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int messageRead(struct client_thread *t) {
pthread_rwlock_rdlock(&message_log_lock);
int i;
for (i = t->next_message + 1; i < messageCount; i++) {
}
t -> next_message = messageCount;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int create_listen_socket(int port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) return -1;
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) == -1) {
close(sock);
return -1;
}
if (ioctl(sock, FIONBIO, (char *) &on) == -1) {
close(sock);
return -1;
}
/* Bind it to the next port we want to try. */
struct sockaddr_in address;
bzero((char *) &address, sizeof (address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &address, sizeof (address)) == -1) {
close(sock);
return -1;
}
if (listen(sock, 20) != -1) return sock;
close(sock);
return -1;
}
int accept_incoming(int sock) {
struct sockaddr addr;
unsigned int addr_len = sizeof addr;
int asock;
if ((asock = accept(sock, &addr, &addr_len)) != -1) {
return asock;
}
return -1;
}
int read_from_socket(int sock, unsigned char *buffer, int *count, int buffer_size,
int timeout) {
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, NULL) | O_NONBLOCK);
int t = time(0) + timeout;
if (*count >= buffer_size) return 0;
int r = read(sock, &buffer[*count], buffer_size - *count);
while (r != 0) {
if (r > 0) {
(*count) += r;
break;
}
r = read(sock, &buffer[*count], buffer_size - *count);
if (r == -1 && errno != EAGAIN) {
perror("read() returned error. Stopping reading from socket.");
return -1;
} else usleep(100000);
// timeout after a few seconds of nothing
if (time(0) >= t) break;
}
buffer[*count] = 0;
return 0;
}
/**
Function to check is a user has registered yet or not.
*/
int registrationCheck(struct client_thread *t) {
if (t->user_has_registered)
return -1;
if (t->user_command_seen && t->nickname[0]) {
// User has met registration requirements.
t->user_has_registered = 1;
t->timeout = 60;
char msg[8192];
snprintf(msg, 1024, ":myircServer.com 001 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 002 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 003 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 004 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 253 %s : Unknown connections\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 254 %s : Channels formed\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 255 %s : I have ??? clients and ??? servers.\n", t->nickname);
write(t->sock, msg, strlen(msg));
return 0;
}
return -1;
}
int connectionMain(struct client_thread *t) {
int sock = t->sock;
char nickname[8192];
char msg[1024];
snprintf(msg, 1024, ":myircserver.com 020 * :Greetings, from the IRC server\n");
write(sock, msg, strlen(msg));
unsigned char buffer[8192];
int length = 0;
t->timeout = 5;
int timeOfLastData = time(0);
while (1) {
length = 0;
messageRead(t);
read_from_socket(sock, buffer, &length, 8192, 1);
if (length > 0)
{
timeOfLastData = time(0);
}
if (length == 0 && ((time(0) - timeOfLastData) >= t->timeout)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out.. See ya!\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}
buffer[length] = 0;
char channel[8192];
int r = sscanf((char *) buffer, "JOIN %s", channel);
if (r == 1) {
if (!t->user_has_registered) {
snprintf(msg, 1024, ":myircserver.com 241 * :JOIN command sent before registration\n");
write(sock, msg, strlen(msg));
}
}
r = sscanf((char *) buffer, "NICK %s", nickname);
if (r == 1) {
/**
check and saw a nickname from the client.
Need to handle what to do next.
Checks if the nickname provided is less than 1 or greater than 30.
if it is, then send and error since it is invalid and we cannot deal with nicknames of these sizes.
if this isn't in place we would be trying to pass a string to big or too small to our nickname variable.
*/
if (strlen(nickname) > 30 || strlen(nickname) < 1) {
snprintf(msg, 1024, "ERROR :Inviliad nickname: Nickname too short or too long.\n");
write(sock, msg, strlen(msg));
} else {
/**
Nickname is a valid length!
copy nickname to thread variable nickname.
*/
strcpy(t->nickname, nickname);
registrationCheck(t);
}
}
/**
Saw USER command if a NICK has been provided. Then mark connection.
As registered. and send client greeting messages.
If this isnt here, then we will not beable to handle USER command or the like.
*/
if (!strncasecmp("USER ", (char *) buffer, 5)) {
if (t->nickname) {
/**
Nickname has been provided and user has been registered.
*/
t->user_command_seen = 1;
registrationCheck(t);
}
}
/**
Checks if the messaged parsed is correct.
Checks nickname, length and the response are correct and send the correct respose code.
Checks of the PRIVMSG has at least 10 bytes of data to be used.
Needed to see if there is data, and if it is sent before registration.
*/
if (!strncasecmp("PRIVMSG", (char *) buffer, 7)) {
if (!t->user_has_registered){
snprintf(msg, 1024, ":myircserver.com 241 * :PRIVMSG command sent before registration\n");
write(sock, msg, strlen(msg));
} else {
// Client is registered, so handle the message.
char recipient[1024];
char message [1024];
if (sscanf((char *) buffer, "PRIVMSG %s :%[^\n]", recipient, message) == 2){
messageAppend(recipient, message);
} else {
// msg is wrongly formed, error.
snprintf(msg, 1024, ":myircserver.com 461 %s :Wrongly formed PRIVMSG command sent.\n", t->nickname);
write(sock, msg, strlen(msg));
}
}
}
/**
* Client left, we must check and close this or we will get a SIGPIPE error that will kill program.
* Send an error statement back to user so they know what is going on.
*/
if (!strncasecmp("QUIT", (char *) buffer, 4)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out (see ya!)\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}}
close(sock);
return 0;
}
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
int handleConnection(int sock) {
printf("WE GET HERE\n");
int i;
for (i = 0; i < clientCount; i++) {
if (threads[i].state == DEAD) {
break;
}}
if (i > MAX_CLIENTS) {
close(sock);
return 1;
}
// clear out client structure, set up for threads.
bzero(&threads[i], sizeof (struct client_thread));
// store file descriptor into thread array.
threads[i].sock = sock;
threads[i].state = ALIVE;
threads[i].thread_id = i;
if (pthread_create(&threads[i].thread, NULL, clientThreadEntry, &threads[i]))
{
close(sock);
return 1;
}
if (i == clientCount) clientCount++;
return 0;
}
int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
if (argc != 2) {
fprintf(stderr, "usage: sample <tcp port>\n");
exit(-1);
}
int master_socket = create_listen_socket(atoi(argv[1]));
fcntl(master_socket, F_SETFL, fcntl(master_socket, F_GETFL, NULL)&(~O_NONBLOCK));
while (1) {
int client_sock = accept_incoming(master_socket);
if (client_sock != -1) {
// Got connection -- do something with it.
handleConnection(client_sock);
} else {usleep(10000);}
}
}
You have a compilation warning. Your function connectionMain takes a client_thread pointer:
int connectionMain(struct client_thread *t)
{
But you are calling it with a sock integer:
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
Presumably you want to call it like so:
connectionMain(t);
That would at least fix the compilation warning.
I am using unix domain sockets to send open file descriptor between different processes. Unix domain sockets work fine but when i used sendmsg to send file descriptor something wierd happened. The function returns just after sendmsg and sendmsg not returning anything. And then recvmsg is giving Invalid Argument error. Here is my code.
Client (sending the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int send_err(int fd, int errcode, const char *msg);
int send_fd(int fd, int fd_to_send);
int main(int argc, char const *argv[])
{
int fd_to_send;
if((fd_to_send = open("vi",O_RDONLY)) < 0)
printf("vi open failed");
struct sockaddr_un address;
int socket_fd, nbytes;
char buffer[256];
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("connect() failed\n");
return 1;
}
nbytes = snprintf(buffer, 256, "hello from a client");
write(socket_fd, buffer, nbytes);
nbytes = read(socket_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM SERVER: %s\n", buffer);
//sending the file descriptor
printf("From send_fd %d \n",send_fd(socket_fd,fd_to_send));
close(socket_fd);
exit(0);
}
int send_err(int fd, int errcode, const char *msg)
{
int n;
if ((n = strlen(msg)) > 0)
if (write(fd, msg, n) != n) /* send the error message */
return(-1);
if (errcode >= 0)
errcode = -1; /* must be negative */
if (send_fd(fd, errcode) < 0)
return(-1);
return(0);
}
int send_fd(int fd, int fd_to_send)
{
int temp;
struct iovec iov[1];
struct msghdr msg;
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (fd_to_send < 0) {
msg.msg_control = NULL;
msg.msg_controllen = 0;
buf[1] = -fd_to_send; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CONTROLLEN;
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
*(int *)CMSG_DATA(cmptr) = fd_to_send; /* the fd to pass */
buf[1] = 0; /* zero status means OK */
}
buf[0] = 0; /* null byte flag to recv_fd() */
printf("before sendmsg \n");
if (temp = sendmsg(fd, &msg, 0) != 2)
{
printf("inside sendmsg condition %d\n",temp);
return(-1);
}
printf("after sendmsg %d\n",temp);
return(0);
}
Server (recv the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#define MAXLINE 10
/* size of control buffer to send/recv one file descriptor */
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));
ssize_t errcheckfunc(int a,const void *b, size_t c);
int connection_handler(int connection_fd);
int main(int argc, char const *argv[])
{
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length;
pid_t child;
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
unlink("./demo_socket");
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("bind() failed\n");
return 1;
}
if(listen(socket_fd, 5) != 0)
{
printf("listen() failed\n");
return 1;
}
while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
{
connection_handler(connection_fd);
int fd_to_recv;
fd_to_recv = recv_fd(socket_fd,&errcheckfunc);
if(read(fd_to_recv,msgbuf,5) < 0)
printf("message read failed");
printf("message received:%s\n",msgbuf);
close(connection_fd);
}
close(socket_fd);
unlink("./demo_socket");
return 0;
}
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t))
{
int newfd, nr, status;
char *ptr;
char buf[MAXLINE];
struct iovec iov[1];
struct msghdr msg;
status = -1;
for ( ; ; )
{
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
if ((nr = recvmsg(fd, &msg, 0)) < 0)
{
printf("recvmsg errrrror %d %d %s\n",nr,errno,strerror(errno));
//perror("recvmsg errrrror");
} else if (nr == 0)
{
perror("connection closed by server");
return(-1);
}
/*
* See if this is the final data with null & status. Null
* is next to last byte of buffer; status byte is last byte.
* Zero status means there is a file descriptor to receive.
*/
for (ptr = buf; ptr < &buf[nr]; )
{
if (*ptr++ == 0)
{
if (ptr != &buf[nr-1])
perror("message format error");
status = *ptr & 0xFF; /* prevent sign extension */
if (status == 0)
{
if (msg.msg_controllen != CONTROLLEN)
perror("status = 0 but no fd");
newfd = *(int *)CMSG_DATA(cmptr);
} else
{
newfd = -status;
}
nr -= 2;
}
}
if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
return(-1);
if (status >= 0) /* final data has arrived */
return(newfd); /* descriptor, or -status */
}
}
ssize_t errcheckfunc(int a,const void *b, size_t c)
{
return 0;
}
int connection_handler(int connection_fd)
{
int nbytes;
char buffer[256];
nbytes = read(connection_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM CLIENT: %s\n", buffer);
nbytes = snprintf(buffer, 256, "hello from the server");
write(connection_fd, buffer, nbytes);
return 0;
}
output of client:
MESSAGE FROM SERVER: hello from the server
before sendmsg
Not even inside sendmsg condition and after sendmsg prints?
Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
Your call to recvmsg() is trying to receive data from a listening socket rather than a connected socket, because your main loop is passing socket_fd to recv_fd() rather than connection_fd. – Matthew Slattery