What I would like to achieve: One client know multiple IP addresses of different servers which hosts exactly same files.
Client should be able to receive infos about files/folders hosted on server (permissions, size and so on). I'm stuck since I moved buffer from char* to struct to have control about corrupted packet. Right now my client doesn't reach server using send() function.
It worked perfectly sith sendto and recvfrom but as far I've readed, they are not usable with structs, only with char* buffers.
Thank you!
Server.c
// Server side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h> //Used to get file metadata
#include <time.h> //Used to format time-based metadata
#include <dirent.h> //User to list directory's content
#define PORT 8080
#define MAXLINE 65527 // 65535 is MAX and other 8 bytes are reserved for header
struct SPacket {
int len;
char msg[MAXLINE - sizeof(int)];
};
void GetFileMetadata(const char* szPath, char* Out)
{
if (access(szPath, F_OK) != 0 )
snprintf(Out, MAXLINE, "%s", szPath);
else
{
struct stat res;
stat(szPath, &res);
int length = 0;
length = snprintf(Out length, MAXLINE, "Filename:\t\t%s\n", szPath);
length = snprintf(Out length, MAXLINE, "Filesize:\t\t%d\n", res.st_size);
length = snprintf(Out length, MAXLINE, "Permissions:\t\t");
length = snprintf(Out length, MAXLINE, (S_ISDIR(res.st_mode)) ? "d" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IRUSR) ? "r" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IWUSR) ? "w" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IXUSR) ? "x" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IRGRP) ? "r" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IWGRP) ? "w" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IXGRP) ? "x" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IROTH) ? "r" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IWOTH) ? "w" : "-");
length = snprintf(Out length, MAXLINE, (res.st_mode & S_IXOTH) ? "x\n" : "-\n");
length = snprintf(Out length, MAXLINE, "Owner:\t\t\t%d\n", res.st_gid);
length = snprintf(Out length, MAXLINE, "Last status change:\t%s", ctime(&res.st_ctime));
length = snprintf(Out length, MAXLINE, "Last file access:\t%s", ctime(&res.st_atime));
length = snprintf(Out length, MAXLINE, "Last file modification:\t%s", ctime(&res.st_mtime));
DIR *d;
struct dirent *dir;
d = opendir(szPath);
if (d != NULL)
{
length = snprintf(Out length, MAXLINE, "Content of %s:\t", szPath);
while ((dir = readdir(d)) != NULL)
{
length = snprintf(Out length, MAXLINE, "%s\t", dir->d_name);
}
closedir(d);
}
length = snprintf(Out length, MAXLINE, "\n");
}
}
int main()
{
int sockfd;
struct SPacket toSend, toRecv;
struct sockaddr_in servaddr, cliaddr;
// Creating socket file descriptor
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// Bind the socket with the server address
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
int len, n;
len = sizeof(cliaddr); //len is value/resuslt
while(1)
{
n = recv(sockfd, &toRecv, sizeof(toRecv), 0);
toRecv.msg[n] = '\0';
// printf("[DEBUG] Client request: %s\n", InMsg);
GetFileMetadata(toRecv.msg, toSend.msg);
// printf("[DEBUG] Server answer: %s (len = %d)\n", OutMsg, strlen(OutMsg));
send(sockfd, &toSend, sizeof(toSend), 0);
memset(&toRecv, 0, MAXLINE); //We're preparing buffer for next request
memset(&toSend, 0, MAXLINE); //We're preparing buffer for next request
}
return 0;
}
Client.c
// Client side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h> //So retarded, need this for bool functions because we compile with C99 standard
#define PORT 8080
#define MAXLINE 65527 // 65535 is MAX and other 8 bytes are reserved for header
struct SPacket {
int len;
char msg[MAXLINE - sizeof(int)];
};
bool isValidIpAddress(char *ipAddress)
{
struct sockaddr_in sa;
int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr)); //Convert IP address from string to binary
return result != 0;
}
bool ReadIPsList(char** iplist, int maxLines, int maxLen, int* ipCount)
{
FILE *fp = fopen("Servers.txt", "r");
size_t len = 255;
char *line = malloc(sizeof(char) * len);
if (fp == NULL)
{
printf("Can't open file Servers.txt!\n");
return false;
}
while(fgets(line, len, fp) != NULL)
{
line[strlen(line)-1]='\0'; //Remove newline
if (*ipCount < maxLines && strlen(line) < maxLen && isValidIpAddress(line))
{
iplist[*ipCount] = (char *)malloc(16);
strncpy(iplist[*ipCount], line, maxLen);
printf("\"%s\" added to IP list\n", iplist[*ipCount]);
}
else
{
printf("Invalid IP address (%s), line (%d).\n", line, *ipCount);
return false;
}
(*ipCount) = 1;
}
if (*ipCount == 0)
{
printf("Servers.txt is empty ?!?\n");
return false;
}
fclose(fp);
free(line);
printf("\n");
return true;
}
bool AskAppendNewServer(char** iplist, int maxLines, int maxLen, int* ipCount)
{
printf("Do you want to add a new server? (Y/N)\n");
char ans = getchar();
if (ans == 'y' || ans == 'Y')
{
if (*ipCount >= maxLines)
{
printf("ERROR! Maximum number of IP addresses has been reached!\n");
return false;
}
FILE *fp = fopen("Servers.txt", "a");
if (fp == NULL)
{
printf("Can't open file Servers.txt!\n");
return false;
}
bool is_ok = false;
char IP[16 1];
do {
printf("\nEnter IP address: ");
scanf("%s", IP);
if (isValidIpAddress(IP))
{
is_ok = true;
fprintf(fp, "%s\n", IP);
iplist[*ipCount] = (char *)malloc(16);
strncpy(iplist[*ipCount], IP, maxLen);
printf("\"%s\" added to IP list\n", iplist[*ipCount]);
}
else
printf("ERROR! %s is not a valid IP address!", IP);
} while (is_ok != true);
fclose(fp);
(*ipCount) = 1;
return true;
}
return false;
}
int main()
{
int sockfd, ipCount = 0, selIp = 0;
struct SPacket toSend, toRecv;
struct sockaddr_in servaddr;
char* ipList[255];
if (ReadIPsList(ipList, 255, 16, &ipCount) == 0)
return -1;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(ipList[selIp]); //Trying 1st server then we move to a new one if this become unavailable
int n, len;
//If we don't receive any answer in less than 1 second then is a problem..
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0) {
perror("Error");
}
connect(sockfd, (const struct sockaddr *) &servaddr, sizeof(servaddr));
while(1)
{
char c;
while((c= getchar()) != '\n' && c != EOF); // Remove any unwanted character from stdin (scanf from AskAppendNewServer created this issue)
printf("Enter filename:\t");
fgets(toSend.msg, MAXLINE, stdin);
toSend.msg[strcspn(toSend.msg, "\n")] = '\0'; //Remove newline
toSend.len = strlen(toSend.msg);
if (strlen(toSend.msg) == 0)
continue;
send(sockfd, &toSend, sizeof(toSend), 0);
if (n = recv(sockfd, &toRecv, sizeof(toRecv), 0) > 0)
{
if (strcmp(toSend.msg, toRecv.msg) == 0) // Yep, this file does not exists on host.
{
if (AskAppendNewServer(ipList, 255, 16, &ipCount) == true)
{
selIp = ipCount - 1; //We append always at the end of the array
printf("Server (%s) has been added successfully. Now you can send your request from it.\n", ipList[selIp]);
servaddr.sin_addr.s_addr = inet_addr(ipList[selIp]);
}
}
else
printf("%s\n", toRecv.msg);
// printf("[DEBUG] Received msg len: %d\n", strlen(toRecv.msg));
}
else
{
int counter = 1, foundValidServer = 0;
while (counter < ipCount && foundValidServer == 0)
{
printf("Timeout reached :: server (%s) not available, trying next server available (%s)\n", ipList[selIp], ipList[(selIp 1) % ipCount]);
counter = 1;
selIp = (selIp 1) % ipCount;
servaddr.sin_addr.s_addr = inet_addr(ipList[selIp]); // Try next available server
sendto(sockfd, (const char *)toSend.msg, strlen(toSend.msg), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
if (n = recvfrom(sockfd, (char *)toRecv.msg, MAXLINE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len) > 0)
{
if (strcmp(toSend.msg, toRecv.msg) == 0)
AskAppendNewServer(ipList, 255, 16, &ipCount);
else
printf("%s\n", toRecv.msg);
foundValidServer = 1;
}
}
}
memset(&toSend.msg, 0, MAXLINE); //We're preparing buffer for next request
memset(&toRecv.msg, 0, MAXLINE); //We're preparing buffer for next request
}
free(ipList); //No memory leaks, please.
close(sockfd);
return 0;
}
CodePudding user response:
It doesn't work with struct because it isn't meant to. You need to provide a pointer to the char buffer (and not the buffer itself) and the length of the buffer in a separate argument. See this recv()
documentation.
If what you want is sending struct data with a socket, try making a string representation within the buffer, and then parse it on receive.