I posted this on freebsd-questions also but maybe the expert density
isn't that high as here in "hackers".
Since I think it may be a design or implementation issue in FreeBSDs'
select(), I'm posting it here as well,
hoping to get an experts' answer.
I have written a small server to test TCP/IP roundtrip times of the
packets in a proprietary protocol and while
compiling and running this server on different platforms (Windows
7/cygwin, UbuntuLinux, FreeBSD 8.0 Release), I found
that the server produces an error when the listening socket (on which
the accept() is performed) is also
member of the select() fd_set.
On the other platforms the program works without error, just under
FreeBSD I'm getting this "invalid argument" error.
Comments appreciated (despite comments about the error checking logic
Here is the code:
/* testsrv.c */
/* gcc -o testsrv testsrv.c */
/* */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <sys/select.h>
#define USEDBUFSIZ 60
#define MAX_HOSTNAME 256
#define MAXFDS 256
#define CLRBUF memset(buf,0,sizeof(buf))
#define max(a,b) (((a) > (b)) ? (a) : (b))
static unsigned char buf[256];
int array_of_fds[MAXFDS];
static fd_set clientfds;
#define SOCKET int
void *memset(void *, int, size_t);
int enter (int);
int remov (int);
int invalidip (char *);
void exit (int);
int getv (int, unsigned char *, int);
int getfds ();
int
main(int argc, char **argv)
{
int nfds;
static fd_set readfds;
SOCKET ListenSocket, newsockfd;
struct sockaddr_in cli_addr;
struct sockaddr_in service;
struct hostent *thisHost;
int bOptVal = 0;
int bOptLen = sizeof(int);
char hostname[256];
char *host_addr;
struct in_addr addr = {0};
char *ip;
u_short port;
int iResult = 0;
int i , n, m, clilen, dummy, connect = 0;
struct timeval tv;
/*---------------------------------------*/
/* Create a listening socket */
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == -1) {
perror("socket creation");
return 1;
} else
printf("ListenSocket=%d\n", ListenSocket);
/*-----------------------------------------*/
/* Bind the socket to the local IP address */
/* and port 3210 */
port = 3210;
if (gethostname(hostname, 256))
perror("gethostname failed\n"), exit(3);
printf("%s\n", hostname), fflush(stdout);
thisHost = gethostbyname(hostname);
ip = inet_ntoa(*(struct in_addr *)(*thisHost->h_addr_list));
if (argc == 2) {
host_addr = argv[1];
service.sin_addr.s_addr = inet_addr(host_addr);
thisHost = gethostbyaddr((const char *)&service.sin_addr.s_addr,
sizeof(service.sin_addr.s_addr),
AF_INET);
if (thisHost == 0)
printf("host unknown\n"), exit(3);
if (invalidip(host_addr))
printf("invalid IP\n"), exit(4);
} else {
service.sin_addr.s_addr = inet_addr(ip);
}
service.sin_port = htons(port);
service.sin_family = AF_INET;
iResult = bind(ListenSocket, (struct sockaddr *)&service,
sizeof(service));
if (iResult == -1) {
perror("bind");
shutdown(ListenSocket, SHUT_RDWR);
return 1;
}
listen(ListenSocket, SOMAXCONN);
printf("SOMAXCONN=%d %d\n", SOMAXCONN, FD_SETSIZE);
/* all sockets are put into an own array_of_fs */
/* in the while() loop below the FD_SET id used by looping through
the */
/* array_of_fds to fill the readfds array in the select() */
enter(ListenSocket);
/*
* Wait for connect
*/
tv.tv_sec = 0;
tv.tv_usec = 5000000; /* 5 seconds */
printf("Server %s listening on port %d\n", thisHost->h_name, port);
memset((void *)array_of_fds, 0, (size_t) MAXFDS *
sizeof(*array_of_fds));
nfds = ListenSocket + 1;
while (1) {
FD_ZERO(&readfds);
FD_SET(ListenSocket, &readfds);
for (i = 0; i < MAXFDS; i++) {
if (array_of_fds[i]) {
nfds = max(nfds, array_of_fds[i]) + 1;
FD_SET(array_of_fds[i], &readfds);
}
}
n = select(nfds, &readfds,
(fd_set *) NULL, /* not interested in write */
(fd_set *) NULL, /* ...or exceptions */
&tv); /* timeout */
switch (n) {
case 1:
clilen = sizeof(cli_addr);
/* first test if a new client has connected */
if (FD_ISSET(ListenSocket, &readfds)) {
newsockfd = accept(ListenSocket,
(struct sockaddr *)&cli_addr, &clilen);
if (enter(newsockfd)) /* socket of new connection is
entered*/
printf("\n%d.connect! ", ++connect), fflush(stdout);
else
printf("too many connections"), exit(1);
sprintf(buf, "ENTERED %d of %d", 1, 10);
send(newsockfd, buf, USEDBUFSIZ, 0); /* send to the
client*/
} else {
for (i = 0; i < MAXFDS; i++) {
int s;
if (FD_ISSET((s = array_of_fds[i]), &readfds)) {
m = getv(s, buf, USEDBUFSIZ);
if (m <= 0) {
shutdown(s, SHUT_RDWR);
if (remov(s) == -1)
printf("error on remove(s)\n", s),
fflush(stdout
);
goto done;
} else {
printf("*");
fflush(stdout);
CLRBUF;
/* now send some data to the client */
sprintf(buf, "XX-123456789-XX-xx :
0x0600, 0x99999999, \"NNNNN\"");
m = send(s, buf, USEDBUFSIZ, 0);
if (m < 0 || m != USEDBUFSIZ) {
perror("cannot send\n");
}
CLRBUF;
sprintf(buf, "END");
m = send(s, buf, USEDBUFSIZ, 0);
if (m < 0 || m != USEDBUFSIZ) {
perror("cannot send\n");
}
} /* else */
} /* if FD_ISSET */
} /* for */
break;
case 0:
break;
case -1:
perror("select");
exit(2);
default:
printf("more than one descriptor ready!\n");
fflush(stdout);
exit(4);
break;
} /* switch */
} /* while */
}
done:
shutdown(ListenSocket, SHUT_RDWR);
return 0;
} /* main */
int
getv(int fd, unsigned char *buf, int count)
{
int m , got = 0;
int i = 0;
while (got < count) {
m = recv(fd, buf + got, count - got, 0);
if (m == count)
return m;
if (m > 0)
got = got + m;
if (m == 0)
return (0);
if (m < 0) {
return -1;
}
}
return -1;
}
int
enter(int i)
{
int k = 0;
while (array_of_fds[k++]);
if (k > MAXFDS - 1)
return -1;
k--;
array_of_fds[k] = i;
return i;
}
int
remov(s)
{
int k;
for (k = 0; k < MAXFDS; k++)
if (array_of_fds[k] == s) {
array_of_fds[k] = 0;
return k;
}
return -1;
}
/*
* this code taken from code for validating IPv4 address taken from
article
* in http://bytes.com/topic/c/answers/212174-code-validating-ipv4-address
*/
#define INVALID -1
#define VALID 0
int
invalidip(char *ipadd)
{
unsigned b1, b2, b3, b4;
unsigned char c;
if (sscanf(ipadd, "%3u.%3u.%3u.%3u%c", &b1, &b2, &b3, &b4, &c) != 4)
return INVALID;
if ((b1 | b2 | b3 | b4) > 255)
return INVALID;
if (strspn(ipadd, "0123456789.") < strlen(ipadd))
return INVALID;
return VALID;
}
_______________________________________________
freebsd-questi...@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-questions
To unsubscribe, send any mail to
"freebsd-questions-unsubscr...@freebsd.org"
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "freebsd-hackers-unsubscr...@freebsd.org"