// By running this program under strace -ff, you can see that the // server’s write() call blocks until the client finishes reading the // data sent, rather than returning immediately after a partial write, // unless `O_NONBLOCK` is set. (Tested on Debian Linux // 6.1.0-26-amd64.) // Running with one flag, like ./bigsocketwrite -nb, you activate // nonblocking behavior. With two flags, like ./bigsocketwrite -nb // -sleep, you also make the client sleep for a second before reading // the data from the server. #include #include #include #include #include #include enum { PORT = 20331, TOTAL = 100*1024*1024 }; /* 100 MiB */ void die(const char *why) { perror(why); exit(1); } int main(int argc, char **argv) { struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, .sin_port = htons(PORT), }; socklen_t alen = sizeof(addr); char *buf = malloc(TOTAL); if (!buf) die("malloc"); memset(buf, 'A', TOTAL); // Ensure pages are wired and quiet valgrind int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) die("socket"); // Avoid “Address already in use” problems (must be *before* bind()) uint32_t optval = 1; if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) { die("SO_REUSEADDR"); } if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) die("bind"); if (listen(fd, 1) < 0) die("listen"); pid_t pid = fork(); if (pid < 0) die("fork"); if (pid == 0) { // child ≡ server alarm(5); // Kill us in case client dies and never connects int c = accept(fd, 0, 0); if (c < 0) die("accept"); if (argc > 1) { int flags = fcntl(c, F_GETFL); if (-1 == flags) die("F_GETFL"); if (-1 == fcntl(c, F_SETFL, flags | O_NONBLOCK)) die("F_SETFL"); } write(c, buf, TOTAL); } else { // parent ≡ client close(fd); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) die("client socket"); if (connect(fd, (struct sockaddr*)&addr, alen) < 0) die("connect"); if (argc > 2) sleep(1); for (;;) { ssize_t n = read(fd, buf, TOTAL); if (n < 0) die("read"); if (n == 0) break; } } return 0; }