0

When I run this program I enter in an ip address and the server port as input. However, after this the program asks me to "Please enter msg: ". However, after typing my message pressing ctrl-D does not do anything. The program still reads from STDIN. How do I indicate the end of the message I'm typing?

Here is the program I'm working with:

/* 
 * echoclient.c - A simple connection-based client
 * usage: echoclient <host> <port>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

#define BUFSIZE 1024

/* 
 * error - wrapper for perror
 */
void error(char *msg) {
    perror(msg);
    exit(0);
}

int main(int argc, char **argv) {
    int sockfd, portno, n;
    struct sockaddr_in serveraddr;
    struct hostent *server;
    char *hostname;
    char buf[BUFSIZE];

    /* check command line arguments */
    if (argc != 3) {
       fprintf(stderr,"usage: %s <hostname> <port>\n", argv[0]);
       exit(0);
    }
    hostname = argv[1];
    portno = atoi(argv[2]);

    /* socket: create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");

    /* gethostbyname: get the server's DNS entry */
    server = gethostbyname(hostname);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host as %s\n", hostname);
        exit(0);
    }

    /* build the server's Internet address */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
      (char *)&serveraddr.sin_addr.s_addr, server->h_length);
    serveraddr.sin_port = htons(portno);

    /* connect: create a connection with the server */
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) 
      error("ERROR connecting");

    /* get message line from the user */
    printf("Please enter msg: ");
    bzero(buf, BUFSIZE);
    fgets(buf, BUFSIZE, stdin);


    /* write: send the message line to the server */
    n = write(sockfd, buf, strlen(buf));
    if (n < 0) 
      error("ERROR writing to socket");

    /* read: print the server's reply */
    bzero(buf, BUFSIZE);
    n = read(sockfd, buf, BUFSIZE);
    if (n < 0) 
      error("ERROR reading from socket");
    printf("Echo from server: %s", buf);
    close(sockfd);
    return 0;
}
Jim Lewis
  • 43,505
  • 7
  • 82
  • 96
user2264035
  • 565
  • 1
  • 7
  • 13
  • 1
    It looks like the program should stop reading `stdin` as soon as you hit "enter" at the end of the first line of the message. What makes you think it's still reading input? – jwodder Dec 03 '13 at 00:29
  • If I press enter it just goes to the next line and continues to take in more input. – user2264035 Dec 03 '13 at 00:37
  • I suspect that the program has gone on to `n = read(sockfd, buf, BUFSIZE)` and is waiting to get a response from the server, and in the meantime is not listening to input from the keyboard. You can type anything you want at the keyboard at this point, including Ctrl-D, but the program will not pay any attention to it until the `read` call returns. – Tim Pierce Dec 03 '13 at 01:05
  • If I want a response from the server do I run the server program before the client or the client before the server? – user2264035 Dec 03 '13 at 01:10
  • If you run the client program before starting the server, then the client won't have anything to connect to :-) – Tim Pierce Dec 03 '13 at 01:42

3 Answers3

4

If you've typed any data on the line, the first Control-D sends what you've typed to the terminal despite you not having hit return yet. You then need to type Control-D a second time to send 0 characters, which indicates EOF to the Standard I/O library.

If the last character you typed was a return, then the saved data is sent to the terminal anyway. If the next character is Control-D, no data is sent so the read gets 0 bytes and that's detected as EOF.

See also Canonical vs non-canonical terminal input.

You should also, and always, check the return value from fgets() and any other input function. If the input function reports failure, you cannot afford to use the input; its state is undefined.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Need to test the result of fgets(buf, BUFSIZE, stdin);

Without testing the result, buf may contain its unaltered previous data and not provide definitive information that no more data is read.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

fgets reads until it reaches EOF or a newline, as if by calling fgetc for each byte. Pressing CtrlD on a terminal does not "set EOF status" on the terminal (there is no such thing) or "send and EOF" (there's also no such thing). What it does (in "cooked mode") is cause the kernel to send everything buffered for line editing over the tty device so that it is received by the application. If there is not yet any input, this causes the application to succeed at reading with a zero-length read; this is the definition of EOF, and will set the EOF status for the stdio FILE stream.

If there is, however, already input waiting (the line you entered), CtrlD will just cause it to be sent to the application. fgets will then read all bytes transmitted, and, not seeing either EOF or a newline, will act as if it calls fgetc again, once again blocking in read. If you then press CtrlD again or press Enter, fgets will then succeed and return.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711