1

This is my first question, so your grace would be appreciated. I'm trying to learn C Sockets for Unix. As part of my learning, I tried to code a simple telnet-like client that connects to a host on a specified port, prints any characters received from the server, and sends anything the user writes to the console. It receives fine, but when I try to send a string, nothing happens. Then, when I interrupt the program, all the strings I tried to send get sent. Thanks in advanced. I'm probably just being stupid.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>

int SetupSock(char *host, char *port) {
    int s, status;  
    struct addrinfo hints, *res;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    getaddrinfo(host, port, &hints, &res);  
    if((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
        printf("Could not create sock...root?\n");
        exit(1);
    }
    if((status = connect(s, res->ai_addr, res->ai_addrlen)) == -1) {
        printf("Connect Failed: %s\n", strerror(errno));
        printf("%s", strerror( errno ));
        printf("\n");       
        exit(1);
    }
    return s;
}

int main (void) {
    char *host = malloc(100), *port = malloc(20), buf[2], *msg = malloc(1000);
    struct timeval waitid;
    fd_set read_flags, write_flags;
    signal(SIGPIPE, SIG_IGN);
    int sock, flags;
    //Input Host and Port
    printf("Host: ");
    gets(host);
    printf("Port: ");
    gets(port);
    sock = SetupSock(host, port);
    flags = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, flags | O_NONBLOCK);
    FD_ZERO(&read_flags);
    FD_ZERO(&write_flags);
    FD_SET(sock, &read_flags);
    FD_SET(fileno(stdin), &read_flags);
    fflush(0);
    int pid = fork();
    if (pid == 0) {
        int status;
        close(stdout);
        while(1) {
        select(sock + 1, &read_flags, &write_flags, (fd_set*) 0, &waitid);
        if (FD_ISSET(fileno(stdin), &read_flags)) {
            gets(msg);
            if((status = send(sock, msg, strlen(msg), MSG_NOSIGNAL)) == -1) {
                printf("Send Failed: %s\n", strerror(errno));
            }
        }
        }
    }
    else {
        close(stdin);
        while(1) {
            select(sock + 1, &read_flags, &write_flags, (fd_set*) 0, &waitid);
            if (FD_ISSET(sock, &read_flags)) {
                if(recv(sock, &buf, 1, 0) > 0){     
                    printf("%s", buf);
                }
            }
        }
    }
    close(sock);
    return 0;
}
MGordon
  • 11
  • 2
  • Using two distinct process to send and receive looks strange. If you want to do this you should close the stdout/stdin fd you are not using. – Stéphane Gimenez Aug 13 '11 at 15:54
  • @cnicutar: You can use anything for the other side. I guess the OP used a random http server from the internet, this is fine. The problem is from this code. – Stéphane Gimenez Aug 13 '11 at 16:03
  • @Stéphane Gimenez I am not convinced. I can see no easily discernible problem in his code. – cnicutar Aug 13 '11 at 16:11

2 Answers2

1

Your select is performed only once here. It should be inside a loop. select just waits for an event to occur on the provided set of file descriptors. It should be called again after each event has been processed.

  • After the `select` is performed, `fork` kicks in and the program is split into a process that reads and one that writes. So even that select is useless there; more `select`s would add nothing. – cnicutar Aug 13 '11 at 16:10
  • @cnicutar: the status of `sock` and `stdin` returned by select wil be fixed forever. It might well be that the `sock` unlocked the select alone (or that `stdin` unlocked it). In these cases, only one of the `gets` or `send` will be executed. So you should select again to see if something has changed. – Stéphane Gimenez Aug 13 '11 at 16:18
  • Indeed, I did not notice FD_ISSET for the `gets`. – cnicutar Aug 13 '11 at 16:20
  • Btw, in this case the `select` for writing on `sock` in not necessary because you don't care if the `send` call will block. Actually, it's even better to block because you don't want do do another `gets` before the data has been sent. (Otherwise you lose the previous input in the `msg` buffer) – Stéphane Gimenez Aug 13 '11 at 16:23
  • Thanks, Stephane. I will try to fix it and let you know how it works out. – MGordon Aug 14 '11 at 21:32
  • I tried making the changes you suggested, but the program still behaves the same. I edited in the changes. Could you think of anything else wrong with it? – MGordon Aug 15 '11 at 01:43
  • @MGordon: I think you should try to move this on [codereview.se]. Flag this question for migration. – Stéphane Gimenez Aug 15 '11 at 15:01
0

To cover an area the other answer missed: select and stdio do not mix. You cannot get an accurate result for whether stdin is readable by whether fileno(stdin) is readable due to buffering.

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