我使用如下的for循环多次调用函数:
for ( int con=0; con < this->controller_info.size(); con++ ) {
try {
this->pi.home_axis( this->controller_info.at(con).addr );
}
catch( std::out_of_range &e ) { ... }
}
其中home_axis()
函数定义为:
long ServoInterface::home_axis( int addr ) {
std::stringstream cmd;
if ( addr > 0 ) cmd << addr << " ";
cmd << "FRF";
cmd << "n";
int bytes=this->controller.Write( cmd.str() );
return NO_ERROR;
}
而CCD_ 2函数只是将字符串中的字符写入套接字文件描述符的标准CCD_。
您可以看到,每次调用home_axis()
时,它都应该有自己的新鲜std::stringstream cmd
缓冲区。但实际情况是,第一次执行for循环时,接收home_axis写入的字节的主机接收到一个字符串,一次:
1 FRF2 FRF
但如果我打印写入的字节,那么它会打印6,两次。因此,写入程序在两个不同的时间正确写入6字节,但主机显然是作为一个缓冲区接收的。
如果我再次执行for循环,那么主机(正确地)接收
1 FRF
然后
2 FRF
在两个接收到的缓冲器进入时分别处理它们。
std::stringstream cmd
缓冲区怎么会像这样混合?
这里不涉及线程。
为了稍微区分一下,如果我在for循环(即usleep(1);
)中只插入1微秒的延迟,那么它就可以正常工作。此外,如果我手动调用home_axis()
函数,但同样快速地连续调用,而不使用像这样的for循环,
this->pi.home_axis( this->controller_info.at(0).addr );
this->pi.home_axis( this->controller_info.at(1).addr );
那么这也行得通。
所以我想知道是否有可能进行编译器优化?
这与编译器完全无关。
TCP是一个字节流。它没有消息边界的概念。写和读之间没有1:1的关系。您可以写入2条消息,每条消息6个字节,接收器可以一次接收所有12个字节,或者1个字节,然后11个字节,或两者之间的任何组合。这就是TCP的工作方式。默认情况下,它会按照自己认为合适的方式分解数据包,以优化传输。
重要的是,TCP保证字节将被传递(除非连接丢失),并且它将以写入字节的相同顺序传递字节。
因此,发送方必须在数据本身中指明每条消息的开始和结束位置。可以在邮件内容之前发送邮件的长度,也可以用唯一的分隔符分隔每条邮件(根据实际情况)。
在接收端,一次读取可能会接收部分消息或多条消息等。接收器有责任缓冲传入的字节,并根据需要从该缓冲区中仅提取完整的消息,无论需要多少次读取才能完成这些消息。
当您用尾随的n
来界定消息时,接收器应缓冲所有字节,并仅提取已接收到其n
的消息,将任何不完整的消息留在缓冲区的末尾,以供后续读取完成。
通过这种方式,可以保留并正确处理消息边界。