[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [MiNT] tcp_select ( was: Re: inet4: errno is not set correct )
Hello,
I just got reply from the cURL team. I described the bug and the
behavior seems to be non POSIX compliant.
Message: http://article.gmane.org/gmane.comp.web.curl.library/29506
So if FreeMiNT wants to be POXIS compatible, it seems that this can be
described as an bug of the socket API (only if there is no other bug
lurking within my test case, of course...).
It seems like the poll is returning to early, it returns when the
socket is ready to be used for sending data ( from FreeMiNTs point of
view) - but it is still not ready for reading, the recv() call is
ending up with the ENOTCON error... but previous send() did not return
that error!
If you uncomment the usleep within the code, or pass an higher timeout
value to socket_ready (which is curios - why does poll runs longer when
you pass an higher timeout? Maybe the return is also wrong..., not just
the returned flags), the recv() returns the error EAGAIN, which is
fine. That's how it should behave,...
The following contains the Test Case, which is already reduced ;) The
same code is within the file attached.
I compiled it with:
gcc -O2 ( Version 4.4.3 )
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define _GNU_SOURCE
#include <poll.h>
#define BUFSIZE 48
int so_pri;
int so_sec;
struct sockaddr_in addr;
struct hostent *host;
char * p_host = "google.com";
char buf[BUFSIZE];
int nbytes;
int so_opt;
char * request = "GET / HTTP/1.1\n"
"Accept: */*\n"
"Accept-Encoding: gzip;q=0, deflate; q=0, *;q=0\n"
"Host: google.com\n"
"\r\n\r\n";
struct timeval curlx_tvnow(void)
{
/*
** gettimeofday() is not granted to be increased monotonically, due
to
** clock drifting and external source time synchronization it can
jump
** forward or backward in time.
*/
struct timeval now;
(void)gettimeofday(&now, NULL);
return now;
}
/*
* Make sure that the first argument is the more recent time, as
otherwise
* we'll get a weird negative time-diff back...
*
* Returns: the time difference in number of milliseconds.
*/
long curlx_tvdiff(struct timeval newer, struct timeval older)
{
return (newer.tv_sec-older.tv_sec)*1000+
(newer.tv_usec-older.tv_usec)/1000;
}
/*
* Same as curlx_tvdiff but with full usec resolution.
*
* Returns: the time difference in seconds with subsecond resolution.
*/
double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
{
return (double)(newer.tv_sec-older.tv_sec)+
(double)(newer.tv_usec-older.tv_usec)/1000000.0;
}
/* return the number of seconds in the given input timeval struct */
long Curl_tvlong(struct timeval t1)
{
return t1.tv_sec;
}
#define CURL_CSELECT_OUT 4
#define CURL_CSELECT_ERR 8
#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif
#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif
#ifndef POLLRDBAND
#define POLLRDBAND POLLPRI
#endif
#define SOCKERRNO (errno)
#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
#define error_not_EINTR (error != EINTR)
int socket_ready(int writefd, int timeout_ms)
{
struct pollfd pfd[2];
int num;
struct timeval initial_tv = {0,0};
int pending_ms = 0;
int error;
int r=-1;
int ret;
if(timeout_ms > 0)
{
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
}
num = 0;
pfd[num].fd = writefd;
pfd[num].events = POLLWRNORM|POLLOUT;
pfd[num].revents = 0;
num++;
do {
if(timeout_ms < 0)
pending_ms = -1;
else if(!timeout_ms)
pending_ms = 0;
r = poll(pfd, num, pending_ms);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
if(r < 0)
return -1;
if(r == 0)
return 0;
ret = 0;
num = 0;
if(pfd[num].revents & (POLLWRNORM|POLLOUT))
ret |= CURL_CSELECT_OUT;
if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
ret |= CURL_CSELECT_ERR;
return ret;
}
int main()
{
int r;
so_pri = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (so_pri == -1)
{
perror("socket() failed");
return 1;
}
so_opt = 1;
if( setsockopt(so_pri, SOL_SOCKET, SO_REUSEADDR, (char *)&so_opt,
sizeof(so_opt)) == -1)
{
perror("setsockopt failed");
return 2;
}
printf("Resolving host: %s ...\n", p_host );
host = gethostbyname( p_host );
if (!host)
{
perror("gethostbyname() failed");
return 3;
}
addr.sin_addr = *(struct in_addr*) host->h_addr;
addr.sin_port = htons(80);
addr.sin_family = AF_INET;
printf("%s\n", inet_ntoa(addr.sin_addr) );
fcntl(so_pri, F_SETFL, O_NONBLOCK );
printf("C0nnecting...");
if( connect(so_pri, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
perror("connect()");
}
int c = 0;
while( 1 ) {
if( c > 100 )
return( 16 );
r = socket_ready( so_pri, 10 );
if( r == -1 )
{
printf("Select error!");
return( 5 );
}
else if( r == 0)
{
printf("Connect timeout!\n");
}
else if( r & CURL_CSELECT_ERR )
{
printf("CURL_CSELECT_ERR!\n");
}
else if( r & CURL_CSELECT_OUT ) {
break;
}
c++;
}
printf("Connect OK!\n");
send(so_pri, request, strlen(request), 0);
//usleep(50000);
nbytes = recv(so_pri, buf, sizeof(buf) - 1, 0);
if (nbytes == -1)
{
perror("recv() failed");
printf("Errno: %d", errno );
return 5;
}
printf("Done\n");
close(so_pri);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define _GNU_SOURCE
#include <poll.h>
#define BUFSIZE 48
int so_pri;
int so_sec;
struct sockaddr_in addr;
struct hostent *host;
char * p_host = "google.com";
char buf[BUFSIZE];
int nbytes;
int so_opt;
char * request = "GET / HTTP/1.1\n"
"Accept: */*\n"
"Accept-Encoding: gzip;q=0, deflate; q=0, *;q=0\n"
"Host: google.com\n"
"\r\n\r\n";
struct timeval curlx_tvnow(void)
{
/*
** gettimeofday() is not granted to be increased monotonically, due to
** clock drifting and external source time synchronization it can jump
** forward or backward in time.
*/
struct timeval now;
(void)gettimeofday(&now, NULL);
return now;
}
/*
* Make sure that the first argument is the more recent time, as otherwise
* we'll get a weird negative time-diff back...
*
* Returns: the time difference in number of milliseconds.
*/
long curlx_tvdiff(struct timeval newer, struct timeval older)
{
return (newer.tv_sec-older.tv_sec)*1000+
(newer.tv_usec-older.tv_usec)/1000;
}
/*
* Same as curlx_tvdiff but with full usec resolution.
*
* Returns: the time difference in seconds with subsecond resolution.
*/
double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
{
return (double)(newer.tv_sec-older.tv_sec)+
(double)(newer.tv_usec-older.tv_usec)/1000000.0;
}
/* return the number of seconds in the given input timeval struct */
long Curl_tvlong(struct timeval t1)
{
return t1.tv_sec;
}
#define CURL_CSELECT_OUT 4
#define CURL_CSELECT_ERR 8
#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif
#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif
#ifndef POLLRDBAND
#define POLLRDBAND POLLPRI
#endif
#define SOCKERRNO (errno)
#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
#define error_not_EINTR (error != EINTR)
int socket_ready(int writefd, int timeout_ms)
{
struct pollfd pfd[2];
int num;
struct timeval initial_tv = {0,0};
int pending_ms = 0;
int error;
int r=-1;
int ret;
if(timeout_ms > 0)
{
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
}
num = 0;
pfd[num].fd = writefd;
pfd[num].events = POLLWRNORM|POLLOUT;
pfd[num].revents = 0;
num++;
do {
if(timeout_ms < 0)
pending_ms = -1;
else if(!timeout_ms)
pending_ms = 0;
r = poll(pfd, num, pending_ms);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
if(r < 0)
return -1;
if(r == 0)
return 0;
ret = 0;
num = 0;
if(pfd[num].revents & (POLLWRNORM|POLLOUT))
ret |= CURL_CSELECT_OUT;
if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
ret |= CURL_CSELECT_ERR;
return ret;
}
int main()
{
int r;
so_pri = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (so_pri == -1)
{
perror("socket() failed");
return 1;
}
so_opt = 1;
if( setsockopt(so_pri, SOL_SOCKET, SO_REUSEADDR, (char *)&so_opt, sizeof(so_opt)) == -1)
{
perror("setsockopt failed");
return 2;
}
printf("Resolving host: %s ...\n", p_host );
host = gethostbyname( p_host );
if (!host)
{
perror("gethostbyname() failed");
return 3;
}
addr.sin_addr = *(struct in_addr*) host->h_addr;
addr.sin_port = htons(80);
addr.sin_family = AF_INET;
printf("%s\n", inet_ntoa(addr.sin_addr) );
fcntl(so_pri, F_SETFL, O_NONBLOCK );
printf("C0nnecting...");
if( connect(so_pri, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
perror("connect()");
}
int c = 0;
while( 1 ) {
if( c > 100 )
return( 16 );
r = socket_ready( so_pri, 10 );
if( r == -1 )
{
printf("Select error!");
return( 5 );
}
else if( r == 0)
{
printf("Connect timeout!\n");
}
else if( r & CURL_CSELECT_ERR )
{
printf("CURL_CSELECT_ERR!\n");
}
else if( r & CURL_CSELECT_OUT ) {
break;
}
c++;
}
printf("Connect OK!\n");
send(so_pri, request, strlen(request), 0);
//usleep(50000);
nbytes = recv(so_pri, buf, sizeof(buf) - 1, 0);
if (nbytes == -1)
{
perror("recv() failed");
printf("Errno: %d", errno );
return 5;
}
printf("Done\n");
close(so_pri);
return 0;
}