如何关闭 write() 系统调用的缓冲?

Posted

技术标签:

【中文标题】如何关闭 write() 系统调用的缓冲?【英文标题】:How to turn off buffering on write() system call? 【发布时间】:2012-02-22 22:20:28 【问题描述】:

我曾经认为write()系统调用是无缓冲的,fwritefread用于缓冲IO。但是,我编写了简单的程序来确定在使用write() 时某些缓冲仍在进行。我在套接字上使用write()read()。由于缓冲,客户端可能会在服务器不断发送数据包时滞后。我不要那个。我希望客户端必须在服务器发送更多记录之前使用记录。

如何在不增加确认等网络负载的情况下实现这一点!

我在 linux 上使用 gcc

server.c:

#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/tcp.h>

int remote_rr_port=2000; // Server will send RR logs using connection on this port.
char const *remote_server_ip="127.0.0.1";
int connFD_rr;


static void startTcpServer(int *sd, const int port) 
  *sd= socket(AF_INET, SOCK_STREAM, 0);

  // Set socket option so that port can be reused
  int enable = 1;
  setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
  struct sockaddr_in a;
  memset(&a,0,sizeof(a));
  a.sin_family = AF_INET;
  a.sin_port = port;
  a.sin_addr.s_addr = INADDR_ANY;
  int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a));
  listen(*sd,2);



// Wait for connection from client
static int getTcpConnection(int sd) 
  char buf[100];
  socklen_t len;
  struct sockaddr_in clientAddress;
  printf("\nWaiting for connection from remote client\n");
  len = sizeof(clientAddress);
  int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len);
  setsockopt(connFD_rr, SOL_SOCKET, SO_SNDBUF, (int[])0, sizeof(int));
  printf("\n Connection from : %s:%d\n",inet_ntop(AF_INET, &clientAddress.sin_addr, buf, sizeof(buf)),clientAddress.sin_port);
  fflush(stdout);
  return connFD;


FILE* rdrr_server_start(void) 

  // Socket Descriptors for the two connections
  int rr_sd;
  int input_sd;

  startTcpServer(&rr_sd, remote_rr_port);

  connFD_rr = getTcpConnection(rr_sd);

  return fdopen(connFD_rr, "w");


int main() 
  int i = 0;
  rdrr_server_start();

  for(i=0;i<10000000; i++) 
    write(connFD_rr, &i, sizeof (int));
    printf("%d\n", i);
  
  return 0;

client.c:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/tcp.h>



int remote_rr_port=2000; // Server will send RR logs using connection on this port.
char const *remote_server_ip="127.0.0.1";
int connFD_rr;

FILE* rdrr_client_start(void) 

  connFD_rr = socket(AF_INET, SOCK_STREAM, 0);

  struct sockaddr_in a;
  memset(&a,0,sizeof(a));
  a.sin_family = AF_INET;
  a.sin_port = remote_rr_port;
  a.sin_addr.s_addr = inet_addr(remote_server_ip);

  printf("\nConnecting to Server on RR port");
  int CONNECT_TO_SERVER= connect(connFD_rr,(struct sockaddr *)  &a, sizeof(a));
  printf("\nConnected to server on RR port");
  setsockopt(connFD_rr, SOL_SOCKET, SO_RCVBUF, (int[])0, sizeof(int));
  return fdopen(connFD_rr, "r");
  

int main() 
  int i = 0;
  rdrr_client_start();
  getrchar();
  while(1) 
    read(connFD_rr, &i, sizeof (int));
    printf("%d\n", i);
  
  return 0;
 

【问题讨论】:

你使用什么协议? TCP 自己管理数据流,在这种情况下,您将不得不忍受它。这不仅仅是一个本地磁盘,在你和目的地之间有很多排队代码。如果您不关心确认,您可以只触发一个 UDP 数据包并继续下一条记录,但不知道收件人是否真的得到了记录。 您认为write(2) 没有缓冲是正确的。 Nagle 算法是 TCP 的一部分! 【参考方案1】:

也许你的意思是你想禁用Nagle's Algorithm,在这种情况下解决方案是:

setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (int[])1, sizeof(int));

编辑:嗯,看起来你想要的不止这些,我怀疑如果不在 UDP 之上设计自己的协议,你想要的东西是可能的。

编辑 2:您可以通过限制发送和接收缓冲区大小来获得类似于您想要的效果。服务器(发件人)应该这样做:

setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (int[])YOUR_BUF_LIMIT, sizeof(int));

而客户(接收方)应该这样做:

setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (int[])YOUR_BUF_LIMIT, sizeof(int));

【讨论】:

这看起来像我可能需要的。我只是确认 禁用 Nagle 算法将以发送更多(更小)数据包为代价最小化延迟,但它不会阻止服务器在客户端有未读数据时发送。试试我建议的发送/接收缓冲区选项。 这不起作用 :( .. 这是发生的事情:在我的测试程序中,我不会开始使用我发送的记录,直到我收到用户输入(即我在 getchar 上停止程序()) 服务器在停止发送更多记录等待客户端开始消费之前发送了大约 8 万条记录。我希望服务器在前几条记录本身之后阻塞。 最后的建议稍微改善了一点!服务器现在阻塞得很快。但是客户的表现受到了很大的打击!它进展得很慢......我有什么遗漏吗? TCP 不是按照您尝试使用它的方式使用的,所以我并不惊讶它很慢。如果你真的想要这种流控制,你应该自己实现它。您可以在主 TCP 套接字上使用带外数据在客户端和服务器之间发送“xon”和“xoff”命令...

以上是关于如何关闭 write() 系统调用的缓冲?的主要内容,如果未能解决你的问题,请参考以下文章

后台开发之IO缓冲区管理

write(2)最佳的缓冲大小

缓冲文件系统(fopen/fread/fwrite)和非缓冲文件系统(open/read/write)

缓冲文件系统(fopen/fread/fwrite)和非缓冲文件系统(open/read/write)

带缓冲I/O 和不带缓冲I/O的区别与联系

在linux中使用write系统调用写入数据时出现问题