所以这是我第一次在这里问一个问题,虽然我已经使用这个网站多年了!
我的问题有点棘手。我正在尝试开发一个客户端服务器应用程序来发送大文件,使用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;
}
}
答案 0 :(得分:1)
创建一个表示通信状态的数组。
0
表示未发送,或发送和接收方报告错误。 1
表示发送。 2
表示已发送,而ack已获取。
分配此数组,并使用互斥锁保护对它的访问。
发送线程保留两个指向数组的指针 - &#34;已发送到&#34;并且&#34;应该发送下一个&#34;。这些由发送线程拥有。
ack线程只是获取ack数据包,锁定数组,并在状态上进行转换。
发送线程锁定阵列,检查它是否可以推进&#34;已发送到&#34;指针(或者它应该重新发送旧东西)。如果它注意到错误,则会减少&#34;应该在下一次发送&#34;指向它的指针。
然后看看它是否应该发送下一个东西。如果它应该,它会将节点标记为&#34;正在发送&#34;,解锁数组并发送它。
如果发送线程没有工作,并且发现无事可做,它会在超时时进入睡眠状态,并且可能会唤醒&#34;通过ack线程。
现在,请注意,客户端可以以错误的顺序获取由此发送的数据包,除非您将其限制为在传输中有1个数据包。
连接状态数组不一定是文字数组,但如果你从那开始并稍后进行优化就会更容易。
在接收端,你必须注意序列号,因为数据包可以不按顺序到达那里。要对此进行测试,请编写一个有意发送错误顺序的数据包的服务器,并确保客户端能够正确地将它们拼接在一起。