Understandng the reason for recv blocking forever - linux

I run a Linux program written in C that would periodically receive data by parsing an HTTP response, crunch some numbers and then report the result by HTTP GET of another web page.
My problem is that sometimes, one of the instances would "freeze".
Looking at top I can see that it is in sk_wait_data state and attaching a debugger reveals that it is blocked by a recv call.
Here is a minimal version of the code that does the TCP connection (it was adapted from http://www.linuxhowtos.org/C_C++/socket.htm):
int connectTCP(const char* host, const char* page, int portno) {
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
// Create socket //
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("ERROR opening socket");
// Get ip from hostname //
server = gethostbyname(host);
if (server == NULL)
error("ERROR, can not find host\n");
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy((char *)&serv_addr.sin_addr.s_addr, // Destination
(char *)server->h_addr, // Source
server->h_length); // Size
serv_addr.sin_port = htons(portno);
// Conect to socket //
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
return sockfd;
}
char* httpGet(const char* host, const char* page, int portno) {
int sockfd, n;
sockfd = connectTCP(host, page, portno);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", page, host);
n = send(sockfd,buffer,strlen(buffer), 0);
if (n < 0)
error("ERROR writing to socket");
int count = 0;
do {
n = recv(sockfd, buffer + count, BUFFER_MAX_SIZE - count, 0);
if (n < 0) {
error("ERROR reading from socket");
}
count += n;
} while(n != 0);
close(sockfd);
return buffer;
}

Bugs in your code:
If recv() returns zero you whould close the socket and stop reading.
If recv() returns -1 you should report the error, close the socket, and stop reading, unless you had set a read timeout and errno was EAGAIN/EWOULDBLOCK, in which case you should handle the timeout however is appropriate for your application.

Related

Can a single request image display, request page only display page content?

I use epoll to get the file descriptor that needs to be read, and when I request a page with an image, my socket only receives one request for the page, and no further events are fired.
When I requested the image individually, it successfully accepted the request and returned.
When I looked at the browser's request, it also requested twice, once for a page and once for an image.
I tried not to use epoll, but it still does.
There are times when I can request an image, such as when I go to the next page and return.
Also, when using epoll, the listening socket accepts multiple connections, shouldn't you only use the same connection? Why would it accept other connections when it seems to do nothing?
void Server::serverListen()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
int tmp = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
int ret = bind(listenfd, (sockaddr*)&addr, sizeof(addr));
assert(ret >= 0);
ret = listen(listenfd, 5);
assert(ret >= 0);
int epollfd = epoll_create(5);
assert(epollfd != -1);
epoll_event events;
events.data.fd = listenfd;
events.events = EPOLLIN;
int n = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &events);
assert(n != -1);
epoll_event eve[10000];
while (true)
{
int num = epoll_wait(epollfd, eve, 10000, -1);
assert(num != -1);
for (int i = 0; i < num; ++i)
{
int fd = eve[i].data.fd;
if (fd == listenfd)
{
sockaddr_in caddr;
socklen_t tmplen = sizeof(caddr);
connfd = accept(listenfd, (sockaddr*)&caddr, &tmplen);
assert(connfd != -1);
int flag = fcntl(connfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(connfd, F_SETFL, flag);
epoll_event even;
even.data.fd = connfd;
even.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
int n = epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &even);
assert(n != -1);
}
else
{
worker.init(eve[i].data.fd, epollfd, "localhost", "us", "mm", "usersdb", 3306, 5);
threadpool->add(&worker);
}
}
}
close(epollfd);
close(listenfd);
}

Does libevent support netlink socket

I use netlink to receive an interrupt number from kernel. The application in user space uses libevent to handle TCP/IP request and netlink message. Does libevent support Linux netlink socket? I will appreciate for a simple example.
Yes, libevent supports netlink socket.
There is https://github.com/libevent/libevent/blob/master/sample/hello-world.c, it is modified below to listen to netlink socket.
The basic example listens to Linux network interface creation/deletion and can be executed with sudo to gain privilege needed. It listens to same events as ip monitor link.
Another example of listening to RAW sockets with libevent is here https://github.com/bodgit/libevent-natpmp/blob/master/natpmp.c.
static void link_recvmsg(int fd, short event, void *arg)
{
char buf[NLMSG_SPACE(BUF_SIZE)] = {0};
socklen_t socklen;
struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
struct sockaddr addr;
memset(&addr, 0, sizeof(struct sockaddr));
if (!fd || -1 == fd)
return;
int status = getsockname(fd, &addr, &socklen);
if(-1 == status)
return;
struct msghdr mh = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = &iov, .msg_iovlen = 1,
.msg_flags = 0, .msg_name = &addr, .msg_namelen = sizeof(struct sockaddr)};
status = recvmsg(fd, &mh, 0);
if ((-1 == status) && ((EINTR == errno) || (EAGAIN == errno)))
return;
if(-1 == status)
return;
if ((mh.msg_flags & MSG_TRUNC) == MSG_TRUNC)
return;
if ((mh.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
return;
for (const struct nlmsghdr *h = (struct nlmsghdr *)buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) {
switch (h->nlmsg_type) {
case RTM_NEWLINK:
fprintf(stderr, "got RTM_NEWLINK\n");
break;
case RTM_DELLINK:
fprintf(stderr, "got RTM_DELLINK\n");
break;
default:
fprintf(stderr, "unexpected case in swtch statement\n");
break;
}
}
}
int main(int argc, char **argv)
{
/* some init code here */
/* NETLINK socket */
int status;
int buf_size = BUF_SIZE;
struct sockaddr_nl src_addr;
int socket_nl = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE);
if(-1 == socket_nl) return -1;
memset(&src_addr, 0, sizeof(struct sockaddr_nl));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups |= RTNLGRP_LINK;
status = setsockopt(socket_nl, SOL_SOCKET, SO_RCVBUF,
&buf_size, sizeof(buf_size));
if(-1 == status) return -1;
status = bind(socket_nl, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl));
if(status < 0) return -1;
static struct event nl_ev;
event_set(&nl_ev, socket_nl, EV_READ|EV_PERSIST, link_recvmsg,
NULL);
if (base) {
event_base_set(base, &nl_ev);
}
event_add(&nl_ev, NULL);
/* some other code, dispatch event and deinit */
}

Second time TFTP PUT opearation doesnt work after GET/PUT opearation is successfully finished first time on TFTP client side

Implementing the TFTP client over UDP socket.
First I create the UDP non blocking socket and perform the PUT/GET operation it works fine.
But when again I try to perform the GET/PUT it does not work. The RRQ/WRQ request itself is not reaching to the server but from the client side it has been sent successfully.
Below is my tftp client code.
=========================================================
int sockfd;
struct sockaddr_in serv_addr;
//called when we retrieve a file from the server
void getFile(int port, char *filename)
{
printf("enter to get file\n");
FILE * file;
if (strchr(filename,'/') != NULL )
{
printf("We do not support file transfer out of the current working directory\n");
return;
}
file = fopen(filename, "wb");
if(file == NULL)
{
perror(filename);
return;
}
if(sockfd < 0)
{
printf("Couldn't open socket\n");
return;
}
if(!send_RRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send RRQ\n");
return;
}
if(!recvFile(sockfd, (struct sockaddr *) &serv_addr, file,filename))
{
printf("Error: didn't receive file\n");
return;
}
fclose(file);
return;
}
//used to upload files to the server
void putFile(int port, char *filename)
{
printf("filenemae is: %s \t",filename);
PACKET packet;
int result;
FILE * fileh;
int timeout_counter = 0;
if (strchr(filename,'/') != NULL )
{
printf("We do not support file transfer out of the current working directory\n");
return;
}
fileh = fopen(filename, "rb");
if(fileh == NULL)
{
perror(filename);
return;
}
if(!send_WRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send WRQ to server\n");
return;
}
while (timeout_counter < MAX_TFTP_TIMEOUTS)
{
result = waitForPacket(sockfd, (struct sockaddr *) &serv_addr, TFTP_OPTCODE_ACK, &packet);
if (result < 0)
{
printf("Error: Timeout sending packet to server\n");
if(!send_WRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send WRQ to server\n");
return;
}
timeout_counter++;
}else
{
break;
}
}
if (result < 0)
{
//we still timed out
printf("Timed out after %d tries, is the server running\n",MAX_TFTP_TIMEOUTS);
fclose(fileh);
return;
}
if (packet.optcode == TFTP_OPTCODE_ERR)
{
//we recieved an error, print it
printError(&packet);
}else
{
if (!sendFile(sockfd, (struct sockaddr *) &serv_addr, fileh))
{
printf("Unable to send file to server\n");
}
}
fclose(fileh);
return;
}
int createUDPSocketAndBind(int port)
{
//create a socket
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
//return -1 on error
if (sockfd == -1)
{
return -1;
}
//zero out the struct
bzero((char*) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(port);
return 0;
}
//main client, checks for args and starts an operation if no errors detected
int main(int argc, char *argv[])
{
int port = 59;
int argOffset = 1;
char* filename;
char fme[] = "test.txt";
createUDPSocketAndBind(port);
printf("for put file\n");
putFile(port,fme); //=====> this operation is successful.
//createUDPSocketAndBind(port); //=====> If I uncomment this function, next operation works.
printf("for getfile\n");
getFile(port,fme); //=======> It is failing.
printf("Usage: %s [-p port] (-w putfile || -r getFile)\n",argv[0]);
}
=========================================================================
I have investigated more and found the problem with my code and solution as well.
When you send the first RD/WR request to the Server it will create a separate process or thread to handle your request and at the same time it will create the new socket and will use the different port number as well to handle the RD/WR request.
Client side when you will receive the response from the Server using below UDP socket API.
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
In the "struct sockaddr *src_addr" instance port number will be updated as server is responding using different port number which server
has created to handle your request and your RD/WR operation will complete successfully using this port number.
Once RD/WR is done successfully server will close the socket which has been created to handle you request and start listening to your new request
on the Original port. But client side "struct sockaddr *src_addr" instance will still have the modified port number and when you try to send the new RD/WR request it will not reach to the server. And that is the reason second RD/WR request will not reach to the server and it will fail.
To fix this on the client side in the "struct sockaddr *src_addr" instance you have to reset the port to the initial value which you have used initially to configure the server instance of the "struct sockaddr *src_addr". So after each RD/WR operation you have to reset the port number
to the original value.
I am quite new to the UDP socket and it was good learning for me.
I think this will be helpful for the beginners like me.
Thanks

exec other program and connect to it with unix domain

i exec a server program(it is a socket server, unix domain), and connect to it.
maybe the server is starting, so connect awalys fail with errno is ENOENT.
i have
setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
it still fails.
does anybody know a good solution.
thanks.
bool KRpcSelectThread::connectToServer(const string &port, timeval timeout)
{
int ret;
if ((m_socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
return false;
socklen_t len;
timeval oldtimeout;
if (getsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, &oldtimeout, &len) < 0 || len < 0)
oldtimeout = timeout;
len = sizeof(timeout);
setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
//int flag = 1;
//setsockopt(m_socketfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
struct sockaddr_un server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, port.c_str(), strlen(port.c_str()));
socklen_t server_addr_length = sizeof(server_addr);
ret = ::connect(m_socketfd, (struct sockaddr*)&server_addr, server_addr_length);
if (ret < 0)
{
int er = errno;
return false;
}
steps:
1. fork and exec another server program(listen unix domain)
2. run this function connectToServer
maybe server is starting(before listen), so it cause connect failed ?

how to read/write from/to a SOCK_SEQPACKET socket?

I try to use a SOCK_SEQPACKET socket with this:
int rc, len;
int worker_sd, pass_sd;
char buffer[80];
struct iovec iov[1];
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
memset(iov, 0, sizeof(iov));
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if((socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
{
perror("server: socket");
exit -1;
}
memset(&server_address, 0, sizeof(server_address));
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "/mysocket");
unlink("/mysocket");
if(bind(socket_fd, (const struct sockaddr *) &server_address, sizeof(server_address)) < 0)
{
close(socket_fd);
perror("server: bind error");
return 1;
}
while(1)
{
printf("wait for message\n");
bytes_received = recvmsg(socket_fd, &msg, MSG_WAITALL);
printf("%d bytes\n", bytes_received);
}
The problem is that the process does not wait but receives -1 from recvmsg and loops forever. Nowhere in the manpages is there any reference what functions shall be used with SOCK_SEQPACKET-style sockets, for example I am not really sure whether recvmsg is even the correct function.
SOCK_SEQPACKET is connection-orientated so you must first accept a connection then do your IO on the accepted client socket.
recvmsg() returns -1 when an error has occured - errno will be set to the error number.
Read here: http://pubs.opengroup.org/onlinepubs/009695399/functions/recvmsg.html

Resources