简体   繁体   English

Unix 域套接字和写缓冲区溢出

[英]Unix domain socket and write buffer overflow

I use Unix domain sockets in one of my application and I am observing a very strange behavior while writing to a socket.我在我的一个应用程序中使用 Unix 域 sockets 并且在写入套接字时我观察到一个非常奇怪的行为。 A short description: there are 2 application - client and server where each one read and write at the same simultaneously, ie the socket works in full duplex mode.简短描述:有 2 个应用程序 - 客户端和服务器,每个应用程序同时读取和写入,即套接字以全双工模式工作。 To prevent some overflow I check the write buffer:为了防止一些溢出,我检查了写缓冲区:

ioctl(socket_handler, TIOCOUTQ, &pending)

But sometimes the result I get is very strange - 768, 1536, 2304 etc. On the one hand, this number does not look like a bytes count I write, on the other hand it looks a little suspicious - 0x300, 0x600 etc, in other words it looks like a flag or error code.但有时我得到的结果很奇怪 - 768、1536、2304 等。一方面,这个数字看起来不像我写的字节数,另一方面看起来有点可疑 - 0x300、0x600 等,在换句话说,它看起来像一个标志或错误代码。 The documentation says nothing about this.文档对此只字未提。

To be consistent and prevent questions I provide a full code example to reproduce the issue:为了保持一致并防止出现问题,我提供了一个完整的代码示例来重现该问题:

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <pthread.h>
#include <poll.h>

static int sock;
static socklen_t len;
static struct sockaddr_un addr;
static std::string path = "@testsock";
static pthread_t thread;
static bool running;
static pollfd polls[2] = {};
static char buf[200];
static int err;

bool InitSocket()
{
    sock = socket(AF_UNIX, SOCK_STREAM,0);
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path.c_str(), path.size());
    *addr.sun_path = '\0';
    len = static_cast<socklen_t>(__builtin_offsetof(struct sockaddr_un, sun_path) + path.length());
    int opt_value = 1;
    err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, static_cast<void *>(&opt_value), sizeof(int));
    int on = 1;
    err = ioctl(sock, FIONBIO, &on);
    return true;
}

bool InitServer()
{
    polls[0].fd = sock;
    polls[0].events = POLLIN;

    err = bind(sock, reinterpret_cast<struct sockaddr *>(&addr), len);
    err = listen(sock, 10);
    return true;
}

bool InitClient()
{
    polls[1].fd = sock;
    polls[1].events = POLLIN;

    err = connect(sock, reinterpret_cast<struct sockaddr *>(&addr), len);
    return true;
}

uint32_t GetRand(uint32_t min, uint32_t max)
{
    return static_cast<uint32_t>(rand()) % (max - min) + min;
}
void *ReadThread(void*)
{
    while(running)
    {
        int status = poll(polls, 2, 1000);
        if(status > 0)
        {
            for(int i = 0;i < 2;i ++)
            {
                if(polls[i].revents == 0)
                    continue;

                if(i == 0)
                {
                    struct sockaddr_un cli_addr;
                    socklen_t clilen = sizeof(cli_addr);
                    int new_socket = accept(sock, reinterpret_cast<struct sockaddr *>(&cli_addr), &clilen);
                    int on = 1;
                    ioctl(new_socket, FIONBIO, &on);
                    polls[1].fd = new_socket;
                    polls[1].events = POLLIN;
                    std::cout << "client connected" << std::endl;
                }
                else
                {
                    ssize_t readBytes = read(polls[1].fd, &buf, 100);
                    if(readBytes > 0)
                    {
                        //std::string str(buf, readBytes);
                        std::cout << "         read " << readBytes << " bytes: " << std::endl;
                        int p = GetRand(10, 100); //
                        usleep(p * 1000);         // removing these lines solves the problem, but ... 
                    }
                    else
                    {
                        std::cout << "read error" << std::endl;
                        running = false;
                    }
                }
            }
        }
    }
    return nullptr;
}



int main(int argc, char *argv[])
{       
    if(argc < 2)
        return 1;

    srand(static_cast<unsigned int>(time(nullptr)));
    InitSocket();
    std::string app_type(argv[1]);
    if(app_type == "-s")
        InitServer();
    else if(app_type == "-c")
        InitClient();
    else
        return 1;

    running = true;
    pthread_create(&thread, nullptr, &ReadThread, nullptr);

    if(app_type == "-s")
    {
        while(polls[1].fd == 0) usleep(100);
    }

    char sendbuf[100];
    for(int i = 0;i < 100;i ++)
    {
        int length = GetRand(10, 100);
        int delay = GetRand(1, 10);
        for(int j = 0;j < length;j ++)
        {
            sendbuf[j] = GetRand('a', 'z');
        }

        unsigned long pending = 0;
        err = ioctl(polls[1].fd, TIOCOUTQ, &pending);
        std::cout << "pending " << pending << std::endl;

        err = send(polls[1].fd, sendbuf, length, MSG_NOSIGNAL);
        std::cout << "write " << length << " bytes" << std::endl;
        usleep(delay * 1000);
    }

    running = false;
    pthread_join(thread, nullptr);

    if(app_type == "-s")
        close(polls[0].fd);
    close(polls[1].fd);

    return 0;
}

running the code: the server:运行代码:服务器:

test_app -s 

and then the client app (in another console window):然后是客户端应用程序(在另一个控制台窗口中):

test_app -c 

A possible output:一个可能的 output:

The server:服务器:

client connected
         read 83 bytes: 
pending 0
write 66 bytes
pending 0
write 18 bytes
pending 768          <----- ????
write 17 bytes
pending 1536         <----- ????
write 79 bytes
         read 100 bytes: 
pending 2304         <----- ????
write 51 bytes
pending 3072
write 25 bytes
         read 100 bytes: 
pending 2304
write 96 bytes
pending 3072
write 19 bytes

The client:客户端:

pending 0
write 83 bytes
         read 66 bytes: 
pending 0
write 19 bytes
pending 768
write 94 bytes
pending 1536
write 52 bytes
pending 2304
write 11 bytes
pending 3072
write 39 bytes
pending 3072
write 35 bytes
         read 100 bytes: 
pending 3840
write 20 bytes
pending 2304
write 92 bytes
pending 3072
write 53 bytes
pending 3840
write 88 bytes
pending 4608
write 84 bytes
         read 100 bytes: 

As I noticed this problem occurs after adding an artificial delay in the read thread.正如我注意到在读取线程中添加人为延迟后会出现此问题。 In my real application I process a message and that creates a small delay.在我的实际应用程序中,我处理一条消息,这会产生一个小的延迟。 But I absolutely do not understand from where these numbers come (pending) and how the delay in the read thread affects this?但我绝对不明白这些数字从何而来(待定)以及读取线程中的延迟如何影响这一点?

How can I avoid this?我怎样才能避免这种情况? How can I get the real write buffer, not these strange numbers?我怎样才能得到真正的写缓冲区,而不是这些奇怪的数字?

I use Unix domain sockets [... and] To prevent some overflow I check the write buffer我使用 Unix 域 sockets [...和]为了防止一些溢出,我检查了写缓冲区

This is not your responsibility, nor, as far as I can determine, within your power.这不是你的责任,也不是,据我所知,在你的权力范围内。 In particular, not all ioctls are applicable to all devices.特别是,并非所有 ioctl 都适用于所有设备。 TIOCOUTQ is an ioctl for terminal devices -- the leading 'T' in the macro name is mnemonic for this -- and it is not listed among the ioctls applicable to sockets in Linux (since you mention in comments that the target is Linux). TIOCOUTQ是用于终端设备的 ioctl——宏名称中的前导“T”是助记符——它未列在适用于 Linux 中 sockets 的 ioctl 中(因为您在评论中提到目标是 Linux)。

The kernel ensures that the buffer does not overflow. kernel 确保缓冲区不会溢出。 When there is a shortage of buffer space it will either block write requests until sufficient space becomes available or perform short writes or both.当缓冲区空间不足时,它将阻止写入请求直到有足够的空间可用或执行短写入或两者兼而有之。 Or if the socket is in non-blocking mode then writes that would otherwise block will instead fail with EAGAIN .或者,如果套接字处于非阻塞模式,则否则会阻塞的写入将改为失败并EAGAIN

How can I avoid this?我怎样才能避免这种情况?

Avoid using ioctls on devices to which they do not apply.避免在它们不适用的设备上使用 ioctl。

How can I get the real write buffer, not these strange numbers?我怎样才能得到真正的写缓冲区,而不是这些奇怪的数字?

You cannot, as far as I am aware, and it seems unlikely that you have a genuine need to do.据我所知,您不能这样做,而且您似乎不太可能真正需要这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM