C UDP RecvFrom最终悬挂在远程连接上

c++ udp recvfrom eventually hangs over remote connection

本文关键字:远程连接 UDP RecvFrom      更新时间:2023-10-16

我正在用C 编写客户端服务器UDP应用程序。当客户端和服务器在同一台计算机上运行时,该应用程序正常工作,但是当我在AWS中的EC2实例上运行客户端时,服务器会收到4个消息,然后开始挂在recvfrom上。客户端正在以500个字节的块发送文件,服务器将其写入文件。服务器接收每个块后,该服务器将发送ack。客户端的超时时间为2秒,如果它在超时之前未接收ack,则它会重新安排上一个块。这是我的客户端代码

void Sender::send_file(const char* host) {
cout << "in send filen";
int sock = 0;
struct sockaddr_in serv_addr;
size_t length = 8;
unsigned char res_buf[8] = {0};
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    std::cout << "n Socket creation error n";
    exit(EXIT_FAILURE);
}
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// copy 0 into serv_addr members
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(host);
size_t file_size = buffer.size();
cout << "file size " << to_string(file_size) << "n";
size_t file_pos = 0;
int next_iteration = 1;
bool establish = true;
while(file_pos < file_size - 1) {
    cout << "waiting for lockn";
    std::unique_lock<std::mutex> lock(mrun);
    main_ready.wait(lock, [&next_iteration, this]{return next_iteration == current_iteration; });
    lock.unlock();
    cout << "acquired lockn";
    ++next_iteration;
    Segment segment;
    vector<unsigned char> req_str;
    if(!establish) {
        if(file_pos + mss < file_size) {
            std::vector<unsigned char> file_chunk(buffer.begin() + file_pos, buffer.begin() + file_pos + mss);
            segment.data = file_chunk;
            file_pos += mss;
        }
        else {
            std::vector<unsigned char> file_chunk(buffer.begin() + file_pos, buffer.end());
            segment.data = file_chunk;
            file_pos = file_size;
        }
        segment.seq_num = file_pos - mss;
        req_str = segment.to_bytes(false, mss);
    }
    else {
        segment.seq_num = 0;
        req_str = segment.to_bytes(true, mss);
    }
    bool is_ack = false, timed_out = false;
    std::chrono::high_resolution_clock::time_point start_time, end_time;
    long duration = 0;
    unsigned char* req = req_str.data();
    size_t num_bytes = req_str.size();
    unsigned int len = 0;
    while(!is_ack) {
        cout << "about to send data " << std::to_string(segment.seq_num) << " bytes " << std::to_string(num_bytes) << "n";
        ssize_t send_res = sendto(sock, req, num_bytes, 0, (const struct sockaddr *) &serv_addr, sizeof(serv_addr));
        printf("Sent to %s:%dn", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
        cout << "send res " << to_string(send_res) << " " << strerror(errno) << "n";
        start_time = std::chrono::high_resolution_clock::now();
        bzero(res_buf, length);
        cout << "about to read ackn";
        ssize_t block_sz = recvfrom(sock, res_buf, length, 0, (struct sockaddr *) &serv_addr, &len);
        cout << "read ackn";
        end_time = std::chrono::high_resolution_clock::now();
        duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
        cout << "block sz " << std::to_string(block_sz) << " duration " << std::to_string(duration) << std::endl;
        if(duration > timeout * 1000)
            timed_out = true;
        if(timed_out || errno == ETIMEDOUT || block_sz < 0) {
            std::cout << "time out/error occurred on read " << to_string(errno) << "n";
            cout << strerror(errno) << "n";
            bzero(res_buf, length);
            timed_out = false;
            continue;
        }
        //add_nulls(res_buf);
        is_ack = read_response(segment.seq_num, res_buf);
        cout << "is ack " << is_ack << "nn";
        bzero(res_buf, length);
    }
    establish = false;
    lock.lock();
    if(--worker_count == 0) {
        lock.unlock();
        iteration_complete.notify_one();
    }
}
cout << "Out of while loopn";
close(sock);
files_sent.push_back(1);
}

它每次都完全发送508个字节,除了仅10个字节的第一部分。我打印每次迭代后的sendto结果,看起来总是像这样

Sent to 52.0.0.0:7735
send res 508 Undefined error: 0

***更新 ***一旦服务器开始挂在recvfrom,客户端时间出去等待ack,客户就会开始不断打印以下错误,因为它会继续重新延伸块。

Sent to 52.0.0.0:7735
send res 508 Resource temporarily unavailable

服务器代码看起来像

 void Receiver::download_file() {
int server_fd;
struct sockaddr_in serv_addr, cli_addr;
int segment_size = 20;
umap seg_map;
ACK_Segment::init_static();
std::random_device rd;  //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_real_distribution<> dis(0.0, 1.0);
if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
}
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&cli_addr, 0, sizeof(cli_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
// bind socket to port
//bzero(buffer, segment_size);
ssize_t block_sz = 0;
unsigned int len = sizeof cli_addr;
bool is_set_mss = true;
if(bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}
//unsigned char* buffer = bvec.data();
vector<unsigned char> bvec(segment_size);
unsigned char* buffer = bvec.data();
bool is_resized = false;
while(true) {
    cout << "about to recvfromn";
    printf("Receiving from %s:%dn", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
    cout << "segment size " << to_string(segment_size) << " len " << to_string(len) << "n";
    block_sz = recvfrom(server_fd, buffer, segment_size, 0, ( struct sockaddr *) &cli_addr, &len);
    cout << "block size " << to_string(block_sz) << "errno " << to_string(errno) << " " << strerror(errno) << "n";
    if(block_sz > 0) {
        cout << "Received data - bytes " << std::to_string(block_sz) << "n";
        printf("Received from %s:%dn", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
        double rand_val = dis(gen);
        if(is_set_mss) {
            seg_map = read_segment(bvec, block_sz, true);
            if(seg_map["checksum_valid"] == "true") {
                segment_size = mss + HEADER_SIZE;
                is_resized = true;
            }
        }
        else {
            if(block_sz == 0) {
                cout << "received no datan";
                break;
            }
            seg_map = read_segment(bvec, block_sz, false);
        }
        if(seg_map["checksum_valid"] == "false")
            cout << "invalid checksumn";
        else if(is_set_mss) {
            vector<unsigned char> ack = get_ack();
            //remove_nulls(ack);
            unsigned char* ack_bytes = ack.data();
            cout << "about to send ackn";
            sendto(server_fd, ack_bytes, 8, 0, (const struct sockaddr *) &cli_addr, len);
            //send(new_socket, ack, 8, 0);
            is_set_mss = false;
        }
        else {
            if(seg_map["in_order"] == "true") {
                string data = seg_map["data"];
                std::ofstream out;
                out.open(file_name, std::ios_base::app);
                out << data;
                out.close();
        cout << "Done writing file chunkn";
            }
            vector<unsigned char> ack = get_ack();
            unsigned char* ack_bytes = ack.data();
            sendto(server_fd, ack_bytes, 8, 0, (const struct sockaddr *) &cli_addr, len);
        }
        if(is_resized) {
            bvec.resize(segment_size);
            is_resized = false;
        }
        bvec.clear();
        buffer = bvec.data();
    cout << "Cleared buffernn";
    }
    cout << "out of inner whilen";
}
}

此打印

Receiving from 168.0.0.0:18832
segment size 508 len 16
block size 508 errno 0 Success

但是,它仅在4次打印,然后在客户端继续发送508字节时挂在recvfrom上,并且该段的大小在recvfrom中保持在508。为什么Recvfrom会开始工作,然后突然停止?

update

我将其浇水到我可以编写的最简单的UDP客户端服务器,这是客户端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <string>
using std::vector;
using std::cout;
using std::to_string;
int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    size_t length = 11;
    unsigned char res_buf[11] = {0};
    if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        std::cout << "n Socket creation error n";
        exit(EXIT_FAILURE);
    }
    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
    // copy 0 into serv_addr members
    memset(&serv_addr, '0', sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(7735);
    serv_addr.sin_addr.s_addr = inet_addr("52.71.240.175");
    vector<unsigned char> req(20);
    for(int i = 0; i < 20; i++)
        req[i] = 'A';
    unsigned char* req_str = req.data();
    size_t num_bytes = req.size();
    unsigned int len = sizeof serv_addr;
    while(true) {
        cout << "about to send datan";
        ssize_t send_res = sendto(sock, req_str, num_bytes, 0, (const struct sockaddr *) &serv_addr, sizeof(serv_addr));
        bzero(res_buf, length);
        cout << "about to read ackn";
        ssize_t block_sz = recvfrom(sock, res_buf, length, 0, (struct sockaddr *) &serv_addr, &len);
        cout << "received " << res_buf <<  "n";
    }
}

这是服务器:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <string>
using std::vector;
using std::cout;
using std::to_string;
int main() {
    int server_fd;
    struct sockaddr_in serv_addr, cli_addr;
    int segment_size = 20;
    memset(&serv_addr, 0, sizeof(serv_addr));
    memset(&cli_addr, 0, sizeof(cli_addr));
    if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(7735);
    unsigned int len = sizeof cli_addr;
    if(bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    vector<unsigned char> bvec(segment_size);
    unsigned char* buffer = bvec.data();
    while(true) {
        cout << "about to receive fromn";
        ssize_t block_sz = recvfrom(server_fd, buffer, segment_size, 0, ( struct sockaddr *) &cli_addr, &len);
        if(block_sz > 0) {
            printf("Received from %s:%dn", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
            char* ack_bytes = "ack message";
            cout << "about to send ackn";
            sendto(server_fd, ack_bytes, 11, 0, (const struct sockaddr *) &cli_addr, len);
        }
    }
}

发生相同的行为,服务器接收了4条消息,但是在第五封信中,它只是挂在recvfrom中。我尝试关闭并重新打开服务器上每个迭代的插座,这无效。

我必须在接收到服务器的每个ACK后关闭并重新打开客户端的套接字。我不需要在服务器端关闭并重新打开。我不确定这是否解决了我面临的问题的根本原因,但它允许我的应用程序正确运行。