'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:

  1. outfile was missing an fopen call, so the server fwrite would segfault
  2. After the fix, the output file would get extraneous zeros because the length given to fwrite was the whole size rather than the returned value of recvfrom
  3. Actually, the correct length is the length value 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.

  1. I changed the code to do the fread directly into the packet struct's data field.
  2. I changed the data function to take a pointer arg to the packet rather than returning one. This eliminates some unnecessary copying
  3. I added an extra zero length data packet to indicate EOF
  4. I changed the primary data read/write loop to look at the returned length from fread rather 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.):

  1. send file to server (data is filename)
  2. send file to client (data is filename)
  3. file data (sent by client and/or server and length of 0 indicates EOF)
  4. 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