使用pthread重新排列数据包

时间:2013-11-08 20:44:31

标签: c++ multithreading sockets pthreads fstream

所以这是我第一次在这里问一个问题,虽然我已经使用这个网站多年了!

我的问题有点棘手。我正在尝试开发一个客户端服务器应用程序来发送大文件,使用UDP和我自己的错误检查和流量控制。现在,我开发了一个功能齐全的服务器和客户端。客户端请求特定文件,服务器开始发送。文件被部分读入缓冲区,以避免每次发送数据包时都必须读取文件的一小部分,从而节省了处理时间。数据包由1400字节的实际数据+ 28字节的标头(序列号,确认号,校验和等)组成。

所以我有了基础知识,一个简单的停止等待协议。在发送下一个数据包之前发送数据包并接收确认。

为了能够实现更智能的流控制算法,对于只有一些窗口的启动器,我必须在两个不同的线程中运行发送部分和接收-ack部分。现在,我遇到了问题。这是我第一次使用线程,所以请耐心等待。

我的问题是从客户端端的数据包写入的文件已损坏。好吧,当使用一个小的jpg文件进行测试时,该文件只有50%的次数被破坏,当使用MP4文件进行测试时,它总是被破坏!所以我想也许线程会以某种方式重新排列数据包的发送顺序?我使用序列号,所以问题必须在将序列号分配给数据包之前发生......

我确信我分割文件的部分是正确的,也是我在客户端重新组装的部分,因为我在尝试实现线程之前已经测试过了。还应该注意的是,我将代码的确切发送部分复制到发送线程中,这在将其放入线程之前也完美地工作了。这也是我发布我的线程部分的原因。代码,因为这显然是造成问题的原因(并且因为项目的整个代码会占用空间)

我的发送线程代码:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;

static void *send_thread(void *){
if (file.is_open()) {
    while(!file.reachedEnd()){
        pthread_mutex_lock(& mutex);

        if(seq <= upperwindow) {

            int blocksize = file.getNextBlocksize();
            senddata = new unsigned char[blocksize + 28];
            Packet to_send;
            to_send.data = new char[blocksize];
            to_send.sequenceNumber = seq;
            to_send.ackNumber = 0;
            to_send.type = 55; // DATA

            file.readBlock(*to_send.data);
            createPacket(senddata, to_send, blocksize + 28);
            if (server.sendToClient(reinterpret_cast<char*>(senddata), blocksize + 28) == -1)
                perror("sending failed");

            incrementSequenceNumber(seq);

            /* free memory */
            delete [] to_send.data;
            delete [] senddata;

        }
        pthread_mutex_unlock(& mutex);
    }
    pthread_exit(NULL);
} else {
    perror("file opening failed!");
    pthread_exit(NULL);
}
}

我收到的ack线程代码:

static void *wait_for_ack_thread(void *){
while(!file.reachedEnd()){
    Packet ack;

    if (server.receiveFromClient(reinterpret_cast<char*>(receivedata), 28) == -1) {
        perror("error receiving ack");
    } else {
        getPacket(receivedata, ack, 28);
        pthread_mutex_lock(& mutex);
        incrementSequenceNumber(upperwindow);

        pthread_mutex_unlock(& mutex)

    }
}
pthread_exit(NULL);
}

非常感谢所有评论! :)

编辑: 添加了readBlock函数的代码:

void readBlock(char & in){

memcpy(& in, buffer + block_position, blocksize);
block_position = block_position + blocksize;
if(block_position == buffersize){
    buf_position ++;
    if(buf_position == buf_reads){
        buffersize = filesize % buffersize;
    }
    fillBuffer();
    block_position = 0;
}
if(blocksize < MAX_DATA_SIZE){
    reached_end = true;
    return;
}
if((buffersize - block_position) < MAX_DATA_SIZE){
    blocksize = buffersize % blocksize;

}

}

1 个答案:

答案 0 :(得分:1)

创建一个表示通信状态的数组。

0表示未发送,或发送和接收方报告错误。 1表示发送。 2表示已发送,而ack已获取。

分配此数组,并使用互斥锁保护对它的访问。

发送线程保留两个指向数组的指针 - &#34;已发送到&#34;并且&#34;应该发送下一个&#34;。这些由发送线程拥有。

ack线程只是获取ack数据包,锁定数组,并在状态上进行转换。

发送线程锁定阵列,检查它是否可以推进&#34;已发送到&#34;指针(或者它应该重新发送旧东西)。如果它注意到错误,则会减少&#34;应该在下一次发送&#34;指向它的指针。

然后看看它是否应该发送下一个东西。如果它应该,它会将节点标记为&#34;正在发送&#34;,解锁数组并发送它。

如果发送线程没有工作,并且发现无事可做,它会在超时时进入睡眠状态,并且可能会唤醒&#34;通过ack线程。

现在,请注意,客户端可以以错误的顺序获取由此发送的数据包,除非您将其限制为在传输中有1个数据包。

连接状态数组不一定是文字数组,但如果你从那开始并稍后进行优化就会更容易。

在接收端,你必须注意序列号,因为数据包可以不按顺序到达那里。要对此进行测试,请编写一个有意发送错误顺序的数据包的服务器,并确保客户端能够正确地将它们拼接在一起。