Solved

Unix domain client and server

Posted on 1998-07-31
5
428 Views
Last Modified: 2013-12-26
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
Comment
Question by:sevrin
  • 3
  • 2
5 Comments
 
LVL 2

Expert Comment

by:seedy
ID: 1295309
Can you check if the fopen was fine by checkig fp as
if ( !(fp = fopen("filetoberead", "r"))) {
      perror("fopen failed");
     exit(1);
}

0
 
LVL 2

Expert Comment

by:seedy
ID: 1295310
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
 

Author Comment

by:sevrin
ID: 1295311
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
 
LVL 2

Accepted Solution

by:
seedy earned 100 total points
ID: 1295312
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
 

Author Comment

by:sevrin
ID: 1295313
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

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now