'Writing content from struct into a file using fwrite
I am trying to write data from struct into a a file. For instance the data is found in newpackets.data. I am trying to write that into a file, but keep getting segfault. I have looked at other questions on here, but could not find a solution. Adding my code below. How can i solve this issue ? Thank you
to run server do ./server 8500 1
to run client do ./client 127.0.0.1 8500 500 3 ex.txt exout.txt
ex.txt will need to be made and then write random words into it. for instance can be like 85 characters
//server side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include "sys/un.h"
#define MAXLINE 32768
struct ackdata
{
int ack;
};
struct packetdata
{
int seqnumber;
int lenght;
char data[MAXLINE];
int type;
};
void dg_echo (int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main (int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
int SERV_PORT = atoi (argv[1]); ///This will grab the port to listen to
int dropc = atoi (argv[2]);
if (SERV_PORT <= 1023)
{
fprintf (stderr, "Error: Port given can not be less than 1023\n");
exit (1);
}
if (SERV_PORT > 65535)
{
fprintf (stderr,
"Error: Port given can not be more than 65535 or less than 1024\n");
exit (1);
}
sockfd = socket (AF_INET, SOCK_DGRAM, 0);
bzero (&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons (INADDR_ANY);
servaddr.sin_port = htons (SERV_PORT);
int bb = bind (sockfd, (struct sockaddr *) &servaddr, sizeof (servaddr));
if (bb < 0)
{
fprintf (stderr, "Binding failure");
}
dg_echo (sockfd, (struct sockaddr *) &cliaddr, sizeof (cliaddr));
close (sockfd);
return 0;
}
void
dg_echo (int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata newpackets;
char filename[MAXLINE];
recvfrom (sockfd, filename, MAXLINE, 0, pcliaddr, &len);
printf ("Name: %s", filename);
for (;;)
{
len = clilen;
//printf("HERe");
if ((outfile = fopen (filename, "a")) == NULL)
{
fprintf (stderr, "\nwriting File was not opened\n");
exit (1);
}
ssize_t rlen =recvfrom(sockfd, &newpackets, sizeof (newpackets), 0, pcliaddr, &len);
if (rlen ==-1){
printf ("failed recv");
return 1;
}
printf ("\nData: %s\n", newpackets.data);
//fprintf(outfile, "%d",newpackets.data );
fwrite(newpackets.data,rlen,1,outfile);
// sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}
Here is the client code:
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#define MAXLINE 4096
#define totaltries 3
struct packetdata data (int seqnumber, int lenght, char*data);
struct packetdata
{
int seqnumber;
int lenght;
char data[MAXLINE];
int type;
};
struct ackdata
{
int ack;
int type;
};
int main (int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize =0;
int winsz = atoi (argv[4]); //this is for the window size
int mtu = atoi (argv[3]); //for mtu
int SERV_PORT = atoi (argv[2]); // the port to listen to
char sendline[MAXLINE], recvline[MAXLINE];
struct packetdata forpackets;
if (mtu <= 0)
{
fprintf (stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit (1);
}
//you shouldnt use any ports from 0-1023
//1024-65535 are fines
if (SERV_PORT <= 1023)
{
fprintf (stderr, "Port Error: Port given can not be less than 1023\n");
exit (1);
}
if (SERV_PORT > 65535)
{
fprintf (stderr,
"Port Error: Port given can not be more than 65535 or less than 1024\n");
exit (1);
}
bzero (&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (SERV_PORT);
if (inet_pton (AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
fprintf (stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf (stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen (argv[5], "r")) == NULL)
{
fprintf (stderr, "\nReading File was not opened\n");
return 1;
}
//file name sending
char filename[MAXLINE];
strcpy(filename, argv[6]);
//printf("Filename: %c", filename);
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr * ) & servaddr, sizeof(servaddr));
//this is for calculations
fseek (infile, 0, SEEK_END);
countpackets = ftell (infile);
fseek (infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; //how many buffer is
if (countpackets % mtu)
{
calc++;
}
printf("\nCalc %f\n",calc);
char fordata[mtu];
//while(1){
while (calc>=sequencenumber && sequencenumber-base<=winsz){
fread(forpackets.data, sizeof(recvline), 1, infile);
//printf("%s", forpackets.data);
// strcpy(fordata, forpackets.data);
strncpy(fordata,(sequencenumber*mtu+forpackets.data),mtu);
printf("%s", fordata);
forpackets = data(sequencenumber,datasize, fordata );
printf("\nSending Packet: %d\n", sequencenumber);
if (sendto(sockfd, &forpackets, sizeof(forpackets), 0, (struct sockaddr * ) & servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}else{
sequencenumber++;
}
}
// }
close(sockfd);
fclose(infile);
}
struct packetdata data (int seqnumber, int lenght, char*data){
struct packetdata dataa;
dataa. seqnumber = seqnumber;
dataa. lenght = lenght;
dataa. type = 1;
memset(dataa.data, 0, MAXLINE);
strcpy(dataa.data, data);
return dataa;
}
Solution 1:[1]
Okay, just to recap the comments:
outfilewas missing anfopencall, so the serverfwritewould segfault- After the fix, the output file would get extraneous zeros because the length given to
fwritewas the whole size rather than the returned value ofrecvfrom - Actually, the correct length is the
lengthvalue inside the packet
You're on the right track. The struct you devised has the right things in it (e.g. sequence number, type, length, data field).
I've refactored the server and client code.
An additional issue is that the server didn't know when to stop writing the output file. There are several ways to do this. But, the one I chose was for the client to send an extra data packet with a length field of 0.
I've adjusted both client and server to do this.
Here is the refactored server code:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/un.h>
#define MAXLINE 32768
struct ackdata {
int ack;
};
struct packetdata {
int seqnumber;
int length;
char data[MAXLINE];
int type;
};
void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
#if 1
if (argc < 2) {
printf("usage: serv_port\n");
exit(1);
}
#endif
int SERV_PORT = atoi(argv[1]); // /This will grab the port to listen to
#if 0
int dropc = atoi(argv[2]);
#endif
if (SERV_PORT <= 1023) {
fprintf(stderr, "Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
int bb = bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (bb < 0) {
fprintf(stderr, "Binding failure");
}
dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
close(sockfd);
return 0;
}
void
dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata newpackets;
char filename[MAXLINE];
recvfrom(sockfd, filename, MAXLINE, 0, pcliaddr, &len);
#if 0
printf("Name: %s", filename);
#else
printf("Name: %s\n", filename);
outfile = fopen(filename,"w");
#endif
for (;;) {
len = clilen;
// printf("HERe");
if (recvfrom(sockfd, &newpackets, sizeof(newpackets), 0, pcliaddr, &len) == -1) {
printf("failed recv");
break;
}
printf("DEBUG: seqnumber=%d type=%d length=%d\n",
newpackets.seqnumber,newpackets.type,newpackets.length);
// end of file
if (newpackets.length == 0)
break;
//printf("\nData: %s\n", newpackets.data);
fwrite(newpackets.data, newpackets.length, 1, outfile);
// sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
if (outfile != NULL)
fclose(outfile);
}
And, here is the refactored client code. I cleaned it up a bit.
- I changed the code to do the
freaddirectly into the packet struct'sdatafield. - I changed the
datafunction to take a pointer arg to the packet rather than returning one. This eliminates some unnecessary copying - I added an extra zero length data packet to indicate EOF
- I changed the primary data read/write loop to look at the returned length from
freadrather than (pre)calculate the number of packets.
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#define MAXLINE 4096
#define totaltries 3
struct packetdata {
int seqnumber;
int length;
char data[MAXLINE];
int type;
};
struct ackdata {
int ack;
int type;
};
void
data(struct packetdata *pkt,int seqnumber, int length, char *data)
{
pkt->seqnumber = seqnumber;
pkt->length = length;
pkt->type = 1;
if ((length > 0) && (data != NULL))
memcpy(pkt->data, data, length);
}
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize = 0;
int winsz = atoi(argv[4]); // this is for the window size
int mtu = atoi(argv[3]); // for mtu
int SERV_PORT = atoi(argv[2]); // the port to listen to
char sendline[MAXLINE],
recvline[MAXLINE];
struct packetdata forpackets;
if (mtu <= 0) {
fprintf(stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit(1);
}
// you shouldnt use any ports from 0-1023
// 1024-65535 are fines
if (SERV_PORT <= 1023) {
fprintf(stderr, "Port Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Port Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
fprintf(stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen(argv[5], "r")) == NULL) {
fprintf(stderr, "\nReading File was not opened\n");
return 1;
}
// file name sending
char filename[MAXLINE];
strcpy(filename, argv[6]);
// printf("Filename: %c", filename);
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr *) &servaddr,
sizeof(servaddr));
// this is for calculations
fseek(infile, 0, SEEK_END);
countpackets = ftell(infile);
fseek(infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; // how many buffer is
if (countpackets % mtu) {
calc++;
}
printf("\nCalc %f\n", calc);
// while(1){
#if 0
while (calc >= sequencenumber && sequencenumber - base <= winsz) {
#else
while (1) {
#endif
ssize_t rlen = fread(forpackets.data, 1, sizeof(forpackets.data),
infile);
data(&forpackets,sequencenumber, rlen, NULL);
printf("\nSending Packet: %d rlen=%d\n", sequencenumber,rlen);
if (sendto(sockfd, &forpackets, sizeof(forpackets), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}
else {
sequencenumber++;
}
if (rlen == 0)
break;
}
// }
close(sockfd);
fclose(infile);
return 0;
}
Note that the client and server are "one-shots". That is, the server exits after receiving a given file.
Also, one change I didn't make was to send/receive the filename within a packet (with a different type field).
I'd change the programs to send only packet structs.
Then, the server could have an outer loop, to service new/different requests (e.g. it could receive a file, then send a file, etc.)
With various type fields (e.g.):
- send file to server (data is filename)
- send file to client (data is filename)
- file data (sent by client and/or server and length of 0 indicates EOF)
- close connection (sent by client to server)
This may be a bit of overkill for the scope of your project ...
The server could look at the IP address from recvfrom and keep some state in a "connection" struct, based on a match to the client IP address. For example, the connection struct could hold the outfile stream (instead of a single instance).
Then, the server could handle multiple/many client streams, interspersed on the same/single socket descriptor.
UPDATE:
Hey craig. TYSM. But is there a way, where the server is not one shot go? – juststruggle
It starts by adding my suggestion about sending the filename inside a packet with a type.
And, having the server loop do a switch (pkt.type).
I've added the ruminants of that in the code below. The client is very similar in function to before. The server will now loop after file transfer, waiting for other packets.
Note that there is a bunch of similar code between server and client. I moved some of that to a common.c file. Note that this is crude. I'd like to have a common.h for definitions and function prototypes and a common.c for common functions. But, IRL, I am being "summoned" :-) so I don't have time to fix that.
I moved the struct definition to that file. In the process, I notice that the size of the data element is different between client and server. That's a bug because type is at the end and would be elided/truncated because of the differing lengths of data
Here's common.c:
// common.c -- common code
#define MAXLINE 1024
#define totaltries 3
#ifdef DEBUG
#define dbgprt(_fmt...) printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
struct packetdata {
int type;
int seqnumber;
int length;
char data[MAXLINE];
};
struct ackdata {
int ack;
int type;
};
enum {
TYPE_TOSVR,
TYPE_TOCLI,
TYPE_DATA
};
void
pktprep(struct packetdata *pkt,int type,int seqnumber, int length, char *data)
{
pkt->type = type;
pkt->seqnumber = seqnumber;
pkt->length = length;
if ((length > 0) && (data != NULL))
memcpy(pkt->data, data, length);
dbgprt("pktprep: SENDPKT seq=%d type=%d length=%d\n",
seqnumber,type,length);
}
Here is server.c:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/un.h>
#include "common.c"
void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen);
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
#if 1
if (argc < 2) {
printf("usage: serv_port\n");
exit(1);
}
#endif
int SERV_PORT = atoi(argv[1]); // /This will grab the port to listen to
#if 0
int dropc = atoi(argv[2]);
#endif
if (SERV_PORT <= 1023) {
fprintf(stderr, "Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
int bb = bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (bb < 0) {
fprintf(stderr, "Binding failure");
}
dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
close(sockfd);
return 0;
}
void
dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
FILE *outfile = NULL;
socklen_t len;
char mesg[MAXLINE];
struct packetdata pkt;
char filename[MAXLINE];
for (;;) {
len = clilen;
// printf("HERe");
if (recvfrom(sockfd, &pkt, sizeof(pkt), 0, pcliaddr, &len) == -1) {
printf("failed recv");
break;
}
dbgprt("DEBUG: seqnumber=%d type=%d length=%d\n",
pkt.seqnumber,pkt.type,pkt.length);
switch (pkt.type) {
case TYPE_TOSVR:
dbgprt("TOSVR: %s\n", pkt.data);
if (outfile == NULL)
outfile = fopen(pkt.data,"w");
break;
case TYPE_DATA:
// end of file
if (pkt.length == 0) {
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
break;
}
//dbgprt("\nData: %s\n", pkt.data);
if (outfile != NULL)
fwrite(pkt.data, pkt.length, 1, outfile);
break;
}
}
if (outfile != NULL)
fclose(outfile);
}
And, here is client.c:
//client side
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include "common.c"
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
FILE *infile = NULL;
int countpackets;
float calc;
int tries = 0;
int base = 0;
int sequencenumber = 0;
int datasize = 0;
int winsz = atoi(argv[4]); // this is for the window size
int mtu = atoi(argv[3]); // for mtu
int SERV_PORT = atoi(argv[2]); // the port to listen to
char sendline[MAXLINE],
recvline[MAXLINE];
struct packetdata pkt;
if (mtu <= 0) {
fprintf(stderr, "MTU Error: mtu can not be less than or equal to 0\n");
exit(1);
}
// you shouldnt use any ports from 0-1023
// 1024-65535 are fines
if (SERV_PORT <= 1023) {
fprintf(stderr, "Port Error: Port given can not be less than 1023\n");
exit(1);
}
if (SERV_PORT > 65535) {
fprintf(stderr, "Port Error: Port given can not be more than 65535 or less than 1024\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
fprintf(stderr, "Inet Pton error");
return 1;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "\nsocket error\n");
return 1;
}
if ((infile = fopen(argv[5], "r")) == NULL) {
fprintf(stderr, "\nReading File was not opened\n");
return 1;
}
// file name sending
char filename[MAXLINE];
pktprep(&pkt,TYPE_TOSVR,sequencenumber++,strlen(argv[6]) + 1,argv[6]);
// printf("Filename: %c", filename);
#if 0
sendto(sockfd, filename, MAXLINE, 0, (struct sockaddr *) &servaddr,
sizeof(servaddr));
#else
sendto(sockfd, &pkt, sizeof(pkt), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr));
#endif
// this is for calculations
fseek(infile, 0, SEEK_END);
countpackets = ftell(infile);
fseek(infile, 0, SEEK_SET);
// printf("countpackets %d\n",countpackets);
// printf("\nMTU %d\n",mtu);
calc = countpackets / mtu; // how many buffer is
if (countpackets % mtu) {
calc++;
}
dbgprt("\nCalc %f\n", calc);
// while(1){
#if 0
while (calc >= sequencenumber && sequencenumber - base <= winsz) {
#else
while (1) {
#endif
ssize_t rlen = fread(pkt.data, 1, sizeof(pkt.data),
infile);
pktprep(&pkt,TYPE_DATA,sequencenumber, rlen, NULL);
if (sendto(sockfd, &pkt, sizeof(pkt), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
printf("failed sending");
return 1;
}
else {
sequencenumber++;
}
if (rlen == 0)
break;
}
// }
close(sockfd);
fclose(infile);
return 0;
}
Compile server.c and client.c with -DDEBUG to start.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
