0

我正在用 C++ 编写客户端服务器 udp 应用程序。当客户端和服务器在同一台机器上运行时,应用程序可以正常工作,但是当我在笔记本电脑上运行客户端并在 AWS 中的 ec2 实例上运行服务器时,服务器会收到 4 条消息,然后开始挂起recvfrom。客户端以 500 字节的块发送文件,服务器将这些文件写入文件。ack服务器在收到每个块后发送一个。客户端有 2 秒的超时,如果它在超时之前没有收到ack它会重新发送前一个块。这是我的客户代码

void Sender::send_file(const char* host) {
cout << "in send file\n";
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 lock\n";
    std::unique_lock<std::mutex> lock(mrun);
    main_ready.wait(lock, [&next_iteration, this]{return next_iteration == current_iteration; });
    lock.unlock();
    cout << "acquired lock\n";
    ++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:%d\n", 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 ack\n";
        ssize_t block_sz = recvfrom(sock, res_buf, length, 0, (struct sockaddr *) &serv_addr, &len);
        cout << "read ack\n";
        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 << "\n\n";
        bzero(res_buf, length);
    }
    establish = false;
    lock.lock();
    if(--worker_count == 0) {
        lock.unlock();
        iteration_complete.notify_one();
    }
}
cout << "Out of while loop\n";
close(sock);
files_sent.push_back(1);
}

它每次发送正好 508 个字节,除了第一个段只有 10 个字节。我在每次迭代后打印 sendto 的结果,看起来总是这样

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

* **Update *** 一旦服务器开始挂起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 recvfrom\n";
    printf("Receiving from %s:%d\n", 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:%d\n", 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 data\n";
                break;
            }
            seg_map = read_segment(bvec, block_sz, false);
        }
        if(seg_map["checksum_valid"] == "false")
            cout << "invalid checksum\n";
        else if(is_set_mss) {
            vector<unsigned char> ack = get_ack();
            //remove_nulls(ack);
            unsigned char* ack_bytes = ack.data();
            cout << "about to send ack\n";
            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 chunk\n";
            }
            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 buffer\n\n";
    }
    cout << "out of inner while\n";
}
}

这打印

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

但是它只打印了 4 次,然后它挂在 recvfrom 上,而客户端继续发送 508 个字节,段大小保持在 508 in recvfrom。为什么 recvfrom 开始工作然后突然停止?

更新

我将其简化为我可以编写的最简单的 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 data\n";
        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 ack\n";
        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 from\n";
        ssize_t block_sz = recvfrom(server_fd, buffer, segment_size, 0, ( struct sockaddr *) &cli_addr, &len);
        if(block_sz > 0) {
            printf("Received from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
            char* ack_bytes = "ack message";
            cout << "about to send ack\n";
            sendto(server_fd, ack_bytes, 11, 0, (const struct sockaddr *) &cli_addr, len);
        }
    }
}

并且发生了同样的行为,服务器收到 4 条消息,但在第五条消息上它只是挂起recvfrom。我尝试在服务器上的每次迭代中关闭并重新打开套接字,但没有效果。

4

1 回答 1

1

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

于 2019-07-01T03:02:44.500 回答