如何可靠地使send(2)做短发送?

时间:2020-01-15 18:09:45

标签: c sockets networking posix

send(2)需要一个缓冲区和一个缓冲区长度。它可以返回错误,也可以返回成功发送到缓冲区长度大小的一定数量的字节。在某些情况下,send发送的字节数少于请求的字节数(例如https://stackoverflow.com/a/2618755/939259)。

除了发送大消息并从另一个线程发出信号并希望得到幸运之外,是否有办法在单元测试中持续触发短发送?

3 个答案:

答案 0 :(得分:2)

只要自己动手:


x = ('name:80', 'desc:100')
dict([tuple(a.split(':')) for a in x])

答案 1 :(得分:1)

如果将要测试的代码打包到共享库(或符号被削弱的静态库)中,则测试可执行文件(与该库链接)将能够覆盖send本身及其链接的库。

示例(覆盖write而不是send

#!/bin/sh -eu
cat > code.c <<EOF
#include <unistd.h>
#include <stdio.h>
ssize_t hw(void)
{
    static const char b[]="hello world\n";
    return write(1, b, sizeof(b)-1);
}
void libfunc(void)
{
    puts(__func__);
    hw();
}
EOF

cat > test.c <<'EOF'
#include <stdio.h>
void libfunc(void);
ssize_t hw(void);

#if TEST
ssize_t hw(void)
{
    puts("override");
    return 42;
}
#endif
int main()
{
    libfunc();
    puts("====");
    printf("%zu\n", hw());
}

EOF

gcc code.c -fpic -shared -o libcode.so
gcc test.c $PWD/libcode.so -o real
gcc -DTEST test.c $PWD/libcode.so -o mocked
set -x
./real
./mocked

示例输出:

hello world
hello world
libfunc
====
12
libfunc
override
====
override
42

这使该符号的libc实现黯然失色,并且尽管存在用于访问替代项(即dlopen和/或-Wl,--wrap)的机制,但您无需在单元测试中访问它(如果您确实需要在其他单元测试中使用它,则将这些其他单元测试放在另一个程序中是最简单的。

答案 2 :(得分:0)

默认情况下,

send(2)仅在所有数据成功复制到发送缓冲区后才返回。 强制它发送较少字节的可能方法在很大程度上取决于情况。

如果 1.可以接入插座 2.不想更改链接的二进制文件中对send的所有调用的行为,

然后可以将套接字设置为非阻塞。然后,对send的调用将发送尽可能多的八位字节。发送的八位位组的数量主要取决于要发送的套接字的发送缓冲区中的可用内存量。 因此,如果您获得

uint8_t my_data[NUM_BYTES_TO_SEND] = {0}; /* We don't care what your buffer actually contains in this example ... */

size_t num_bytes = sizeof(my_data);

send(fd, my_data, num_bytes);

,如果希望send发送的数据少于num_bytes,则可以尝试减少套接字fd的发送缓冲区。 能否实现,如何完成此操作可能取决于您的操作系统。

在Linux下,您可以尝试通过使用setsockopt(2)选项SO_SNDBUF手动设置缓冲区大小来缩小发送缓冲区,该手册在`socket(7)手册页中进行了介绍:

uint8_t my_data[NUM_BYTES_TO_SEND] = {0};
size_t num_bytes = sizeof(my_data);
size_t max_bytes_to_send = num_bytes - 1;   /* Force to send at most 1 byte less than in our buffer */

/* Set the socket non-blocking - you should check status afterwards */
int status = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);

/* reduce the size of the send buffer below the number of bytes you want to send */
setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &max_bytes_to_send, sizeof (size_t));

...

send(fd, my_data, num_bytes);

/* Possibly restore the old socket state */

可能还需要弄混SO_SNDBUFFORCE选项。

有关

的更多信息

无论如何,最好的选择取决于您的情况。 如果您正在寻找一种可靠的解决方案来检查代码,那么您甚至可能无法访问它,而是动态链接到您的项目中,那么您可以采用此处建议的另一种方法:在编译后的代码中使send符号变暗。 另一方面,这会影响代码中对send的所有所有调用(您当然可以绕过此问题,例如通过替换send取决于您的某些标志可以设置)。

如果您可以访问套接字fd,并且只希望特定的send调用受到影响(我想您就是这种情况,因为您谈论的是单元测试,并且检查发送的字节数少于预期的)所有测试都可能不是您想要的?),那么缩小发送缓冲区的方法可能是可行的方法。

相关问题