I am trying to write a C code that connects using non-blocking TCP socket along with select()
. When I read the man page about EINPROGRESS
, I feel a little bit confused.
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to
select(2)
orpoll(2)
for completion by selecting the socket for writing. Afterselect(2)
indicates writability, usegetsockopt(2)
to read theSO_ERROR
option at levelSOL_SOCKET
to determine whetherconnect()
completed successfully (SO_ERROR
is zero) or unsuccessfully(SO_ERROR
is one of the usual error codes listed here, explaining the reason for the failure).
Is there any sample code I can refer to? Although it is a pretty old question, I don't see anyone post a complete working code. Some suggest to use connect twice but I don't know exactly how.
Sure, below is a little C program that uses a non-blocking TCP connect to connect to www.google.com's port 80, send it a nonsense string, and print out the response it gets back:
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
static void SendNonsenseCommand(int sock)
{
const char sendString[] = "Hello Google! How are you!\r\n\r\n";
if (send(sock, sendString, sizeof(sendString), 0) != sizeof(sendString)) perror("send()");
}
int main(int argc, char ** argv)
{
// Create a TCP socket
const int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {perror("socket"); return 10;}
// Set the TCP socket to non-blocking mode
const int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {perror("fcntl(F_GETFL)"); return 10;}
if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0) {perror("fcntl(F_SETFL)"); return 10;}
// Get the IP address of www.google.com
struct hostent * he = gethostbyname("www.google.com");
if (he == NULL) {printf("Couldn't get a hostent for www.google.com\n"); return 10;}
// Start a non-blocking/asynchronous TCP connetion to port 80
struct sockaddr_in saAddr;
memset(&saAddr, 0, sizeof(saAddr));
saAddr.sin_family = AF_INET;
saAddr.sin_addr = *(struct in_addr*)he->h_addr;
saAddr.sin_port = htons(80);
const int connectResult = connect(sock, (const struct sockaddr *) &saAddr, sizeof(saAddr));
int isTCPConnectInProgress = ((connectResult == -1)&&(errno == EINPROGRESS));
if ((connectResult == 0)||(isTCPConnectInProgress))
{
if (isTCPConnectInProgress == 0) SendNonsenseCommand(sock);
// TCP connection is happening in the background; our event-loop calls select() to block until it is ready
while(1)
{
fd_set socketsToWatchForReadReady, socketsToWatchForWriteReady;
FD_ZERO(&socketsToWatchForReadReady);
FD_ZERO(&socketsToWatchForWriteReady);
// While connecting, we'll watch the socket for ready-for-write as that will tell us when the
// TCP connection process has completed. After it's connected, we'll watch it for ready-for-read
// to see what Google's web server has to say to us.
if (isTCPConnectInProgress) FD_SET(sock, &socketsToWatchForWriteReady);
else FD_SET(sock, &socketsToWatchForReadReady);
int maxFD = sock; // if we were watching multiple sockets, we'd compute this to be the max value of all of them
const int selectResult = select(maxFD+1, &socketsToWatchForReadReady, &socketsToWatchForWriteReady, NULL, NULL);
if (selectResult >= 0)
{
if ((FD_ISSET(sock, &socketsToWatchForWriteReady))&&(isTCPConnectInProgress))
{
printf("Socket is ready for write! Let's find out if the connection succeeded or not...\n");
struct sockaddr_in junk;
socklen_t length = sizeof(junk);
memset(&junk, 0, sizeof(junk));
if (getpeername(sock, (struct sockaddr *)&junk, &length) == 0)
{
printf("TCP Connection succeeded, socket is ready for use!\n");
isTCPConnectInProgress = 0;
SendNonsenseCommand(sock);
}
else
{
printf("TCP Connection failed!\n");
break;
}
}
if (FD_ISSET(sock, &socketsToWatchForReadReady))
{
char buf[512];
const int numBytesReceived = recv(sock, buf, sizeof(buf)-1, 0);
if (numBytesReceived > 0)
{
buf[numBytesReceived] = '\0'; // ensure NUL-termination before we call printf()
printf("recv() returned %i: [%s]\n", numBytesReceived, buf);
}
else if (numBytesReceived == 0)
{
printf("TCP Connection severed!\n");
break;
}
else perror("recv()");
}
}
else {perror("select()"); return 10;}
}
}
else perror("connect()");
close(sock); // just to be tidy
return 0;
}