使用 C 和 UDP 实现 Stop and wait,什么原因导致 'resource temporarily unavailable'?

Implementing Stop and wait with C & UDP, and what does cause 'resource temporarily unavailable'?

我正在使用 c 和 udp 套接字编程实现停止和等待。为了模拟这个协议,我写了两段代码。

这是我的 server.c 文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>


typedef struct packet{
    char data[1024];
}Packet;

typedef struct frame{
    int frame_kind; //ACK:0, SEQ:1 FIN:2
    int sq_no;
    int ack;
    Packet packet;
}Frame;

int main(int argc, char** argv)
{
    int sockfd;
    int clilen;
    int state;
    int n;
    int sum;
    int recv_result;

    int frame_id = 0;

    Packet packet;
    Frame frame;
    Frame recv_frame;
    char buffer[1024] = "";

    struct timeval tv;

    struct sockaddr_in serveraddr, clientaddr;

    FILE* infile = fopen(argv[2], "r");
    if(argc != 3)
    {
        perror("error! usage: $./server <PORT> filename\n");
        exit(1);
    }
    if(infile == NULL)
    {
        printf("error! failed to open the file.\n");
        exit(0);
    }

    clilen = sizeof(clientaddr);
    sockfd = socket(AF_INET, SOCK_DGRAM, 0); //using diagram instead of stream-- UDP
    if(sockfd < 0)
    {
        perror("socket error: ");
        exit(0);
    }

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(atoi(argv[1])); //set port number 9999

    //set socket option -- timeout is 100000 microseconds

    tv.tv_sec = 1;
    tv.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));

    state = bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(state == -1)
    {
        perror("bind error: ");
        exit(0);
    }

    do{
        n = fscanf(infile, "%s", buffer);

        strcpy(packet.data, buffer);
        memcpy(&(frame.packet), &packet, sizeof(Packet));
        frame.frame_kind = 1; //SEQ
        frame.sq_no = frame_id;
        frame.ack = 0;
        printf("flag2");
        while(1)
        {
            sendto(sockfd, &frame, sizeof(frame), 0, (struct sockaddr*)&clientaddr, (socklen_t)clilen);
            printf("Frame %d sent\n", frame_id);
            recv_result = recvfrom(sockfd, &recv_frame, sizeof(recv_frame), 0,(struct sockaddr*)&clientaddr, &clilen );

            if(recv_result > 0 && recv_frame.sq_no == 0 && recv_frame.ack == frame_id){
                printf("Ack %d received\n", recv_frame.sq_no);
                break;
            }else{
                printf("Frame %d time expired\n", frame_id);
            }
        }
        frame_id++;
    }while(n > 0);

    printf("finished\n");
    return 0;
}

这是我的 client.c 文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

typedef struct packet{
    char data[1024];
}Packet;

typedef struct frame{
    int frame_kind; //ACK:0, SEQ:1 FIN:2
    int sq_no;
    int ack;
    Packet packet;
}Frame;

int main(int argc, char *argv[])
{
    int sock;
    int str_len;
    socklen_t adr_sz;
    FILE* outfile = fopen("output.txt", "w");
    struct sockaddr_in serv_adr, from_adr;
    Frame ackframe, recv_frame;
    int frame_id;
    int recv_result;
    struct timeval tv;

    if(argc!=3){
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    sock=socket(PF_INET, SOCK_DGRAM, 0);   
    if(sock==-1)
    {
        printf("error! failed to open the socket\n");
        exit(1);
    }

    memset(&serv_adr, 0, sizeof(serv_adr));
    adr_sz = sizeof(serv_adr);
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));

    tv.tv_sec = 1;
    tv.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));

    frame_id = 0;
    while(1)
    {
        recv_result = recvfrom(sock, &recv_frame, sizeof(recv_frame), 0,(struct sockaddr*)&serv_adr, &adr_sz);
        perror("error: ");
        printf("data %s\n", recv_frame.packet.data);
        printf("recv result %d\n", recv_result);
        printf("recv frame=%d, frame_kind = %d\n", recv_frame.sq_no, recv_frame.frame_kind);
        if(recv_result > 0 && recv_frame.frame_kind == 1 && recv_frame.sq_no==frame_id){
            printf("Frame %d received\n", recv_frame.sq_no);
            fprintf(outfile, "%s", recv_frame.packet.data);
            ackframe.frame_kind = 0;
            ackframe.ack = recv_frame.sq_no + 1;
            printf("Ack %d sent\n", ackframe.ack);
            sendto(sock, &recv_frame, sizeof(recv_frame), 0,(struct sockaddr*)&serv_adr, adr_sz);


            break;
        }else{
            printf("Frame %d time expired\n", frame_id);
        }
    }   
    close(sock);
    fclose(outfile);
    return 0;
}

服务器需要 3 个参数:./server <port> <test.txt>

客户端还需要 3 个参数:./client <ip> <port>

执行了,但是client的recvfrom总是returns -1.

我阅读了一些相同主题的问题,但无法从我的代码中找出错误。有什么方法可以用 recvfrom & sendto 修复我的代码吗?或者我需要使用 select 功能吗? (我只会与一台服务器和一台客户端通信。)

"client" 和 "server" 程序包含一些可疑代码。首先让我们以 "server" 为例,它使用 clientaddr 调用 sendto,而您 尚未初始化 。谁知道您尝试将数据包发送到哪里。这实际上会导致 未定义的行为,因为结构的内容是 不确定的

然后让我们以客户端为例,在该客户端中,您在未本地绑定的套接字上调用 recvfrom。你传递给recvfrom的地址结构指针由recvfrom函数填充,但是里面的数据并没有用在其他任何地方。

可能还有其他问题,但这两个很突出。