how to use select api in udp for linux - c

int CreateSocket()
{
socklen_t len;
// Socket creation for UDP
acceptSocket=socket(AF_INET,SOCK_DGRAM,0);
if(acceptSocket==-1)
{
printf("Failure: socket creation is failed, failure code\n");
return 1;
}
else
{
printf("Socket started!\n");
}
memset(&addr, 0, sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=htonl(INADDR_ANY);
rc=bind(acceptSocket,(struct sockaddr*)&addr,sizeof(addr));
if(rc== -1)
{
printf("Oh dear, something went wrong with bind()! %s\n", strerror(errno));
return 1;
}
else
{
printf("Socket an port %d \n",port);
}
while(rc!=-1)
{
fd_set master;
fd_set read_fds;
int retval;
FD_ZERO(&master);
FD_ZERO(&read_fds);
FD_SET(acceptSocket, &master);
FD_SET(acceptSocket, &read_fds);
retval =select(2, &master, NULL, NULL, NULL);
len = sizeof(client);
if(retval == -1)
{
printf("error\n");
}
else if(FD_ISSET (acceptSocket, &master))
{
rc=recvfrom(acceptSocket,buf, 256, 0, (struct sockaddr*) &client, &len);
if(rc==0)
{
printf("Server has no connection..\n");
break;
}
if(rc==-1)
{
printf("Oh dear, something went wrong with read()! %s\n", strerror(errno));
break;
}
XcpIp_RxCallback( (uint16) rc, (uint8*) buf, (uint16) port );
}
else
{
makeTimer("First Timer", &firstTimerID, 2, 2); //2ms
makeTimer("Second Timer", &secondTimerID, 10, 10); //10ms
makeTimer("Third Timer", &thirdTimerID, 100, 100); //100ms
}
}
close(acceptSocket);
return 0;
}
The above is a server code for udp layer to recieve data from the client via the ip address and port number. I am using select api to check if there is data via the port then recieving the data else call the timer fucntion. I want to achieve of recieving the data from the client and after recieving I have to call the timer. But the above code is not calling the timer task. What is the mistake in the above code ?? is it efficient to use select api ??

The first parameter to select() is 1 + highest-numbered-FD-in-the-three-sets, so should be acceptSocket + 1.
(I'm assuming that your real problem is more complicated than the above code, because you could just make a blocking recvfrom() call and not bother with select(). You need select() if you want to handle multiple sockets in a single thread, and/or to wake up the blocking call after a timeout - although there are other ways of doing the latter.)

The way you are calling select() with timeout == NULL it will block until there is data to be read for one of the file descripors in master. In your case, with master only containing acceptSocket, the else where you call makeTimer() will never be reached.
Do
struct timeval timeout;
timeout.tv_sec = 0l;
timeout.tv_usec = 0l;
retval = select( acceptSocket+1, master, NULL, NULL, &timeout );
to just check without blocking
Note that select() returns 0 if it returns because of timeout and the number of file descriptors contained in the returned descriptor set otherwise. So you also have to change your condition:
else if(FD_ISSET (acceptSocket, &master))
to
else if(retval > 0 && FD_ISSET (acceptSocket, &master))
because otherwise you would call recvfrom() after a timout as well and in that case it would block

Related

set connect() timeout on unix domain socket

Is using alarm() is the only to set connect() timeout on unix domain socket? I've tried select() which is described here but seems like select() returns ok immediately on unix domain socket every time and
no error occurred by calling getsockopt(SO_ERROR), but a send() on the fd returns an error says Transport endpoint is not connected. I paste the select() code below.
I think using alarm would meet the case, but seems it's considered as an old-fashion way. So I'm here to see if there's any other solutions for this. Thanks in advance.
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
syslog(LOG_USER|LOG_ERR, "fcntl get failed: %s", strerror(errno));
close(fd);
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
syslog(LOG_USER|LOG_ERR, "set fd nonblocking failed: %s", strerror(errno));
close(fd);
return -1;
}
if(connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINPROGRESS) {
close(fd);
return -1;
}
FD_ZERO(&set);
FD_SET(fd, &set);
if(select(fd + 1, NULL, &set, NULL, &timeout) <= 0) {
close(fd);
return -1;
}
/*
if(connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
close(fd);
return -1;
}
*/
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0) {
syslog(LOG_USER|LOG_ERR, "getsockopt failed: %s", strerror(errno));
close(fd);
return -1;
}
if(error != 0) {
syslog(LOG_USER|LOG_ERR, "getsockopt return error: %d", error);
close(fd);
return -1;
}
}
if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) {
syslog(LOG_USER|LOG_ERR, "set fd blocking failed: %s", strerror(errno));
close(fd);
return -1;
}
Somewhere (I did not bookmark that page) in another post I found that the connect() only establishes a TCP connection. It only means that on the other end, there is a working TCP stack, but it does not mean, the server has actually accept()-ed!
The example there was connect() is like calling a support center, and the automatic voice tells you, you are in a queue, but you still cannot communicate. accept() is the actual operator taking your call.
My solution for the same problem will be to have the client wait for the server to actually send something, before moving on with other client-stuff. I can put this in a select-timeout loop.
listen() has a parameter, how many connections can be put in a backlog before starting to drop client connection attempts.
You can use select() or poll() after EINPROGRESS, as described in the connect man page. If you get EAGAIN or EWOULDBLOCK, the Unix domain socket has run out of backlog entries, the queue length specified by the server with the listen() call. The connect() failed.
Note that a connecting client can be able to write to Unix domain sockets until the system buffer is full, before the server even accepted the call. That works for each backlog buffer. Failures occur afterwards.
A failed connect() might need a new socket before retrying. select() might return 0 also if the connection was refused, such as if the server didn't listen(). That depends on system and libray. At any rate, after an error of EAGAIN, it is necessary to retry. For example:
int rtc, so_error, max_retry = 5;
socklen_t len = sizeof so_error;
while ((rtc = connect(fd, (struct sockaddr *)&address, sizeof address)) != 0
&& errno == EAGAIN && --max_retry >= 0) {
sleep(1);
// new socket?
}
if (rtc < 0 && errno != EINPROGRESS) {
syslog(LOG_USER|LOG_ERR, "connect returned %d: %s", rtc, strerror(errno));
close(fd);
return -1;
}
if (rtc < 0)
{
fd_set set, wset, eset;
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(fd, &set);
wset = set;
eset = set;
if(select(fd + 1, &set, &wset, &eset, &timeout) <= 0) {
close(fd);
return -1;
}
// [...]
}

socket is set with O_NONBLOCK, but it seems the calling thread still gets into sleep when sending data sometimes

Client
In fact, my client doesn't recv and process data send from server, just connects to my server.
int netif_msg_client_socket_create(char *sockpath)
{
int addrlen, retval;
int sockfd;
struct sockaddr_un serv;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0) {
PR_ERROR(NETIF_MSG_M, " fatal failure, client msg socket, error is %s, %s %u\n", strerror(errno), __FILE__, __LINE__);
return -1;
}
/* Make client socket. */
memset (&serv, 0, sizeof (struct sockaddr_un));
serv.sun_family = AF_UNIX;
strncpy (serv.sun_path, sockpath, strlen(sockpath));
addrlen = sizeof (serv.sun_family) + strlen(serv.sun_path);
retval = connect(sockfd, (struct sockaddr*)&serv, addrlen);
if(retval < 0)
{
PR_ERROR(NETIF_MSG_M, " fatal failure, client msg connect, error is %s, %s %u\n", strerror(errno), __FILE__, __LINE__);
close(sockfd);
return -1;
}
fcntl(sockfd, F_SETFL, O_NONBLOCK);
return sockfd;
}
2.Server
But my server will try to send some data to the client continuously.
int netif_msg_server_socket_create(char *sockpath)
{
int addrlen, retval;
int sockfd;
struct sockaddr_un serv;
/* First of all, unlink existing socket */
unlink (sockpath);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0)
return -1;
fcntl(sockfd, F_SETFL, O_NONBLOCK);
/* Make server socket. */
memset (&serv, 0, sizeof (struct sockaddr_un));
serv.sun_family = AF_UNIX;
strncpy (serv.sun_path, sockpath, sizeof(serv.sun_path)-1);
addrlen = sizeof (serv.sun_family) + strlen(serv.sun_path);
//printf("sizeof(serv) == %d, addrlen == %d.\r\n", sizeof(serv), addrlen);
retval = bind (sockfd, (struct sockaddr *) &serv, addrlen);
if (retval < 0)
{
close (sockfd); /* Avoid sd leak. */
return -1;
}
retval = listen (sockfd, 20);
if (retval < 0)
{
close (sockfd); /* Avoid sd leak. */
return -1;
}
return sockfd;
}
My server uses select and accepts the connection from my client successfully.
After my server sent 412 packets(96 Bytes each), it seems the server sleeps on send.
Key codes:
printf("Try to send packet(%d bytes) to clientfd %d.\n", MSGCB_DLEN(msgcb), client->acpt_fd);
retval = send(client->acpt_fd, msgcb->data_ptr, MSGCB_DLEN(msgcb), 0);
if(retval != MSGCB_DLEN(msgcb))
{
printf("Send netif notify msg failed[%d].\n", retval);
} else {
printf("Send netif notify msg succeeded.\n");
}
After 412 packets sent to my client and "Try to ..." outputed, nothing goes on, neither "...failed" nor "...succeeded" outputs.
I use getsockopt to fetch the SO_RCVBUF and SO_SNDBUF, there are about 100000Bytes for each of them.
I don't know why, need your help, thanks!
If you want the server socket that is connected to the client to be non-blocking, then you must specifically set the new socket that is returned from accept() to be non-blocking. Your code only sets the listening socket to non-blocking.
You can perform non-blocking I/O with send using the MSG_DONTWAIT flag in the last parameter.
retval = send(client->acpt_fd, msgcb->data_ptr, MSGCB_DLEN(msgcb),
MSG_DONTWAIT);
When performing non-blocking I/O, you need to detect when the return value is signalling you to retry the operation.
if (retval < 0) {
if (errno == EAGAIN) {
/* ... handle retry of send laster when it is ready ... */
} else {
/* ... other error value cases */
}
}

Non blocking socket in c

Currently I am working on a single server,single client udp chat application. Initially I used blocking sockets which is the default condition. Now I want to convert the socket into non-blocking so that communication between client and server could be done without the obstacle of turns...
I've implemented the select function on the server side for now,but the when it starts the client gets to send a message once which is displayed on the server side,afterwards both client and server get unresponsive, so now I am showing how have I implemented the select() function on the server side:
//Declaring a non-blocking structure
fd_set readfds,writefds;
// clear the set ahead of time
FD_ZERO(&readfds);
FD_ZERO(&writefds);
// add our descriptor to the set
FD_SET(sd, &readfds);
FD_SET(sd, &writefds);
/value of sd+1
int n=sd+1;
Since I want to both receive and send data,I've implemented the select function in the loop:
int client_length = (int)sizeof(struct sockaddr_in);
int rv = select(n, &readfds, NULL, NULL, NULL);
if(rv==-1)
{
printf("Error in Select!!!\n");
exit(0);
}
else if(rv==0)
{
printf("Timeout occurred\n");
}
else
if (FD_ISSET(sd, &readfds))
{
int bytes_received = recvfrom(sd, buffer,SIZE, 0, (struct sockaddr *)&client, &client_length);
if (bytes_received < 0)
{
fprintf(stderr, "Could not receive datagram.\n");
closesocket(sd);
WSACleanup();
exit(0);
}
}
further for sending data:
fgets(buffer,SIZE,stdin);
int rv1 = select(n, &writefds, NULL, NULL, NULL);
if(rv1==-1)
{
printf("Error in Select!!!\n");
exit(0);
}
else if(rv1==0)
{
printf("Timeout occurred\n");
}
else
if(FD_ISSET(sd,&writefds))
{
if(sendto(sd, buffer,strlen(buffer), 0, (struct sockaddr *) &client,client_length)<0)
{
printf("Error sending the file! \n");
exit(1);
}
}
}
So I would really appreciate if somoone let me know whether I've done this right or not,if this is ok then will the same implelementation on the client side resolve my issue?
This is incorrect:
select(n, &writefds, NULL, NULL, NULL);
The second argument is used to check for readability only. To check for writability, use the third argument:
select(n, NULL, &writefds, NULL, NULL);

asynchronous udp server client application

I am trying to make an asynchronous UDP chat application, currently having only one client and server.
When I run my server, a lot of redundant data is displayed. Afterward, when some text is typed,
Error sending the file!
is displayed.
Could someone please look at the code and let me know where I am going wrong?
Server:
u_long iMode=1;
ioctlsocket(sd,FIONBIO,&iMode);
int n=sd+1;
fd_set readfds,writefds;
while(1)
{
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(sd,&readfds);
FD_SET(sd,&writefds);
int rv = select(n, &readfds, &writefds, NULL, NULL);
if(rv==-1)
{
printf("Error in Select!!!\n");
exit(0);
}
if(rv==0)
{
printf("Timeout occurred\n");
}
if (FD_ISSET(sd, &readfds))
{
FD_CLR(sd,&readfds);
int client_length = (int)sizeof(struct sockaddr_in);
memset(&buffer,0,sizeof(buffer));
int bytes_received = recvfrom(sd, buffer,SIZE, 0, (struct sockaddr *)&client, &client_length);
if (bytes_received < 0)
{
fprintf(stderr, "Could not receive datagram.\n");
closesocket(sd);
WSACleanup();
exit(0);
}
}
printf("\nClient says: %s",buffer);
printf("\nWrite :");
fgets(buffer,SIZE,stdin);
if(FD_ISSET(sd,&writefds))
{
FD_CLR(sd,&writefds);
int client_length = (int)sizeof(struct sockaddr_in);
if(sendto(sd, buffer,strlen(buffer), 0, (struct sockaddr *) &client,client_length)<0)
{
printf("Error sending the file! \n");
exit(1);
}
}
}
closesocket(sd);
WSACleanup();
return 0;
}
I see a problem. This line:
int rv = select(n, &readfds, &writefds, NULL, NULL);
Will nearly always return immediately with the readfds empty. But writefds will almost ALWAYS be set with the "sd" socket indicating that it is ready for writing/sending.
Hence, your code correctly skips the attempt to call recvfrom(), but nothing stops it from falling through to the sending code path. All of the variables such client, client_length, and buffer as are likely uninitialized at that point. Or worse, buffer and client are exactly what they were from the last successful call in the loop. That likely explains the redundant data.
My advice would be to not have a "writefds" set in the select call at all. Then only "send" when you actually read. The sendto call won't block for any significant amount of time anyway.

TCP client socket, doesn't block on select()

The following code is a test program wriiten to understand the behaviour of select() call in a TCP client program.
What I observe is that the select is not blocking, instead the program is blocking on recv().
The output is as follows:
Wait on select.
Wait on recv.
...
My question is why the select() returns a success? Ideally it should be blocking on the select() instead of recv().
The TCP server is sending a character string of 15 bytes once in 3 seconds.
int clientfd = -1;
int dummyfd = -1;
int maxfd = -1;
struct sockaddr_in server_addr;
char recv_buf[100] = {0};
int msg_len = 0;
int bytes_recv = 0;
fd_set readfd;
int retval = 0;
/* Open the socket and a dummy socket */.
clientfd = socket(AF_INET, SOCK_STREAM, 0);
dummyfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == clientfd || -1 == dummyfd)
{
perror("socket error: ");
exit(1);
}
printf("Socket opened : %d\n", clientfd);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);
//server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("127.0.0.1", &(server_addr.sin_addr));
memset(&(server_addr.sin_zero), 0, 8);
/* Connect to server */
if(connect(clientfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)))
{
perror("connect error: ");
exit(1);
}
printf("Connect Success\n");
maxfd = (clientfd > dummyfd) ? (clientfd + 1) : (dummyfd + 1);
while(1)
{
FD_ZERO(&readfd);
FD_SET(clientfd, &readfd);
FD_SET(dummyfd, &readfd);
printf("Wait on select\n");
retval = select(maxfd , &readfd, NULL, NULL, NULL);
if(retval <= 0)
{
printf("select failed\n");
}
else
{
printf("Wait on recv\n");
/* ... The process waits here ... */
bytes_recv = recv(clientfd, recv_buf, 100, 0);
printf("%d: Bytes recv = %d\t%s\n", retval, bytes_recv, recv_buf);
memset(recv_buf, 0 ,100);
}
}
close(clientfd);
return 0;
}
Edit: Without dummyfd, the program works as intended.
A follow up question:
When the server is closed abruptly, how to detect this using select()?
Can the program be modified so that is blocks on select() when the server side, say, crashes?
Use the following to be sure it's the clientfd that's returning from the select:
else if (FD_ISSET(clientfd, &readfd)) {
Don't have time to test, but I suspect the dummyfd is returning as an EOF from the select, not the clientfd.
After select() returns, you will want to conditionally receive from clientfd. My guess is that there may be data on dummyfd that is triggering the select to complete, but the receive is on the clientfd.
retval = select(maxfd , &readfd, NULL, NULL, NULL);
if(retval <= 0)
{
printf("select failed\n");
}
else
{
if (FD_ISSET(clientfd, &readfd))
{
bytes_recv = recv(clientfd, recv_buf, 100, 0);
...
}
if (FD_ISSET(dummyfd, &readfd))
{
/* "dummyfd" processing */
}

Resources