无法通过 UDP 连接发送结构

时间:2021-04-11 07:00:56

标签: c sockets network-programming udp

我正在尝试实现一个客户端服务器架构,其中客户端一次发送一个文件 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'。它还放置了字符数组和其他整数/布尔值的大小

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

1 个答案:

答案 0 :(得分:4)

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

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 位,具体取决于所使用的操作系统和编译器。那么即使是 32 位整数在不同的处理器上也可能不同,即大端与小端。根据编译器的不同,结构中的对齐方式也可能在两侧不同。

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