0

In a simple program where I'm trying to send command-line inputs from client to server, I keep getting a "Broken Pipe" for the server side. I send a string to the server and the server returns the string as lower-case to the client.

Server:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include <ctype.h>
#include <unistd.h>

int main()
{

    char str[100];
    int listen_fd, comm_fd;

    struct sockaddr_in servaddr;

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);

    bzero( &servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htons(INADDR_ANY);
    servaddr.sin_port = htons(37892);

    bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    listen(listen_fd, 10);

    comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);

    while(1){
                bzero( str, 100);
                read(comm_fd,str,100);
                for(int i = 0; i < strlen(str); i++){

                        str[i] = tolower(str[i]);

                }
                printf("Echoing back - %s",str);
                write(comm_fd, str, strlen(str)+1);
        }
}

Client

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include<ctype.h>
#include <unistd.h>

int main(int argc,char **argv)
{
    int sockfd,n;
    char sendline[100];
    char recvline[100];
    struct sockaddr_in servaddr;

    sockfd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof servaddr);

    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(37892);

    inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));

    connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));


    if(argc==1) printf("\nNo arguments");

    if (1){
        {
                bzero( sendline, 100);
                bzero( recvline, 100);
                strcpy(sendline, argv[1]);
                write(sockfd,sendline,strlen(sendline)+1);
                read(sockfd,recvline,100);
                printf("%s",recvline);
         }
     }
}

The problem I found was that when the client's side is done sending the string, the command line input does not work like fgets() where the loop will wait for another user input. If I change the if(1) in the client's side to a while(1), it will obviously run an infinite loop as no new inputs are being added. The dilemma is, how would I be able to keep the server's side running to continuously return the string to the client while processing single requests from the command line on the client's side?

Dmitry Grigoryev
  • 3,156
  • 1
  • 25
  • 53
Joe
  • 23
  • 7
  • 4
    You aren't paying attention to what `read()` and `write()` return, which is always a serious problem. You don't error check `socket()` or `connect()` (or `bind()` or `accept()`) either — ditto. You copy `argv[1]` into `sendline` which really isn't necessary, but if you do the copying, you must check there's enough space. Although the `read()` in the client is 'safe', you need to know how many bytes were received so you can print the correct number; you might have read 100 of 120 bytes, and the string won't be null-terminate. You have to be very, very careful! – Jonathan Leffler Feb 21 '18 at 06:48
  • To answer the headline question, you'd have to handle SIGPIPE errors — or ignore them and pay attention to error returns from `write()`. You can then close the connection and loop back for the next one. You also aren't closing any file descriptors, so sooner or later you'll run out of them. – Jonathan Leffler Feb 21 '18 at 06:50
  • Possible duplicate of [How to prevent SIGPIPEs (or handle them properly)](https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly) – Jeremy Friesner Feb 21 '18 at 16:05

2 Answers2

3

Your program has two problems:

1) read() works differently than you think:

Normally read() will read up to a certain number of bytes from some file or stream (e.g. socket).

Because read() does not distinguish between different types of bytes (e.g. letters, the end-of-line marker or even the NUL byte) read() will not work like fgets() (reading line-wise).

read() is also allowed to "split" the data: If you do a write(..."Hello\n"...) on the client the server may receive "Hel" the first time you call read() and the next time it receives "lo\n".

And of course read() can concatenate data: Call write(..."Hello\n"...) and write(..."World\n"...) on the client and one single read() call may receive "Hello\nWorld\n".

And of course both effects may appear at the same time and you have to call read() three times receiving "Hel", "lo\nWo" and "rld\n".

TTYs (= the console (keyboard) and serial ports) have a special feature (which may be switched off) that makes the read() call behave like fgets(). However only TTYs have such a feature!

In the case of sockets read() will always wait for at least one byte to be received and return the (positive) number of bytes received as long as the connection is alive. As soon as read() returns zero or a negative value the connection has been dropped.

You have to use a while loop that processes data until the connection has been dropped.

You'll have to check the data received by read() if it contains the NUL byte to detect the "end" of the data - if "your" data is terminated by a NUL byte.

2) As soon as the client drops the connection the handle returned by accept() is useless.

You should close that handle to save memory and file descriptors (there is a limit on how many file descriptors you can have open at one time).

Then you have to call accept() again to wait for the client to establish a new connection.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
2
  1. Your client sends one request and reads one response.
  2. It then exits without closing the socket.
  3. Your server runs in a loop reading requests and sending responses.
  4. Your server ignores end of stream.
  5. Little or none of this code is error-checked.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
user207421
  • 305,947
  • 44
  • 307
  • 483