3

So I am playing around with the idea of ports and client/server communication.

I have a server.c program that can open a port, open a listening descriptor, and upon receiving a connection, fork a child to handle communication with a connecting client. I have a client.c program that takes in 5 commandline arguments. Basically the first 3 arguments are practice strings to send to server and the 4th is hostname and the 5th is the port number.

So far connecting these two has worked fine, however, when client tries to write the 3 different strings (argv[1],argv[2], and argv[3]) to the server.c, server.c seems to only be able to read the first one then it seems to be stuck and not continue on with the additional reads even though client will finish writing all the strings to the communication file descriptor. I have been stuck for over 4 hours trying to figure out what should have been a simple practice program to better learn servers and clients. I don't wanna get anymore lost then I already am so I hope someone could anyone give me any advice on how to handle this issue or what I am doing wrong.

Client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "uici.h"
#include "func.h"


int main(int argc, char *argv[]){
  int fd;
  u_port_t portnum;

  if(argc != 6){
    fprintf(stderr, "Usage: %s string1 string2 string3 host port\n",argv[0]);
    return -1;
  }
  portnum = (u_port_t)atoi(argv[5]);
  if((fd = u_connect(portnum, argv[4])) == -1){
    perror("Failled to establish connection");
    return 1;
  }
  fprintf(stderr, "[%ld]:connection made to %s\n", (long)getpid(), argv[4]);
  if((write(fd, argv[3], strlen(argv[3])+1)) == -1){
    fprintf(stderr, "Failed to write %s to fd", argv[3]);
    r_close(fd);
    return 0;
  }
  if((write(fd, argv[1], strlen(argv[1])+1)) == -1){
    fprintf(stderr, "Failed to write %s to fd", argv[1]);
    r_close(fd);
    return 0;
  }
  if((write(fd, argv[2], strlen(argv[2])+1)) == -1){
    fprintf(stderr, "Failed to write %s to fd", argv[2]);
    close(fd);
    return 0;
  }
  fprintf(stderr, "Everything has been written\n");
  return 0;
}

Server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "func.h"
#include "uici.h"

int main(int argc, char *argv[])
{
   u_port_t portnumber;
   int listenfd;
   int fd;
   char client[MAX_CANON];
   int bytes_copied;
   pid_t child;

   if (argc != 2) {
      fprintf(stderr, "Usage: %s port\n", argv[0]);
      return 1;
   }

   portnumber = (u_port_t) atoi(argv[1]);
   if ((listenfd = u_open(portnumber)) < 0) {
      perror("Listen endpoint creation failed");
      return 1;
   }

   fprintf(stderr, "[%ld]: Waiting for the first connection on port %d\n",
                    (long)getpid(), (int)portnumber);
   for ( ; ; ) {
      if ((fd = u_accept(listenfd, client, MAX_CANON)) != -1) {
         fprintf(stderr, "[%ld]: A connection has been received from %s\n",
                 (long) getpid(), client);
         if ((child = fork()) == -1)
            perror("Could not fork a child");

         if (child == 0) {                            /* child code */
            r_close(listenfd);
            int MAXSZ = 1024;
            char str3[MAXSZ];
            char str1[MAXSZ];
            char str2[MAXSZ];
            int bytesread = 0;
            fprintf(stderr, "Beginning the reads\n");
            read(fd,str3, MAXSZ);
            fprintf(stderr, "Finished 1st read\n");
            read(fd,str1, MAXSZ);
            fprintf(stderr, "Finished 2nd read\n");
            read(fd,str2, MAXSZ);
            fprintf(stderr, "str3: %s\n",str3);
            fprintf(stderr, "str1 = %s\n",str1);
            fprintf(stderr, "str2 = %s\n",str2);
            close(fd);
            return 0;
         } else {                                    /* parent code */
            close(fd);
            while (waitpid(-1, NULL, WNOHANG) > 0) ;  /* clean up zombies */
         }
      }
      else
         perror("Accept failed");
   }
}
JGc
  • 31
  • 3
  • Side issue: nice cast of `(long) getpid()` with `fprintf(stderr, "[%ld]: A ...` – chux - Reinstate Monica Apr 22 '16 at 15:20
  • Idea: Rather than printing the string, print the length of the data received with `fprintf(stderr, "[%ld]: A connection has been received from %d\n", (long) getpid(), fd);` Could it be that not all the string is received? (`client` lacks a null character?) – chux - Reinstate Monica Apr 22 '16 at 15:26
  • You have code to write messages delimited by zero bytes. But you have no code to *read* messages delimited by zero bytes. Did you just forget to write it? – David Schwartz Apr 22 '16 at 16:24

2 Answers2

0

Note: There is no guarantee that read or write will read or write all the data from/to the file descriptor, and no guarantee that the data blocks the client is writing will be read in the same way on the server.

You have to make sure to check the bytes written and continue to write data to the fd until you have written them all. When reading, you have to have the client send some kind of header data describing the amount of data that is to be expected, or in your case, you could read 1 byte at a time and look for a '\0' character, indicating the end of a string.

Since MAXSZ is likely larger than the strings you are sending, the server could be reading all of the strings at once into the first buffer, then blocking on the subsequent read.

  • Using r_write is to make sure it write all bytes. Is there a way to use something like poll of anything to make this work better? I don't know much about that function but I was wondering if anyone had more information on the use of these tools – JGc Apr 22 '16 at 15:51
  • I don't think r_write is a standard POSIX function, and I didn't see you provide an implementation of it. I doubt the writing is the problem though, as small writes tend to be sent unfragmented. Check the bytes read of your first r_read call, if that number is larger than the length of your first string, you can be sure it is reading more than expected. It is probably reading all three strings at once, and since printf only prints until the first '\0' character, you only see the first string printed to the console. –  Apr 22 '16 at 15:57
  • Thanks i will try that :) – JGc Apr 22 '16 at 16:17
0

First child forked closes the listener with r_close(listenfd);

Then when client send argv[1] no listener are available and, I think, u_accept(listenfd, client, MAX_CANON) return an error because of listenfd is not valid.

LPs
  • 16,045
  • 8
  • 30
  • 61
  • I've tried different examples with just a different body in the forked server. Those don't return an error and work fine, the issue is the read in the forked child and the writes in the client – JGc Apr 22 '16 at 15:50