1

I am trying to create a non-blocking connect with epoll. I am still learning epoll so I was giving a try with the code mentioned in this example - How to code an epoll based sockets client in C.

I just modified the timeout and make the event as EPOLLOUT.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 20000
#define SERVER "127.0.0.1"
#define MAXBUF 1024
#define MAX_EPOLL_EVENTS 64

int main() {
    int sockfd;
    struct sockaddr_in dest;
    char buffer[MAXBUF];
    struct epoll_event events[MAX_EPOLL_EVENTS];
    int i, num_ready;

    /*---Open socket for streaming---*/
    if ( (sockfd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
        perror("Socket");
        exit(errno);
    }

    /*---Add socket to epoll---*/
    int epfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLOUT; // Cann append "|EPOLLOUT" for write events as well
    event.data.fd = sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);

    /*---Initialize server address/port struct---*/
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
        dest.sin_port = htons(PORT);
    if ( inet_pton(AF_INET, SERVER, &dest.sin_addr.s_addr) == 0 ) {
        perror(SERVER);
        exit(errno);
    }

    /*---Connect to server---*/
    if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) {
        if(errno != EINPROGRESS) {
            perror("Connect ");
            exit(errno);
        }
    }

    /*---Wait for socket connect to complete---*/
    num_ready = epoll_wait(epfd, events, MAX_EPOLL_EVENTS, 100000/*timeout*/);
    for(i = 0; i < num_ready; i++) {
        if(events[i].events & EPOLLOUT) {
            printf("Socket %d connected\n", events[i].data.fd);
        }
    }

    /*---Wait for data---*/
    num_ready = epoll_wait(epfd, events, MAX_EPOLL_EVENTS, 100000);
    for(i = 0; i < num_ready; i++) {
        if(events[i].events & EPOLLOUT) {
            printf("Socket %d got some data\n", events[i].data.fd);
            //bzero(buffer, MAXBUF);
            //recv(sockfd, buffer, sizeof(buffer), 0);
            //printf("Received: %s", buffer);
        }
    }

    close(sockfd);
    return 0;
}

Result:

[root@test-vm socket_prgm]# ./test_epoll_so.o 
Socket 3 connected
Socket 3 got some data

Two things I am observing with the above program:

  1. There is no server process running on the port 20000. The socket was still able to connect for the event EPOLLOUT.
  2. The timeout period does not work at all. The script executes immediately.

Am I missing something obvious here? Thank you.

wonder
  • 885
  • 1
  • 18
  • 32
  • Recommend checking the return value from `epoll_ctl()` to make sure it's doing what you think it is. – Steve Friedl Nov 30 '19 at 18:08
  • @SteveFried The return value of `epoll_ctl()` is 0. I hope this is doing what is expected to do. – wonder Nov 30 '19 at 18:12
  • 1
    Try making the socket blocking by removing `SOCK_NONBLOCK`. – tambre Nov 30 '19 at 18:29
  • @tambre Now the output is "Connection refused". I think this could be because there is no daemon running on that port. I tried with port 22 and I am getting the same result as above but without waiting for the timeout period. Also, I want it for a non blocking socket. I could see in many examples, most of them have -1(which should block indefinitely) as timeout in epoll_wait but it will be within a `while true or for(;;)` loop. – wonder Nov 30 '19 at 18:33
  • You're ignoring `EINPROGRESS` for `connect()`. Presumably poll returns immediately if the socket isn't connected or bound. – tambre Nov 30 '19 at 18:59
  • Yes that's correct. However, it did work for blocking socket but not for non blocking socket connect. Does epoll not work with the later case? – wonder Nov 30 '19 at 19:06
  • Are any other event bits also set? What distribution and kernel are you using? Try checking for `EPOLLIN` instead. – tambre Nov 30 '19 at 19:59

1 Answers1

1

I think the obvious is indeed missed:

2.The timeout period does not work at all. The script executes immediately.

It executes immediately because you connect to the local host. This is a particular scenario when EINPROGRESS is highly unlikely to be returned because the connection can be established immediately. As the connection is established, the file descriptor becomes writable, and epoll_wait returns that event, so you are all settled to proceed. It would wait your timeout before erroring out if 3-way-handshake with a remote host took longer than 100 seconds.

1. There is no server process running on the port 20000. The socket was still able to connect for the event EPOLLOUT.

This is your miss: there's nothing in your code making it a background process. You have to fork() or use threads to leave a process after your main() returns.

wick
  • 1,995
  • 2
  • 20
  • 31