I have created a socket in C++ and I needed it to have certain connection timeout. So that's what is happening:
Create socket
Make it NON_BLOCKING
Call connect
It returns -1 and errno EINPROGRESS as expected
Call select
Returns >0, so connection has been made
Make the socket BLOCKING again
Code for this part is the following:
bool mastControl::prepareSocket(char * address, int port, int * sockfd) {
struct sockaddr_in serv_addr;
struct timeval timeout = {0,100000};
struct timeval connTimeout;
struct hostent * server = NULL;
fd_set socketSet;
socklen_t lon;
int sockOpt = 0;
long socketFlags = 0;
int buffersize = 8;
int res = 0;
int connectReturn = 0;
const int WAIT_TO_RECONN = 15;
server = gethostbyname(address);
*sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (*sockfd < 0) {
qDebug()<<"Impossible to open socket: "<<strerror(errno);
return false;
}
if (server == NULL) {
qDebug()<<"No such host: "<<strerror(h_errno);
return false;
}
// Initializating server direction struct:
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(port);
// Making socked non-blocking in order to set a timeout value for connection:
if((socketFlags = fcntl(*sockfd, F_GETFL, NULL)) < 0){
qDebug()<<"Impossible to retrieve sockets descriptor flags "<<strerror(errno);
return false;
}
socketFlags |= O_NONBLOCK;
if(fcntl(*sockfd, F_SETFL, socketFlags) <0){
qDebug()<<"Impossible to update sockets descriptor flags: "<<strerror(errno);
return false;
}
connectReturn = connect(*sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
if(connectReturn < 0){
if(errno == EINPROGRESS){
do{
// Establishing a 15 seconds timeout:
connTimeout.tv_sec = 15;
connTimeout.tv_usec = 0;
FD_ZERO(&socketSet); // Initialising set of sockets as an empty set
FD_SET(*sockfd, &socketSet); // Adding socket to set
connectReturn = select(*sockfd+1, NULL, &socketSet, NULL, &connTimeout);
if(connectReturn<0 && errno!=EINTR){ // Error in select
qDebug()<<"Connection error in select function: "<<strerror(errno);
return false;
}
else if(connectReturn>0){ // Socket selected for writing
lon = sizeof(int);
if(getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, (void*)(&sockOpt), &lon) <0){
qDebug()<<"Unnable to get socket options: "<<strerror(errno);
return false;
}
// Checking the value returned:
if(sockOpt){
qDebug()<<"Error in delayed connection: "<<strerror(errno);
return false;
}
break;
}
else{ // Timeout
qDebug()<<"Connection timeout exceeded: "<<strerror(errno);
return false;
}
} while (1);
}
else{
qDebug()<<"Connection error: "<<strerror(errno);
sleep(WAIT_TO_RECONN); // Wait 15 seconds
return false;
}
}
//Connected
// Must set the socket as blocking again:
if((socketFlags = fcntl(*sockfd, F_GETFL, NULL)) < 0){
qDebug()<<"Impossible to retrieve sockets descriptor flags "<<strerror(errno);
return false;
}
socketFlags &= (~O_NONBLOCK);
if(fcntl(*sockfd, F_SETFL, socketFlags) <0){
qDebug()<<"Impossible to update sockets descriptor flags "<<strerror(errno);
return false;
}
if (setsockopt (*sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout)) < 0) {
qDebug()<< "ERR - setsockopt failed";
return false;
}
if (setsockopt (*sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout)) < 0) {
qDebug()<< "ERR - setsockopt failed";
return false;
}
if ((res = setsockopt (*sockfd, SOL_SOCKET, SO_SNDBUF, &buffersize,
sizeof(buffersize))) == -1) {
qDebug()<< "ERR - setsockopt failed (SO_SNDBUF) = " << res;
return false;
}
//Socket Ready
return true;
}
That works ok. But then I have a loop where I'm calling a function which checks if there is a new packet received to read:
bool mastControl::findPacket(int sockfd, st_messageMastToPc * messageReceived, bool * connected) {
int n = 0;
bool messageFound = false;
char * buffer = (char *) messageReceived;
unsigned int pos = 0;
while ( ((n = read(sockfd, &(buffer[pos]), 1)) > 0) and not messageFound) {
//qDebug() << "read output " << n;
if (n == 1) {
pos++;
if ( (pos == 1) && (buffer[0] == 2)) {
// Some stuff...
} else if ( (pos == 2) && (buffer[1] == 2) ) {
// Some stuff...
} else if (pos >= uiMessageMastToPcSize) {
messageFound = true;
//Complete message received
} else if (pos < 2) {
// Reseting pos
pos = 0;
}
}
}
if (n < 0){
qDebug()<< "Disconnected. Reason #" << errno << ": " << strerror(errno);
*connected = false;
}
return messageFound;
}
Read function is giving EAGAIN as errno, which means "Resource temporarily unavailable". Then I'm supposing I am disconnected, and since boolean connected is now false, in the loop I will be trying to reconnect creating the socket again, ans so on.
So, in the first place I don't know why I'm receiving this error.
In the second place, I don't know if I am disconnected at all, or it's me the one who is disconnecting when creating the new socket after this error.
Any help?
EAGAIN does not mean you're disconnected, it just means "there's nothing to read now; try again later".
You could either unset O_NONBLOCK with fcntl(2) (making read wait until there's something available), or just wait on the socket with something like select(2) before calling read.
EDIT: Now that you've added more code, I can see that you're setting SO_RCVTIMEO for the socket. This can cause a blocking read to return EAGAIN (so if you don't want that to happen, simply leave SO_RCVTIMEO alone).
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm a C++ beginner learning I/O multiplexing.
Here is my code:
test.cpp (doesn't use epoll() and works well):
#include <iostream>
#include <cstdio>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int main() {
std::cout << "Hello" << std::endl;
char buffer[1024];
buffer[0] = 'f';
fprintf(stdout, "%s", buffer);
std::cout << "Hello" << std::endl;
int serverFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
// bind & listen
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(80);
int bindResult = bind(serverFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if (bindResult < 0) {
fprintf(stderr, "Fail to bind\n");
return 1;
}
int listenResult = listen(serverFd, 1024);
if (listenResult < 0) {
fprintf(stderr, "Fail to listen\n");
return 1;
}
struct sockaddr clientAddr;
unsigned int clientlen = sizeof(clientAddr);
int acceptFd = accept(serverFd, &clientAddr, &clientlen);
if (acceptFd < 0) {
fprintf(stderr, "Fail to create client connection file descriptor\n");
return 1;
}
int fd = acceptFd;
ssize_t received = recv(fd, &buffer, 1024, 0);
if (received < 0) {
fprintf(stderr, "Fail to received bytess from client\n");
if (errno == EINTR) {
fprintf(stderr, "Reason: EINTR\n");
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "Reason: EAGAIN or EWOULDBLOCK\n");
} else {
fprintf(stderr, "Reason: %d\n", errno);
close(fd);
return 1;
}
} else if (received == 0) {
close(fd);
} else {
buffer[received] = '\0';
fprintf(stdout, "%s", buffer);
}
}
test_2.cpp (does use epoll() and doesn't work well):
#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
int main() {
// TODO: too much error message to handle, so it's necessary to deal with it (maybe macros can)
std::cout << "Hello" << std::endl;
// process ignore SIGPIPE which is caused by send(), or process will exit, which is hard to find out
signal(SIGPIPE, SIG_IGN);
// here needs a socket fd or other fd
// well, AF_INET is okay;socket(PF_INET, SOCK_SEQPACKET, 0) is sctp, tcp cannot use SOCK_SEQPACKET :(
// when using tcp, watch out **record boundaries**
int serverFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverFd < 0) {
fprintf(stderr, "Fail to create socket file descriptor\n");
return 1;
}
// nonblock
// nonblock
int flags = fcntl(serverFd, F_GETFL, 0);
if (flags < 0) {
fprintf(stderr, "Fail to get flags\n");
return 1;
}
int setFlagResult = fcntl(serverFd, F_SETFL, flags | O_NONBLOCK);
if (setFlagResult < 0) {
fprintf(stderr, "Fail to set flags\n");
return 1;
}
// bind & listen
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(80);
int bindResult = bind(serverFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if (bindResult < 0) {
fprintf(stderr, "Fail to bind\n");
return 1;
}
int listenResult = listen(serverFd, 1024);
if (listenResult < 0) {
fprintf(stderr, "Fail to listen\n");
return 1;
}
// epoll fd
int epollFd = epoll_create(1);
if (epollFd < 0) {
fprintf(stderr, "Fail to create epoll file descriptor\n");
return 1;
}
// event
struct epoll_event event, events[1024];
event.events = EPOLLIN;
event.data.fd = serverFd;
// ctl
int ctlResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, serverFd, &event);
if (ctlResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
// wait
while (1) {
int event_count = epoll_wait(epollFd, events, 1024, -1);
for (int i = 0; i < event_count; i++) {
struct epoll_event event = events[i];
// accept
if (event.data.fd == serverFd) {
unsigned int clientlen = sizeof(clientAddr);
int acceptFd = accept(serverFd, (struct sockaddr *) &clientAddr, &clientlen);
if (acceptFd < 0) {
fprintf(stderr, "Fail to create client connection file descriptor\n");
fprintf(stderr, "Fail Reason: %d\n", errno);
return 1;
}
// nonblock
int flags = fcntl(acceptFd, F_GETFL, 0);
if (flags < 0) {
fprintf(stderr, "Fail to get flags\n");
return 1;
}
int setFlagResult = fcntl(acceptFd, F_SETFL, flags | O_NONBLOCK);
if (setFlagResult < 0) {
fprintf(stderr, "Fail to set flags\n");
return 1;
}
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = serverFd;
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, acceptFd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
// client recv
} else if (event.events & EPOLLIN) {
int fd = event.data.fd;
char buffer[1024+1];
ssize_t received = recv(fd, &buffer, 1024, 0);
if (received < 0) {
fprintf(stderr, "Fail to received bytess from client\n");
if (errno == EINTR) {
fprintf(stderr, "Reason: EINTR\n");
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "Reason: EAGAIN or EWOULDBLOCK\n");
} else {
fprintf(stderr, "Reason: %d\n", errno);
close(fd);
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
return 1;
}
} else if (received == 0) {
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
close(fd);
} else {
buffer[received] = '\0';
fprintf(stdout, "%s", buffer);
// if you want to send something...
event.events |= EPOLLOUT;
// here is some data that event can hold
event.data.u32 = (uint32_t) 1;
// you can now send data or just put event in epoll, which is maybe easier
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
}
// client send
} else if (event.events & EPOLLOUT) {
int fd = event.data.fd;
char buffer[] = "I see you";
ssize_t sendResult = send(fd, &buffer, 1024, 0);
if (sendResult < 0) {
fprintf(stderr, "Fail to received bytess from client\n");
if (errno == EINTR) {
fprintf(stderr, "Reason: EINTR\n");
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "Reason: EAGAIN or EWOULDBLOCK\n");
} else {
if (errno == EPIPE) {
fprintf(stderr, "Reason: EPIPE\n");
} else {
fprintf(stderr, "Reason: %d\n", errno);
}
close(fd);
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
return 1;
}
} else if (sendResult == 0) {
event.events = EPOLLIN;
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &event);
if (ctlClientResult < 0) {
fprintf(stderr, "Fail to run epoll_ctl\n");
return 1;
}
} else {
// if you want to recv something...
// event.events |= EPOLLIN;
// // you can now send data or just put event in epoll, which is maybe easier
// int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &event);
// if (ctlClientResult < 0) {
// fprintf(stderr, "Fail to run epoll_ctl\n");
// return 1;
// }
}
}
}
}
return 0;
}
When I try to make a TCP socket connection (such as curl -v "http://host:80/", which can make test2.cpp run in line 81), acceptFd is < 0 and errno is 11 according to line 84, which means "Resource deadlock avoided".
Why? There isn't any thread-related code, is it?
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = serverFd;
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, acceptFd, &event);
When you add the newly-accepted connection to the epoll set, you tell it to report it as a hit on serverFd. So when the client sends you data, you try to accept a new connection instead.
Change event.data.fd = serverFd to event.data.fd = acceptFd.
Then you can move on to your next bug:
char buffer[] = "I see you";
ssize_t sendResult = send(fd, &buffer, 1024, 0);
1024?!
Also, any time you use non-blocking sockets, you should add code to handle EAGAIN or EWOULDBLOCK errors as non-fatal.
Error code 11 is EAGAIN, which is a very common error code to encounter when dealing with non-blocking socket I/O. It means the requested operation has nothing to do, try it again later.
In the case of accept(), it means:
EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 and POSIX.1-2008 allow either error to be returned for this case, and do not require these constants to have the same value, so a portable application should check for both possibilities.
This means you are calling accept() at the wrong time, when the listening socket does not have any clients to accept. Double check your epoll() usage, it likely has a logic bug in it that is causing you to call accept() prematurely.
For instance, after accept() has successfully accepted a client, you are assigning the listening socket instead of the client socket to event.data.fd when calling epoll_ctl(EPOLL_CTL_ADD):
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = serverFd; // <-- HERE, SHOULD BE acceptFd INSTEAD!
int ctlClientResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, acceptFd, &event);
As such, when the client has data waiting to be read, your loop will end up calling accept() on the listening socket instead of calling recv() on the client socket.
Also, you are not checking the reported event.events field for socket errors. If an error occurs on a client socket, the reported events may include the EPOLLERR and/or EPOLLHUP flags. You should check for those flags and if present then close the client socket, before checking for the EPOLLIN flag to call recv().
Also, note that in your example, it is not necessary to call epoll_ctl(EPOLL_CTL_DEL) before calling close() on a socket. Closing a socket file descriptor will automatically remove it from the epoll instance that is monitoring it.
Hi everyone i have a little problem, i supposed to transfer a file from a server( a tcp server with threads to a client). The problems appers at the end of transmision the file is recived by client but it stucks and I can't longer communicate with it.
This is the server
int main(int argc, char *argv[])
{
int socket_desc, client_sock, c;
struct sockaddr_in server, client;
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(2025);
//Bind
if (bind(socket_desc, (struct sockaddr *) &server, sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc, 5);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
int enable = 1;
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
pthread_t thread_id;
while ((client_sock = accept(socket_desc,
(struct sockaddr *) &client,
(socklen_t*) &c)))
{
puts("Connection accepted");
if (setsockopt(client_sock,
SOL_SOCKET,
SO_REUSEADDR,
&enable,
sizeof(int)) < 0)
error("setsockopt(SO_REUSEADDR) failed");
if (pthread_create(&thread_id,
NULL,
connection_handler,
(void*) &client_sock) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
pthread_join(thread_id, NULL);
puts("Handler assigned");
}
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
return 0;
}
void *connection_handler(void *socket_desc)
{
printf("Enter in handler");
//Get the socket descriptor
int sock = *(int*) socket_desc;
send_problemo(sock);
return 0;
}
This is the sending function where I think is the real problem
int send_problemo(int *sock)
{
ssize_t read_return;
char *file_path = "Problems/1.txt";
char buffer[BUFSIZ];
int filefd;
filefd = open(file_path, O_RDONLY);
char end[2] = "1";
if (filefd == -1)
{
perror("open");
exit (EXIT_FAILURE);
}
while (1)
{
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
{
printf("este 0 \n");
break;
}
if (read_return == -1)
{
perror("read");
exit (EXIT_FAILURE);
}
if (write(sock, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
}
// close(sock);
close(filefd);
}
The client is connecting normally and receives the file in this function
int recive_problemo(int *sockfd)
{
char *file_path = "path.c";
char buffer[BUFSIZ];
ssize_t read_return;
int filefd;
filefd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (filefd == -1)
{
perror("open");
exit (EXIT_FAILURE);
}
do
{
read_return = read(sockfd, buffer, BUFSIZ);
if (read_return == -1)
{
perror("read");
exit (EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
} while (read_return > 0);
close(filefd);
}
I kind of managed how to solve this. If i shutdown(SHUT_WR) from server the client isnt stuck anymore, but i want to communicate with it further.
Also with the same function if i transfer from client to server, it works perfectly, so can anyone help me please?
do
{
read_return = read(sockfd, buffer, BUFSIZ);
// error handling
// file write
} while (read_return > 0);
Will keep looping until the socket closes or there's an error. It has no way to tell if a file has finished.
Common solutions are to close the socket (but you don't want that) and establish a communication protocol so that you know when the file is done and can exit the loop.
To keep things very simple, I recommend sending the length of the file before sending the file. The loop now looks something like:
uint64_t file_len;
read_return = recv(sockfd, &file_len, sizeof(file_len), MSG_WAITALL);
if (read_return == sizeof(file_len))
{
// Strongly consider handling the endian of file_len here
while (file_len)
{
size_t readmax = std::min(file_len, BUFSIZ);
read_return = read(sockfd, buffer, readmax);
if (read_return > 0)
{
if (write(filefd, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
file_len -= read_return;
}
else
{
// handle error
// exit loop if not recoverable
}
}
}
The server end picks up the responsibility of getting and sending the length of the file. I won't get into that because there are too many different ways to get the length of a file. Pick your favourite.
Documentation on recv and MSG_WAITALL.
What I'm trying to do is a forking proxy that deals HTTP(S) connections: while GET (without SSL) requests are successfully executed and the contents are delivered to the client, when it comes to CONNECT method things are not going well, since connect()ing to the remote server may not immediately succeeds: in fact, it nevers succeeds.
I tried for a non blocking socket connected to the remote server, so I can see if connect() goes immediately or takes some time: in the second case, I'd call select() to see when the remote server is ready to send data to me: yet, connect() never connects.
Here's my proxy main() code:
int main(int argc, char *argv[]) {
// ClientManager.cpp is described below
ClientManager cm;
//listening on port given by argv
if (cm.startListeningForClient(listening_port)) {
while(true) {
int new_client_socket = cm.acceptConnectionFromClient();
if (new_client_socket >= 0) {
cm.forkAndManageClient();
}
else {
perror("accept error");
}
}
} else {
perror("Error on start listening");
}
return EXIT_SUCCESS;
}
Now follows, with some omissis not involved with my issue, ClientManager.cpp, whose functions are called in main() above:
ClientManager::ClientManager() {
sockfd_client = -1; // socket connected to client
new_sockfd_client = -1; // socket accepting connection from client
sockfd_server = -1; // socket connected to remote server
}
// error controls omitted
bool ClientManager::startListeningForClient(int port) {
struct sockaddr_in serv_addr;
bzero((char*)&serv_addr,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(port);
serv_addr.sin_addr.s_addr=INADDR_ANY;
sockfd_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(sockfd_client,(struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(sockfd_client, 50);
return true;
}
// error controls omitted
int ClientManager::acceptConnectionFromClient(void) {
struct sockaddr_in cli_addr;
unsigned int clilen;
bzero((char*)&cli_addr, sizeof(cli_addr));
clilen = sizeof(cli_addr);
new_sockfd_client = accept(sockfd_client, (struct sockaddr*)&cli_addr, &clilen);
return new_sockfd_client;
}
int ClientManager::forkAndManageClient() {
// getRequestFromClient: the method below receives requests from
// clients and parses the infos I need (i.e. what method,
// hostname of remote server to be resolved, its port, ...)
getRequestFromClient();
// managing the HTTP(S) request by the child process
int pid = fork();
if (pid < 0) {
perror("ERROR on fork");
}
else if (pid > 0) {
// parent process
// do nothing
}
else {
// close immediately the client socket used for accepting new connections from the parent process
close (sockfd_client);
if (!manageRequest()) {
perror("Error managing request from client");
}
// close the connection from the client
close (new_sockfd_client);
new_sockfd_client = -1;
// the child process will terminate now
_exit(EXIT_SUCCESS);
}
return pid;
}
// now the problematic method...
bool ClientManager::manageRequest(void) {
// if this is a CONNECT request
if (rm.isCONNECT()) {
struct sockaddr_in remote_server;
int conn_res;
remote_server.sin_family = AF_INET;
remote_server.sin_addr.s_addr = rm.getServerAddr();
remote_server.sin_port = rm.getServerPort();
fd_set fdset;
struct timeval tv;
sockfd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// make socket not blocking
int flags = fcntl(sockfd_server, F_GETFL, 0);
flags = flags | O_NONBLOCK;
if (fcntl(sockfd_server, F_SETFL, flags) == -1) {
perror("FCNTL:");
}
printf("CONNECT set socket to non-blocking mode\n");
conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
printf("AFTER CONNECT()\n");
if (conn_res < 0) {
if (errno != EINPROGRESS) {
printf("CONNECT: connect() failed, quitting\n");
return false;
}
}
printf("CONNECT connection is taking place...\n");
// connected immediately
if (conn_res == 0) {
printf("CONNECT connected OK!\n");
goto CONNECTED;
}
FD_ZERO(&fdset);
FD_SET(sockfd_server, &fdset);
tv.tv_sec = 5; // tried 5, 20, 60 seconds, but it always times out
tv.tv_usec = 0;
printf("CONNECT attempting select()\n");
if (select(sockfd_server+1, NULL, &fdset, NULL, &tv) == 0) {
errno = ETIMEDOUT;
close(sockfd_server);
sockfd_server = -1;
return false;
}
if (FD_ISSET(sockfd_server, &fdset)) {
int so_error;
socklen_t len = sizeof so_error;
if (getsockopt(sockfd_server, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
return false;
}
} else {
printf("sockfd_server not set\n");
}
CONNECTED:
fcntl(sockfd_server, F_SETFL, flags &~ O_NONBLOCK);
// yeah, now I will start to deal the HTTPS flow in both directions
return true;
}
}
It does manage setting socket to non blocking mode, and to print CONNECT connection is taking place..., but it always returns Error managing request from client: Connection timed out.
I apologize for posting miles of LOC, but this is what drives me crazy since days, and after reading posts, tutorial and guides, I really don't know what to do.
It connects now to every site which requires an HTTPS connection!
Proper error checking and following closing of socket descriptors were missing. Here's my code:
bool ClientManager::manageRequest(void) {
if (rm.isCONNECT()) {
struct sockaddr_in remote_server, local_bind;
int conn_res, select_res;
memset(&remote_server, 0, sizeof(remote_server));
remote_server.sin_family = AF_INET;
remote_server.sin_addr.s_addr = rm.getServerAddr();
remote_server.sin_port = rm.getServerPort();
memset(&local_bind, 0, sizeof(local_bind));
local_bind.sin_family = AF_INET;
local_bind.sin_addr.s_addr = htonl(INADDR_ANY);
local_bind.sin_port = htons(0);
fd_set rdset, wrset;
struct timeval tv;
sockfd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd_server < 0) {
perror("socket: ");
}
if(!setNonBlocking(sockfd_server))
perror("fcntl");
debug_green("CONNECT set socket to non-blocking mode\n");
bind(sockfd_server, (struct sockaddr*) &local_bind, sizeof(local_bind));
conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
// The socket is nonblocking and the connection cannot be completed immediately
// check for EINPROGRESS
if ((conn_res == -1) && (errno != EINPROGRESS)) {
FD_ZERO(&rdset);
FD_SET(sockfd_server, &rdset);
wrset = rdset;
tv.tv_sec = 0;
tv.tv_usec = 0;
debug_yellow("CONNECT attempting select()\n");
do {
select_res = select(sockfd_server+1, &rdset, &wrset, NULL, &tv);
} while ((select_res == -1) && (errno == EINTR));
if ((!FD_ISSET(sockfd_server, &rdset)) && ((!FD_ISSET(sockfd_server, &wrset)))) {
debug_red("SELECT sockfds not responding\n");
close(sockfd_server);
sockfd_server = -1;
return false;
}
conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
if (conn_res == -1) {
if(errno == EISCONN)
printf ("connect(): connections already existing, OK\n");
else {
printf("connect() for safety check: connection NOT successfull\n");
close(sockfd_server);
sockfd_server = -1;
return false;
}
}
printf("connection OK\n");
fflush(stdout);
} else {
debug_green("Connection immediately OK\n");
fflush(stdout);
}
if (!setBlocking(sockfd_server)) {
perror("FCNTL:");
}
debug_green("CONNECT set socket back to blocking mode\n");fflush(stdout);
}
return true;
}
Functions setting blocking or non blocking socket:
bool ClientManager::setNonBlocking(int sockfd) {
printf("setting non block socket\n"); fflush(stdout);
int flags;
if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
return false;
flags |= O_NONBLOCK;
if (fcntl(sockfd, F_SETFL, flags) < 0)
return false;
return true;
}
bool ClientManager::setBlocking(int sockfd) {
printf("setting block socket\n"); fflush(stdout);
int flags;
if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
return false;
flags &= (~O_NONBLOCK);
if (fcntl(sockfd, F_SETFL, flags) < 0)
return false;
return true;
}
Debug functions:
#define DEFAULTCOLOR "\033[0m"
#define RED "\033[22;31m"
#define YELLOW "\033[1;33m"
#define GREEN "\033[0;0;32m"
#define debug_red(...) std::cout << RED << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_yellow(...) std::cout << YELLOW << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_green(...) std::cout << GREEN << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
I'm working on a c++ class that acts as a high-level wrapper around sockets in linux. While testing it, I purposely made the server's accept() call time out by having the client application sleep for a few seconds before calling connect().
However, after the server times out, the client application is still able to call connect() and send data without detecting an error. This is obviously a problem, because the server is not receiving the data, so the client should know the connection failed.
Here is my code. The server app calls Socket::accept_connection() and the client app sleeps and then calls Socket::connect_to().
// Accept a connection on the server side with a timeout
Socket *Socket::accept_connection(double timeout) {
Socket *new_connection = NULL;
socklen_t sin_size;
struct sockaddr_storage client_address; // Client's address
struct sockaddr_in client_port_address; // Client's port
char s[INET6_ADDRSTRLEN];
sin_size = sizeof client_address;
fd_set rfds;
struct timeval timeout_structure;
timeout_structure.tv_sec = (long)(timeout);
timeout_structure.tv_usec = int((timeout - timeout_structure.tv_sec) * 1e6);
struct timeval *timeout_ptr = NULL;
if(timeout > 0)
timeout_ptr = &timeout_structure;
// Loop until the timeout has been reached
while(true) {
FD_ZERO(&rfds);
FD_SET(socket_desc, &rfds);
if(select(socket_desc + 1, &rfds, NULL, NULL, timeout_ptr) > 0) {
int client_sock = accept(socket_desc, (struct sockaddr *)&client_address, &sin_size);
if(client_sock == -1) {
// Failed to connect
connected = false;
continue;
} else {
// Connected
inet_ntop(client_address.ss_family, get_in_addr((struct sockaddr *)&client_address), s, sizeof s);
getpeername(client_sock, (struct sockaddr*)&client_port_address, &sin_size);
int client_port = ntohs(client_port_address.sin_port);
// ...
}
} else {
// Timed out
connected = false;
std::cout << "accept() timed out\n";
break;
}
}
return new_connection;
}
// Connect to the given ip address and port
bool Socket::connect_to(std::string server_ip, int server_port, double timeout) {
connected = false;
// Create the socket and allocate memory for reading in data
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
struct timeval timeout_structure;
timeout_structure.tv_sec = (long)(timeout);
timeout_structure.tv_usec = int((timeout - timeout_structure.tv_sec) * 1e6);
struct timeval *timeout_ptr = NULL;
if(timeout > 0)
timeout_ptr = &timeout_structure;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(server_ip.c_str(), std::to_string(server_port).c_str(), &hints, &servinfo)) != 0) {
fprintf(stderr, "Socket error: connect_to, getaddrinfo: %s\n", gai_strerror(rv));
throw;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((socket_desc = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("Socket error: connect_to, socket");
continue;
}
int flags = 0, error = 0, ret = 0;
fd_set rset, wset;
socklen_t len = sizeof(error);
//clear out descriptor sets for select
//add socket to the descriptor sets
FD_ZERO(&rset);
FD_SET(socket_desc, &rset);
wset = rset; //structure assignment ok
//set socket nonblocking flag
if((flags = fcntl(socket_desc, F_GETFL, 0)) < 0)
continue;
if(fcntl(socket_desc, F_SETFL, flags | O_NONBLOCK) < 0)
continue;
//initiate non-blocking connect
if(ret = connect(socket_desc, p->ai_addr, p->ai_addrlen) == -1) {
if (errno != EINPROGRESS) {
close(socket_desc);
perror("Socket error: connect_to, could not connect");
continue;
}
}
if(ret != 0) { // If connect did not succeed right away
// We are waiting for connect to complete now
if((ret = select(socket_desc + 1, NULL, &wset, NULL, timeout_ptr)) < 0)
return false;
if(ret == 0){ //we had a timeout
errno = ETIMEDOUT;
return false;
}
//we had a positive return so a descriptor is ready
if(FD_ISSET(socket_desc, &rset) || FD_ISSET(socket_desc, &wset)){
if(getsockopt(socket_desc, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error != 0)
return false;
} else
return false;
if(error){ //check if we had a socket error
errno = error;
return false;
}
}
//put socket back in blocking mode
if(fcntl(socket_desc, F_SETFL, flags) < 0)
return false;
break;
}
if(p == NULL) {
fprintf(stderr, "Socket error: connect_to, failed to connect\n");
socket_desc = 0;
return false;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
freeaddrinfo(servinfo); // all done with this structure
connected = true;
return connected;
}
This is normal, not a problem.
TCP maintains a listen backlog queue into which connections are placed that have been completed by the TCP stack but not yet accepted by the application.
TCP maintains a socket receive buffer per socket into which data is placed that has arrived from the peer and not yet been read by the application.
the client should know the connection failed.
It didn't fail. The server can accept it and read the data.
I'm trying to implement a pair server/client programs that is able to exchange information through Unix Sockets. Problem is the client is constantly being reset and re-run by the server in a loop, and I'm having trouble sending/receiving data to and from the socket after the first time. Right now I have something like this:
Server:
int main(int argc, char const *argv[]){
unix_socket server;
server.initSocket(DEFAULT_SOCKET_PATH,SERVER_MODE);
server.wait();
bool first=true;
for (int i = 0; i < 30; ++i)
{
//send & receive data
server.closeSocket();
first=false;
}
return 0;
}
Client:
int loop_controller(){
unix_socket client;
client.initSocket(DEFAULT_SOCKET_PATH,CLIENT_MODE);
// receive & send data
client.closeSocket();
}
int main () {
for (int i = 0; i < 30; ++i){
loop_controller();
}
return 0;
}
The initSocket(), wait() and closeSocket() members of unix_socket class (mode 0 for servers, mode 1 for clients):
void unix_socket::initSocket(const char* sock_path, const int sc_mode){
if (sc_mode==0){
if (mode != sc_mode) mode=sc_mode;
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
//exit(1);
}
// ---
local.sun_family = AF_UNIX;
strcpy(local.sun_path, sock_path);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(sock, (struct sockaddr *)&local, len) == -1) {
close(sock);
perror("bind");
//exit(1);
}
// ---
} else if(sc_mode==1) {
if (mode != sc_mode) mode=sc_mode;
if ((client_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
//exit(1);
}
// ---
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, sock_path);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(client_sock, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
//exit(1);
}
// ---
} else printf("Invalid sc_mode argument: %d\n",sc_mode);
}
//...
void unix_socket::wait(){
if (mode==0){
if (listen(sock, 5) == -1) {
perror("listen");
//exit(1);
}
t = sizeof(remote);
if ((client_sock = accept(sock, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
//exit(1);
}
} else printf("Invalid call to wait(): mode %d\n, must be 0",mode);
}
//...
void unix_socket::closeSocket(){
//close(sock);
close(client_sock);
}
//...
When running both programs with Valgrind, however, I'm getting SIGPIPE errors:
==5384==
==5384== Process terminating with default action of signal 13 (SIGPIPE)
==5384== at 0x573DDA2: send (send.c:28)
==5384== by 0x405910: unix_socket::sendMsg(char, double) (in .../server)
==5384== by 0x405B6C: unix_socket::sendVectorXd(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&) (in .../server)
==5384== by 0x402044: main (in .../server)
==5384==
I'm guessing that something is not being done correctly when closing/reopening socket. What would be the right procedure?
In your loop, you are only accept()ing once. The end of the loop body indicates the client socket being closed, but yet it is never accept()ed again for the next iteration.
Your loop should be something like:
server.startListening(); // bind, listen and friends
for (int i = 0; i < 30; i++) {
server.acceptClient(); // Call accept
// insert byte pushing routines here..
server.closeSocket(); // call close() on socket returned by accept()
}