Reputation: 43
I am trying to implement a client server architecture where client sends a file 1 line at a time and server receives and acknowledges it. It is implemented using stop-n-wait protocol. The issue is that the client is only able to send the string but not the entire struct packet. I know this because I orinted the integer values of the packet and they are coming wrong whereas the string part is correct on the server side.
server-side code The data.seq_no and data.size are all coming garbage values. That is the issue
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#define PORT 4444
#define MAX_PACKET_SIZE 200
#define MAXPENDING 5
typedef struct packet{
char data[MAX_PACKET_SIZE];
int size;
int seq_no;
bool last_pkt;
bool type;
}PKT;
char db[2000];
void die(char *s){
printf("%s\n", s);
exit(0);
}
int main(){
int s;
struct sockaddr_in servr, cli;
int slen=sizeof(servr);
if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
die("creating");
printf("socket created successfully\n");
memset(&servr, 0, sizeof(servr));
servr.sin_family = AF_INET;
servr.sin_addr.s_addr = htonl(INADDR_ANY);
servr.sin_port = htons(PORT);
if(bind(s, (struct sockaddr*)&servr, sizeof(servr))==-1)
die("connection failed");
printf("connected to the server\n");
int curr = 0;
PKT data;
while(1){
if(recvfrom(s, &data, sizeof(data), 0, (struct sockaddr*)&cli, &slen)==-1)
die("recvfrom()");
printf("RECV PKT: seq no. = %d, size = %d\n", data.seq_no, data.size);
printf("%d\n", data.size);
for(int i = 0; i<data.size; ++i)
db[curr+i]==data.data[i];
curr+=data.size;
int flag = rand()%10;
if(flag!=0){
data.size = 0;
data.type = 0;
if(sendto(s, &data, sizeof(data), 0, (struct sockaddr*)&cli, slen)==-1)
die("sendto()");
printf("SENT ACK: seq no = %d\n", data.seq_no);
}
else{
printf("DROP DATA: seq no = %d, size = %d\n", data.seq_no, data.size);
}
}
close(s);
}
client-side code generate_packet generates correct packet. It generates packet with 1 line of text till '\n'. Also it puts size of char array and other ints/bools as well
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#define PORT 4444
#define MAX_PACKET_SIZE 100
typedef struct packet{
int size;
int seq_no;
bool last_pkt;
bool type; //true is data and false is ack
char data[MAX_PACKET_SIZE];
}PKT;
void die(char *s){
printf("%s\n", s);
exit(1);
}
PKT generate_packet(int start){
FILE *fp;
fp = fopen("input.txt", "r");
fseek(fp, start, SEEK_SET);
PKT packet;
int count = 0;
char ch;
while((ch = fgetc(fp))!=EOF && ch!='\n')
packet.data[count++] = ch;
packet.data[count++] = '\0';
packet.size = count;
if(ch==EOF)
packet.last_pkt=true;
else
packet.last_pkt=false;
packet.type=true;
packet.seq_no = start;
fclose(fp);
return packet;
}
int main(){
int s;
struct sockaddr_in servr, cli;
int slen=sizeof(servr),retval;
struct timeval tv;
fd_set set;
if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
die("creating");
printf("socket created successfully\n");
memset(&servr, 0, sizeof(servr));
servr.sin_family = AF_INET;
servr.sin_addr.s_addr = inet_addr("127.0.0.1");
servr.sin_port = htons(PORT);
struct packet* data_pkt = malloc(sizeof(struct packet));
data_pkt->last_pkt=false;
int start = 0;
while(data_pkt->last_pkt!=true){
(*data_pkt) = generate_packet(start); //get the packet here
printf("packet generated, %d, %d, %d\n", data_pkt->seq_no, data_pkt->last_pkt, data_pkt->size);
start+=data_pkt->size;
if(sendto(s, data_pkt, sizeof(*data_pkt), 0, (struct sockaddr*)&servr, slen)==-1)
die("sendto()");
printf("SENT DATA, seq_no = %d, size = %d\n", data_pkt->seq_no, data_pkt->size);
while(1){
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&set);
FD_SET(s, &set);
retval = select(s+1, &set, NULL, NULL, &tv);
if (retval == -1)
die("error");
else if (retval == 0){
if(sendto(s, data_pkt, sizeof(*data_pkt), 0, (struct sockaddr*)&servr, slen)==-1)
die("sendto()");
printf("RESENT DATA, seq_no = %d, size = %d\n", data_pkt->seq_no, data_pkt->size);
}
else{
if(recvfrom(s, data_pkt, sizeof(*data_pkt), 0, (struct sockaddr*)&servr, &slen)==-1)
die("receiving error");
printf("RECEIVED ACK: seq no = %d\n", data_pkt->seq_no);
break;
}
}
}
close(s);
}
Upvotes: 0
Views: 104
Reputation: 123320
The packet structures used for sending and receiving are clearly different. On the server:
char data[MAX_PACKET_SIZE];
int size;
int seq_no;
bool last_pkt;
bool type;
Whereas on the client
int size;
int seq_no;
bool last_pkt;
bool type; //true is data and false is ack
char data[MAX_PACKET_SIZE];
As can be seen, the order of the fields in the struct is completely different. But the transport simply sends the bytes making up the struct and does not marshal the data in a specific way or unmarshals these. When doing this the structures must be exactly the same.
The same does not only mean the order, but also the size of the various data types. An int
might be 32 bit or 64 bit depending on the OS and compiler used. Then even a 32 bit integer might be different on different processors, i.e. big-endian vs. little-endian. And depending on the compiler the alignment in the struct might be different too on both sides.
Thus, unless it is guaranteed that the same OS, compiler, CPU architecture ... is used on both sides it is better to explicitly marshal the data into a clearly defined serialized format on the sender side and unmarshal it on the receiver side.
Upvotes: 4