Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 469
  • Last Modified:

Unix domain client and server

I'm working through W. Richard Stevens' book "Unix Network Programming", and I thought I'd test his Unix domain stream client and server programs on my system (Linux, slakware distribution). The programs are as below; as Stevens says on page 278: "1 The client reads a line from its standard input and writes the line to the server; 2 The server reads a line from its network input and echoes the line back to the client; 3 The client reads the echoed line and prints it on its standard output."

My question is this: as an experiment, I thought I'd modify the client program so that it received its input from a file rather than from standard input. But none of my attempts seem to work! (For example, I tried making a FILE *fp declaration, making fp = fopen("filetoberead", "r"); and then calling str_cli(fp, sockfd)) Can someone explain why this doesn't work, and suggest how I can get the client to read from a file? I'm sure the answer is simple, but it eludes me!!

Here is the code:

/*
 * Example of client using UNIX domain stream protocol
 */

#include <stdio.h>
#include "uniks.h"
int writen(register int, register char *, register int);
int readline(int, char *, int);

main(int argc, char *argv[])
{
      int                  sockfd, servlen;
      struct sockaddr_un      serv_addr;

      pname = argv[0];

      /*
       * Fill in the structure "serv_addr" with the address of the
       * server that we want to send to.
       */

      bzero((char *) &serv_addr, sizeof(serv_addr));
      serv_addr.sun_family = AF_UNIX;
      strcpy(serv_addr.sun_path, UNIXSTR_PATH);
      servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);

      /* Open a UNIX domain stream socket */

      if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            printf("client: can't open stream socket\n");

      /*
       * Connect to the server
       */

      if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
            printf("client: can't connect to server\n");
      str_cli(stdin, sockfd);            /* do it all */

      close(sockfd);
      exit(0);
}            

/* Read the contents of the FILE *fp, write each line to the
 * stream socket (to the server process), then read a line back from
 * the socket and write it to the standard output.
 *
 * Return to caller when an EOF is encountered on the input file.
 */

#define MAXLINE 512

str_cli(register FILE *fp, register int sockfd)
{
      int       n;
      char      sendline[MAXLINE], recvline[MAXLINE + 1];

      while (fgets(sendline, MAXLINE, fp) != NULL)
            {
            n = strlen(sendline);
            if (writen(sockfd, sendline, n) != n)
                  printf("str_cli: writen error on socket\n");

      /* Now read a line from the socket and write it to
       * our standard output.
         */

      n = readline(sockfd, recvline, MAXLINE);
      if (n < 0)
            printf("str_cli: readline error\n");

            fputs(recvline, stdout);
            }
}      

/*
 * Write n bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */

int writen(register int fd, register char *ptr, register int nbytes)
{

int nleft, nwritten;

nleft = nbytes;
while (nleft > 0)
      {
      nwritten = write(fd, ptr, nleft);
      if (nwritten <= 0)
            return(nwritten);            /* error */

      nleft -= nwritten;
      ptr += nwritten;
      }
      return(nbytes - nleft);
}

/*
 * Read a line from a descriptor. Read the line one byte at a time,
 * looking for the new line. We store the newline in the buffer,
 * then follow it with a null. We return the number of characters up
 * but not including the null.
 */

int readline(int fd, char *ptr, int maxlen)
{
      int n, rc;
      char c;

      for (n = 1; n < maxlen; n++)
            {
            if ((rc = read(fd, &c, 1)) == 1)
                  {
                  *ptr++ = c;
                  if (c == '\n')
                        break;
                  }
            else if (rc == 0)
                        {
                        if (n == 1)
                              return(0);      /* EOF, no data read */
                        else
                              break;            /* EOF, some data read */
                        }
            else
                  return(-1);                  /* error */
            }
            *ptr = 0;
            return(n);
}


/*
 * Example of server using UNIX domain stream protocol
 */

#include "uniks.h"
int readline(int, char *, int);
int writen(int, char *, int);

main(int argc, char *argv[])
{
      int                  sockfd, newsockfd, clilen, childpid, servlen;
      struct sockaddr_un      cli_addr, serv_addr;

      pname = argv[0];

      /*
       * Open a socket (a UNIX domain stream socket).
       */

      if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            printf("server: can't open stream socket\n");

      /*
       * Bind our local address so that the client can send to us.
       */

      bzero((char *) &serv_addr, sizeof(serv_addr));
      serv_addr.sun_family = AF_UNIX;
      strcpy(serv_addr.sun_path, UNIXSTR_PATH);
      servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);

      if (bind(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
            printf("server: can't bind local address\n");

      listen(sockfd, 5);
      for ( ; ;) {
      clilen = sizeof(cli_addr);
      if (newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen) < 0)
            printf("server: accept error\n");

      if (childpid = fork() < 0)
            printf("server: fork error\n");
      else if (childpid == 0)      {      /* child process */
      close(sockfd);
             str_echo(newsockfd);
      exit(0);
                        }
      close(newsockfd);
            }
}      
      
/*
 * Read a stream socket one line at a time, and write each line back
 * to the sender. Return when the connection is terminated.
 */

#define MAXLINE 512

str_echo(int sockfd)
{
      int n;
      char line[MAXLINE];

      for( ; ; )
            {
      n = readline(sockfd, line, MAXLINE);
            if (n == 0)
                  return;            /* connection terminated */
            else if (n < 0)
                  printf("str_echo: readline error\n");

            if (writen(sockfd, line, n) != n)
                  printf("str_echo: writen error\n");
            }
}

/*
 * Read a line from a descriptor. Read the line one byte at a time,
 * looking for the newline. We store the newline in the buffer, then
 * follow it with a null. We return the number of characters up to but
 * not including the null.
 */

int readline(fd, ptr, maxlen)
register int      fd;
register char      *ptr;
register int       maxlen;
{
      int n, rc;
      char c;

      for(n = 1; n < maxlen; n++)
                              {
            if ((rc = read(fd, &c, 1)) == 1)
                                    {
                                    *ptr++ = c;
                                    if (c == '\n')
                                          break;

                                    }
            else if (rc == 0)
                                    {
                                    if (n == 1)
                                          return(0); /* EOF, no data read */
                                    else
                                          break;
                                    }
            else
                  return(-1);            /* error */
                              }
            *ptr = 0;
            return(n);
}

                                    
/* write n bytes to a descriptor. Use in place of write()
 * when fd is a stream socket.
 */

int writen(register int fd, register char *ptr, register int nbytes)
{
int      nleft, nwritten;

nleft = nbytes;
while (nleft > 0)
            {
            nwritten = write(fd, ptr, nleft);
            if (nwritten <= 0)
                  return(nwritten);            /* error */

            nleft -= nwritten;
            ptr += nwritten;
            }
      return(nbytes - nleft);
}
0
sevrin
Asked:
sevrin
  • 3
  • 2
1 Solution
 
seedyCommented:
Can you check if the fopen was fine by checkig fp as
if ( !(fp = fopen("filetoberead", "r"))) {
      perror("fopen failed");
     exit(1);
}

0
 
seedyCommented:
Does the program work fine for stdin?  

Also,
>if (childpid = fork() < 0)
>    printf("server: fork error\n");
can be better written as
if ((childpid = fork()) < 0)
    printf("server: fork error\n");
0
 
sevrinAuthor Commented:
To answer your second question first, Seedy: I *think* the program works for stdin. What happens is I start the server on one virtual console, and the client on another. When I type characters on the client's virtual console, nothing happens; but when I type characters on the server's virtual console, they are echoed. I must admit I expected that I would be typing on the client's console rather than the server's; but on the other hand, as there's no echoing without the client, perhaps all *is* well. (Perhaps you can tell me....)

When I try to get the client to read from a file rather than from stdin, the behaviour is exactly the same: ie, characters are echoed on the server's virtual console. I modified the client code to incorporate the test you suggested, and have pasted it below, just so that you can see what I've done more clearly.

Thanks.


/*
 * Example of client using UNIX domain stream protocol
 */

#include <stdio.h>
#include "uniks.h"
int writen(register int, register char *, register int);
int readline(int, char *, int);

main(int argc, char *argv[])
{
      int                  sockfd, servlen;
      struct sockaddr_un      serv_addr;
      FILE *fp;

      pname = argv[0];

      /*
       * Fill in the structure "serv_addr" with the address of the
       * server that we want to send to.
       */

      bzero((char *) &serv_addr, sizeof(serv_addr));
      serv_addr.sun_family = AF_UNIX;
      strcpy(serv_addr.sun_path, UNIXSTR_PATH);
      servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);

      /* Open a UNIX domain stream socket */

      if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            printf("client: can't open stream socket\n");

      /*
       * Connect to the server
       */

      if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
            printf("client: can't connect to server\n");
      if (!(fp = fopen("thingo.txt", "r")))
            {
            printf("Can't open file\n");
            exit(1);
            }
      str_cli(stdin, sockfd);            /* do it all */

      close(sockfd);
      exit(0);
}            
0
 
seedyCommented:
No, the program does not work fine for stdin.  The server program is reading from the stdin(server's virtual console) and writing it back to the same.  

The problem is, again, a seemingly innocuous parantheses.  Modify your server code to accept connection as follows(watch the additional parantheses):
. code ...
clilen = sizeof(cli_addr);
if ((newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen)) < 0)
    printf("server: accept error\n");

. code ...
Your intention in writing  a = b() < 0  was (a = b()) < 0.  But the compiler was seeing a = (b() < 0)!

Welcome to the unforgiving world of C!
0
 
sevrinAuthor Commented:
Thanks, Seedy! The original program works as it should do, and my modification to read from a file instead of standard input does as well. All praise Seedy!
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now