无法通过 UDP 连接发送结构

not able to send struct over a UDP connection

我正在尝试实现客户端服务器架构,其中客户端一次发送一个文件 1 行,服务器接收并确认它。它是使用 stop-n-wait 协议实现的。问题是客户端只能发送字符串而不能发送整个结构数据包。我知道这是因为我对数据包的整数值进行了排序,但它们出错了,而字符串部分在服务器端是正确的。

服务器端代码 data.seq_no 和 data.size 都是垃圾值。就是这个问题

#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);    
}

客户端代码 generate_packet 生成正确的数据包。它生成包含 1 行文本直到 '\n' 的数据包。它还放置了 char 数组和其他 ints/bools 的大小

#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++] = '[=11=]';

    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);    
}

用于发送和接收的数据包结构明显不同。在服务器上:

char data[MAX_PACKET_SIZE];
int size;
int seq_no;
bool last_pkt;
bool type;

而在客户端

int size;
int seq_no;
bool last_pkt;
bool type;    //true is data and false is ack
char data[MAX_PACKET_SIZE];

可以看出,struct中字段的顺序是完全不同的。但是传输只是发送构成结构的字节,并不以特定方式编组数据或解组这些数据。执行此操作时,结构必须完全相同。

相同的不仅是顺序,还有各种数据类型的大小。 int 可能是 32 位或 64 位,具体取决于 OS 和使用的编译器。然后即使是 32 位整数在不同的处理器上也可能不同,即大端与小端。根据编译器的不同,结构中的对齐方式在两侧也可能不同。

因此,除非保证双方使用相同的 OS、编译器、CPU 体系结构...,否则最好将数据显式编组为明确定义的序列化格式在发送端并在接收端解组。