为什么我的TCP传输在cygwin上被破坏了?

Wil*_*mKF 2 c++ porting cygwin tcp endianness

我正在尝试调试从Cygwin发送时我的TCP传输被破坏的原因.我看到在Centos上运行的服务器程序中只显示每个结构的前24个字节.第25到第28个字节被加扰,之后的所有其他字节都被清零.走向另一个方向,从Cygwin上的Centos接收,再次只有每个块的前24个字节出现在我在Cygwin上运行的服务器程序中.第25到第40个字节被加扰,之后的所有其他字节都被清零.在Cygwin上发送或接收localhost时,我也看到了这个问题.对于localhost,前34个字节是正确的,之后全部归零.

我正在努力的应用程序在Centos4上与Centos交谈时正常工作,我正在尝试将其移植到Cygwin.Valgrind报告没有关于Centos的问题,我没有在Cygwin上运行Valgrind.这两个平台都是小端x86.

我在运行Cygwin的主机Windows XP系统上运行Wireshark.当我用Wireshark嗅探数据包时,它们看起来很完美,因为从Cygwin发送数据包并收到数据包给Cygwin.

不知何故,数据在Wireshark查看的级别与程序本身之间被破坏.

C++代码使用::write(fd, buffer, size)::read(fd, buffer, size)写入和读取TCP数据包,其中fd是客户端和服务器之间打开的套接字的文件描述符.此代码在Centos4与Centos交谈时完美运行.

最奇怪的事情对我来说是,数据包嗅探器显示所有的情况下正确完整的数据包,但在cygwin应用程序从不读取完整的数据包或在其他方向,CentOS的应用程序从不读取完整的数据包.

任何人都可以建议我如何调试这个?

这是一些请求的代码:

size_t
read_buf(int fd, char *buf, size_t count, bool &eof, bool immediate)
{
  if (count > SSIZE_MAX) {
    throw;
  }

  size_t want = count;
  size_t got = 0;

  fd_set readFdSet;
  int fdMaxPlus1 = fd + 1;

  FD_ZERO(&readFdSet);
  FD_SET(fd, &readFdSet);

  while (got < want) {
    errno = 0;

    struct timeval timeVal;
    const int timeoutSeconds = 60;

    timeVal.tv_usec = 0;
    timeVal.tv_sec = immediate ? 0 : timeoutSeconds;

    int selectReturn = ::select(fdMaxPlus1, &readFdSet, NULL, NULL, &timeVal);

    if (selectReturn < 0) {
      throw;
    }

    if (selectReturn == 0 || !FD_ISSET(fd, &readFdSet)) {
      throw;
    }

    errno = 0;

    // Read buffer of length count.
    ssize_t result = ::read(fd, buf, want - got);

    if (result < 0) {
      throw;
    } else {
      if (result != 0) {
        // Not an error, increment the byte counter 'got' & the read pointer,
        // buf.
        got += result;
        buf += result;
      } else { // EOF because zero result from read.
        eof = true;
        break;
      }
    }
  }
  return got;
}
Run Code Online (Sandbox Code Playgroud)

我发现了更多关于这种失败的信息.正在读取数据包的C++类的布局如下:

unsigned char _array[28];
long long _sequence;
unsigned char _type;
unsigned char _num;
short _size;
Run Code Online (Sandbox Code Playgroud)

显然,漫长的时间会被随后的四个字节所扰乱.

Centos应用程序发送的C++内存,以_sequence开头,以十六进制表示,看起来像是write():

_sequence: 45 44 35 44 33 34 43 45
    _type: 05
     _num: 33
    _size: 02 71
Run Code Online (Sandbox Code Playgroud)

Wireshark在数据包中显示网络大端格式的内存:

_sequence: 45 43 34 33 44 35 44 45
    _type: 05
     _num: 33
    _size: 71 02
Run Code Online (Sandbox Code Playgroud)

但是,在C++ cygwin little-endian应用程序中的read()之后,它看起来像这样:

_sequence: 02 71 33 05 45 44 35 44
    _type: 00
     _num: 00
    _size: 00 00
Run Code Online (Sandbox Code Playgroud)

我很难过这是怎么回事.这似乎是big-endian和little-endian的问题,但这两个平台都是小端的.


这里_array是7个整数而不是28个字符.

发件人完成内存转储:

_array[0]: 70 a2 b7 cf
_array[1]: 9b 89 41 2c
_array[2]: aa e9 15 76
_array[3]: 9e 09 b6 e2
_array[4]: 85 49 08 81
_array[5]: bd d7 9b 1e
_array[6]: f2 52 df db
_sequence: 41 41 31 35 32 43 38 45
    _type: 05
     _num: 45
    _size: 02 71
Run Code Online (Sandbox Code Playgroud)

并在收到时:

_array[0]: 70 a2 b7 cf
_array[1]: 9b 89 41 2c
_array[2]: aa e9 15 76
_array[3]: 9e 09 b6 e2
_array[4]: 85 49 08 81
_array[5]: bd d7 9b 1e
_array[6]: f2 52 df db
_sequence: 02 71 45 05 41 41 31 35
    _type: 0
     _num: 0
    _size: 0
Run Code Online (Sandbox Code Playgroud)

Cygwin测试结果:

4
8
48
0x22be08
0x22be28
0x22be31
0x22be32
0x22be38
Run Code Online (Sandbox Code Playgroud)

Centos测试结果:

4
8
40
0xbfffe010
0xbfffe02c
0xbfffe035
0xbfffe036
0xbfffe038
Run Code Online (Sandbox Code Playgroud)

Ben*_*igt 5

现在您已经显示了数据,您的问题很明显.您没有控制结构的对齐,因此编译器会自动将8字节字段(long long)放在结构起始的8字节边界(偏移32)上,留下4个字节的填充.

将对齐更改为1个字节,一切都应该解决.这是您需要的代码段:

__attribute__ ((aligned (1))) __attribute ((packed))
Run Code Online (Sandbox Code Playgroud)

我也建议你使用被位块传输在网络上固定大小的类型结构,例如uint8_t,uint32_t,uint64_t


以前的想法:

使用TCP,您不需要readwrite 数据包.您可以从字节流中读取和写入.数据包用于承载这些字节,但不保留边界.

您的代码看起来很合理,您可能想要更新问题的措辞.