4

I used to think that write() system call is unbuffered and that fwrite and fread are used for buffered IO. However I wrote simple programs to establish that some buffering was still going on when using write(). I am using write() and read() on sockets. Due to buffering, it is possible for the client to lag behind while server keeps sending packets. I do not want that. I want that the client must consume the record before the server sends more records.

How can I make that happen without adding network load of acknowledgments etc !

I am using gcc on linux

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;
} 
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
AnkurVj
  • 7,958
  • 10
  • 43
  • 55
  • What protocol are you using? TCP does its own management of the data stream, in which case you're pretty much going to have to live with it. This isn't just a local disk, and there's a lot of queuing code between you and the destination. If you don't care about acknowledgements you can just fire off a UDP packet and move on to the next record, but have no idea if the recipient actually got the record. – tbert Jan 31 '12 at 06:45
  • 2
    You are right in believing that `write(2)` is not buffered. Nagle algorithm is part of TCP! – Basile Starynkevitch Jan 31 '12 at 06:48

1 Answers1

5

Perhaps what you mean is that you want to disable Nagle's Algorithm in which case the solution is:

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

Edit: Hmm, it looks like you want more than this, and I doubt what you want is possible without designing your own protocol on top of UDP.

Edit 2: You may be able to get an effect similar to what you want by limiting the send and receive buffer sizes. The server (sender) should do:

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

and the client (receiver) should do:

setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (int[]){YOUR_BUF_LIMIT}, sizeof(int));
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • This looks like what I might need. Ill just confirm – AnkurVj Jan 31 '12 at 06:50
  • Disabling Nagle's algorithm will minimize latency at the expense of sending more (smaller) packets, but it won't prevent the server from sending when the client has unread data. Try the send/receive buffer options I suggested. – R.. GitHub STOP HELPING ICE Jan 31 '12 at 06:54
  • This isnt working :( .. Here is what is happening : In my test program, I dont start consuming the records I am sending until I receive a user input (i.e. I halt the program on a getchar()) The server sends around 80K records before it stops sending more records waiting for the client to start consuming. I would have liked that the server blocks after the first few records itself. – AnkurVj Jan 31 '12 at 06:59
  • The last suggestion improved things a bit ! The server now blocks much quickly. However the client's performance is hit a lot ! It is going very slow ... is there something I am missing ? – AnkurVj Jan 31 '12 at 07:10
  • TCP is not made to be used the way you're trying to use it, so I'm not surprised it's slow. If you really want this kind of flow control you should probably implement it yourself. You could possibly use out-of-band data on the main TCP socket to send "xon" and "xoff" commands between the client and server... – R.. GitHub STOP HELPING ICE Jan 31 '12 at 07:24
  • COuld you please correct my formatting of the attached code ? I dont know how to correct it – AnkurVj Jan 31 '12 at 07:27