我正在尝试实现一个客户端服务器架构,其中客户端一次发送一个文件 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);
}
答案 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 架构……否则最好在发送方将数据显式编组为明确定义的序列化格式,并在接收方解组一边。