用于循环的套接字写入混合了字符串缓冲区



我使用如下的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的消息,将任何不完整的消息留在缓冲区的末尾,以供后续读取完成。

通过这种方式,可以保留并正确处理消息边界。

最新更新