Search code examples
csocketstcp

Still sending one message with TCP_NODELAY


from what i've understood of Nagel's algorithm is that it tries to send multiple messages in one message if possible to use less bandwith.

My problem is that for a university project i would have to disable this; I have to first send a name then a year, a month, a day and finally a filename.

On the server side I will have to process it to a string: name/year/month/day/filename

It is explicitly stated that my client/server should work with the client/servers from other students. So I am not allowed to just set a \0 or another character at the end every message and then process it on the server because any student could have a different end charachter.

My code looks like this

int main(int argc, char *argv[])
{
  int sockfd;
  int yes=1;
  struct sockaddr_in their_addr;
  struct hostent *he;
  if ((he=gethostbyname(argv[1])) == NULL) {
    perror("Client: gethostbyname");
    return EXIT_FAILURE;
  }

  if ((sockfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) {
    perror("Client: socket");
    return EXIT_FAILURE;
  }

  their_addr.sin_family = AF_INET;
  their_addr.sin_port = htons(PORT);
  their_addr.sin_addr = *((struct in_addr*)he->h_addr);
  memset(&(their_addr.sin_zero), '\0', 8);

  if (connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1) {
    perror("Client: connect");
    return EXIT_FAILURE;
  }

  if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(int))==-1) {
    perror("Client: setsockopt");
    return EXIT_FAILURE;
  }
  if (send(sockfd,argv[2],strlen(argv[2]),0)==-1) {
    perror("Client: send username");
    return EXIT_FAILURE;
  }

  if (send(sockfd,argv[4],4,0)==-1) {
    perror("Client: send year");
    return EXIT_FAILURE;
  }

I thought that this would work because of the line

setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(int)

sometimes also written like this (none of them work anyways)

setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &yes, sizeof(yes));

I did not find anything saying that this should be done (I always used 0 instead of IPPROTO_TCP):

sockfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

but I found some code with this so I tried it out, but it still did not work. On the server side I have also very standard code with 5 recv(), I also tried to implement TCP_NODELAY there and it still did not work. I doubt the server code will help as the problem seems to be from the client sending one message.

So I would like to know what I am doing wrong and how to effectively get 5 different messages instead of one (what I am currently doing is to have sleep(1) between each send, which is clearly not optimal).

Thank you in advance for the response


Solution

  • There are no 'messages' end-to-end in TCP; it's a byte stream protocol. The protocol is free to combine the bytes from multiple sends as it wishes, or to split one send into multiple segments. This means that if you want discrete messages then you have to invent them. The usual methods include sending a length ahead of the actual message bytes; or having a specific terminating character (which the receiver must then scan for); or using fixed-length messages (I would advise against this as it's inflexible).

    All of those would require establishing a standard approach for all students to use. But that's how it is in real life: communication requires the protocols to be agreed in advance. I don't know your teacher's opinion, but I'd award good marks if you collectively defined a message standard and wrote it up as part of submitting your work.

    The "wait between messages" approach which you discovered for yourself is very much a cross-your-fingers and hope solution; you hope your wait time exceeds the time taken to transmit the message, which could be quite large if there is a network burp. And the receiver hopes that either (a) all bytes are delivered at once, or (b) that if it polls for data then a 'no more' indication means that it has read the whole message.