socket programming: 'accept: Bad file descriptor' - c++

I'm trying to write a game that will let multiple clients connect and play - below is the relevant code (it's very messy - cleaning up later):
Edit: I realized it's a lot of scrolling... the crash occurs towards the end of the game during:
std::cout << black_hits << " black hits & " << white_hits
<< " white hits.\n";
if (black_hits == 4) {
std::cout << "you won!\n";
std::cin.ignore().get();
close(client); //<<<< CRASH HERE
return 0;
}
Not really a crash I guess... but close enough :)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define BACKLOG 10
#define MAXDATASIZE 100
typedef enum {RED,GREEN,BLUE,YELLOW,ORANGE} color;
int StartMasterMind(int client, sockaddr_storage addr_in);
struct msgstruct {
int length;
char* send_data;
};
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int tcp_connect(const char *serv, const char *host = NULL)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(host, serv, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
//if (send(new_fd, "Hello, world!", 13, 0) == -1)
// perror("send");
//close(new_fd);
StartMasterMind(new_fd,their_addr);
// exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
void InitializeGame(const char* port)
{
tcp_connect(port);
}
std::vector<color> GetInputAsColorMap(char* input)
{
[...]//redacted for clarity
}
int StartMasterMind(int client, sockaddr_storage addr_in)
{
struct msgstruct message;
struct sockaddr_storage their_addr = addr_in;
socklen_t addr_len;
message.send_data = "Welcome to ... M A S T E R M I N D.\n";
message.length = strlen(message.send_data);
send(client, message.send_data, message.length, 0);
[...]//redacted for clarity
if (strcmp(theValue, "random") == 0 || strcmp(theValue, "Random") == 0)
{
[...]//redacted for clarity
}
else
{
[...]//redacted for clarity
}
char* buf;
for (int i = 0; i < 8; ++i) {
std::vector<color> current_try(4);
int black_hits = 0, white_hits = 0;
std::vector<int> correctColorIndex;
std::vector<int> correctColor;
bool exclude[4] = {false};
std::cout << "test\n";
message.send_data = "Please enter your guess: ";
message.length = strlen(message.send_data);
send(client, message.send_data, message.length, 0);
addr_len = sizeof their_addr;
std::cout << "addr_len: " << addr_len << std::endl;
recvfrom(client, buf, MAXDATASIZE-1, 0, (struct sockaddr *)&their_addr, &addr_len);
current_try = GetInputAsColorMap(buf);
std::cout << "the buffer: " << buf << std::endl;
std::cout << "current_try: " << current_try[0] << current_try[1] << current_try[2] << current_try[3] << std::endl;
[...]//redacted for clarity
std::cout << black_hits << " black hits & " << white_hits
<< " white hits.\n";
if (black_hits == 4) {
std::cout << "you won!\n";
std::cin.ignore().get();
close(client); //<<<< CRASH HERE
return 0;
}
}
[...]//redacted for clarity
}
int main(int argc, char** argv)
{
InitializeGame(argv[1]);
return 0;
}
Here is sample output:
server: waiting for connections...
server: got connection from 127.0.0.1
value or random:
1122
test
addr_len: 128
the buffer: 1123�
current_try: 1123
3 black hits & 0 white hits.
test
addr_len: 128
the buffer: 1223�
current_try: 1223
2 black hits & 1 white hits.
test
addr_len: 128
the buffer: 1122�
current_try: 1122
4 black hits & 0 white hits.
you won!
accept: Bad file descriptor
accept: Bad file descriptor
accept: Bad file descriptor
... // continuously, hundreds of times
I'm very new to socket programming; could someone give me a hand? This crashes with or without trying to close(client) at the end of the game.

I think when the child process is wrapping back to start of while(1) loop, it tries to accept a connection with server socket descriptor = "sockfd" which you already closed for the child:
if (!fork()) { // this is the child process
close(sockfd);
....
}
Try this link to read as how to terminate the child process after its work is complete.

That message means that you're calling accept() on an invalid file descriptor, i.e. probably one that you've closed.

Related

accept socket errno 9 c++

New to socket programming, trying to implement a TCP server, but accept() returns -1 and errno = 9 (bad file descriptor). The problem arose after I introduced the accept() function in the freertos task. I really don’t understand what the problem is here, because before this my action everything worked, but I need to use the task.
#include "TCP.hpp"
#include <iostream>
#include <cmath>
#include <cerrno>
#include <cstring>
#include <clocale>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "freertos/queue.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "ping/ping_sock.h"
#include "errno.h"
static const char *TAG = "TCP";
QueueHandle_t TCP::xQueue1 = NULL;
ip_addr_t TCP::target_addr;
int TCP::ip_protocol;
int TCP::listen_sock;
sockaddr_in TCP::servaddr;
sockaddr_in TCP::cliaddr;
char TCP::addr_str[128];
char TCP::buf[128];
int TCP::sock;
TCP::Clbk_t TCP::clbk_tcp_recv = nullptr;
sockaddr_storage TCP::source_addr;
typedef struct
{
int len;
void * dataPtr;
}message;
void TCP::tcp_set_Clbk(Clbk_t clbk)
{
clbk_tcp_recv = clbk;
}
void TCP::tcp_create_server(sa_family_t serv_family, in_addr_t serv_addr, in_port_t serv_port)
{
int opt = 1;
struct sockaddr_in6 *servaddrPtr;
if (serv_family == AF_INET){
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_len = 24;
servaddr.sin_family = serv_family;
servaddr.sin_addr.s_addr = serv_addr;
servaddr.sin_port = htons(serv_port);
ip_protocol = IPPROTO_IP;
} else if (serv_family == AF_INET6){
servaddrPtr = (struct sockaddr_in6 *)&servaddr;
servaddrPtr->sin6_len = 24;
servaddrPtr->sin6_family = serv_family;
servaddrPtr->sin6_port = htons(serv_port);
ip_protocol = IPPROTO_IPV6;
}
ESP_LOGI(TAG, "Create socket...\n");
if ((listen_sock = socket(serv_family, SOCK_STREAM, ip_protocol)) < 0) {
ESP_LOGE(TAG, "socket not created\n");
}
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind errno %d", errno);
ESP_LOGE(TAG, "IPPROTO: %d", serv_family);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Socket bound, port %d", serv_port);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Soccket listening . . .");
xTaskCreate(tcp_listening_task, "tcp_listening_task", 4096, nullptr, 5, nullptr);
CLEAN_UP:
close(listen_sock);
}
void TCP::tcp_listening_task(void *arg)
{
int keepAlive = 1;
socklen_t addr_len = sizeof(source_addr);
ESP_LOGI(TAG, "Socket %d ",listen_sock);
for (;;) {
sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
ESP_LOGI(TAG, "Socket %d ",sock);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
}
if (sock > 0) {
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
if (source_addr.ss_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
}
else if (source_addr.ss_family == PF_INET6) {
inet6_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
}
ESP_LOGI(TAG, "Socket %d accepted ip address: %s",sock, addr_str);
xTaskCreate(tcp_task_recv, "tcp_task_recv", 4096, nullptr, 5, nullptr);
}
}
}
void TCP::tcp_task_recv(void *arg)
{
int n;
int number_receiving = 0;
struct timeval tv;
tv.tv_sec = 1;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&tv,sizeof(struct timeval));
ESP_LOGI(TAG,"server waiting message");
for (;;) {
n = recv(sock, (char *)buf, sizeof(buf), 0);
if (n < 0) {
ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
number_receiving++;
if (number_receiving == 10) {
number_receiving = 0;
}
}
else if (n == 0) {
ESP_LOGW(TAG, "Connection closed");
number_receiving = 0;
}
else {
if (clbk_tcp_recv != nullptr) clbk_tcp_recv(buf, n, (struct sockaddr *)&source_addr);
number_receiving = 0;
}
}
}
void TCP::tcp_sendd(void *data, uint32_t length)
{
static message msg;
xQueue1 = xQueueCreate(10, sizeof(message));
xTaskCreate(tcp_task_send, "udp_task_send", 4096, nullptr, 5, nullptr);
if(sock < 0){
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
return;
}
if(xQueue1 == NULL){
ESP_LOGE(TAG, "queue is null");
return;
}
msg.dataPtr = data;
msg.len = length;
xQueueSend(xQueue1, (void *)&msg, portMAX_DELAY);
}
void TCP::tcp_task_send(void *arg)
{
message pxRxedMessage;
for(;;)
{
xQueueReceive(xQueue1, (void *)&pxRxedMessage, portMAX_DELAY);
int err = send(sock, pxRxedMessage.dataPtr, pxRxedMessage.len, 0);
if (err < 0){
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
}
}
}
I tried to look for information on this issue, but since. I'm also new to c++ programming, nothing worked out
You should be passing the listen_sock as the arg parameter to tcp_listening_task(), rather than using a static class member, eg:
int listen_sock = socket(...);
...
xTaskCreate(tcp_listening_task, "tcp_listening_task", 4096, reinterpret_cast<void*>(listen_sock), 5, nullptr);
...
void TCP::tcp_listening_task(void *arg)
{
int listen_sock = reinterpret_cast<int>(arg);
...
close(listen_sock);
}
And especially the same with sock and tcp_task_recv() too, since your server is designed to handle multiple clients being connected simultaneously, which you can't handle with a single static sock variable:
int sock = accept(...);
...
xTaskCreate(tcp_task_recv, "tcp_task_recv", 4096, reinterpret_cast<void*>(sock), 5, nullptr);
...
void TCP::tcp_task_recv(void *arg)
{
int sock = reinterpret_cast<int>(arg);
...
close(sock);
}

c++ socket timeout error when trying to connect

I've recently into c/c++ socket programming so I just made simple program that server and client respond each other. My server is in VMware( linux fedora) and client is windows(in visual studio 2017).
And it worked(pls notice that server and client are in one laptop)..
I also tried same client code with another computer(my deskTop) and got a WSAETIMEDOUT(10060)
error. The error was captured in connect() function in client code.
I have no idea how to solve :(
Could anybody help me?
Here's a client code
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
using namespace std;
#define DEFAULT_BUFLEN 512
int main(int argc, char *argv[]) {
WSADATA wsaData;
SOCKET connectSocket;
SOCKADDR_IN servAddr;
char sendBuf[DEFAULT_BUFLEN];
char recvBuf[DEFAULT_BUFLEN];
char nickName[DEFAULT_BUFLEN];
int recvLen = 0;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "start up error";
exit(1);
}
connectSocket = socket(PF_INET, SOCK_STREAM, 0);
if (connectSocket == INVALID_SOCKET) {
cout << "socket creation error";
exit(1);
}
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(argv[1]);
servAddr.sin_port = htons(atoi(argv[2]));
if (connect(connectSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
cout << "connect error: " << WSAGetLastError();
exit(1);
}
while (1) {
recvLen = recv(connectSocket, recvBuf, sizeof(recvBuf), 0);
if (recvLen > 0) {
if (strcmp(recvBuf, "Exit") == 0)
break;
cout << recvBuf;
//cin >> nickName;
cin.getline(nickName, DEFAULT_BUFLEN);
send(connectSocket, nickName, sizeof(nickName),0);
}
else if (recvLen == 0)
cout << "Connection closed";
else if (recvLen == -1)
cout << "Connection failed";
}
closesocket(connectSocket);
WSACleanup();
return 0;
}
Blockquote
and server code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <string>
#define DEFAULT_LEN 512
#define DEFAULT_CLIENT_NUM 30
using namespace std;
char nickNameCommand[]="Please set your nickName:";
void error_handling(char * message);
class Client {
private:
char nickName[DEFAULT_LEN];
int clientSock;
public:
struct sockaddr_in clnt_addr;
int& getClientSock(){
return clientSock;
}
};
class Server {
private:
int serv_sock;
char informClientNickName[DEFAULT_LEN] = "Your NickName is ";
public:
Server(){
}
char message[DEFAULT_LEN]= "";
char clientNickName[DEFAULT_CLIENT_NUM][DEFAULT_LEN];
struct sockaddr_in serv_addr;
void setServSock(int value){
serv_sock = value;
}
int getServSock(){
return serv_sock;
}
void setInformClientNickName(int clientNum){
strcat(informClientNickName,clientNickName[clientNum]);
strcat(message , informClientNickName);
}
char* getInformClientNickName(){
return informClientNickName;
}
};
int main(int argc,char * argv[])
{
Client clientSocket[DEFAULT_CLIENT_NUM];
Server server;
int clientNum=0;
int recvLen=0;
socklen_t clnt_addr_size;
char message[DEFAULT_LEN];
if(argc!=2)
{
printf("Usage: %s <port>\n",argv[0]);
exit(1);
}
server.setServSock(socket(PF_INET,SOCK_STREAM,0));
if(server.getServSock() == -1)
error_handling("socket() error");
memset(&(server.serv_addr),0,sizeof(server.serv_addr));
server.serv_addr.sin_family=AF_INET;
server.serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server.serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(server.getServSock(),(struct sockaddr*)&
(server.serv_addr),sizeof(server.serv_addr)) ==-1)
error_handling("bind() error");
if(listen(server.getServSock(),5)==-1)
error_handling("listen() error");
clnt_addr_size=sizeof(clientSocket[0].clnt_addr);
while(1) {
clientSocket[clientNum].getClientSock() = accept(server.getServSock(),
(struct sockaddr*)&(clientSocket[clientNum].clnt_addr),&clnt_addr_size);
if(clientSocket[clientNum].getClientSock()==-1)
error_handling("accept() error");
else{
write(clientSocket[clientNum].getClientSock(),nickNameCommand,sizeof(nickNameCommand));
read(clientSocket[clientNum].getClientSock(),(void*)(server.clientNickName[clientNum]),DEFAULT_LEN);
}
cout << "Received name is : " << server.clientNickName[clientNum]<<endl;
clientNum++;
cout << "ClientNum: " << clientNum <<endl;
recvLen=0;
for(int i=0;i<clientNum;i++){
server.setInformClientNickName(i);
write(clientSocket[clientNum].getClientSock(),server.message,sizeof(server.message));
}
}
for(int i=0;i<clientNum;i++)
close(clientSocket[i].getClientSock());
close(server.getServSock());
return 0;
}
void error_handling(char * message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}

getaddrinfo, AI_PASSIVE - different behaviour windows <-> linux

I have adapted the code from http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html (selectserver.c -- a cheezy multiperson chat server) to compile on Windows. The complete code follows below. I compile using gcc version 6.1.0 (x86_64-posix-seh, Built by MinGW-W64 project). I compile using gcc6.1.0 on Linux, too.
Basically, you run it, telnet 2 or more times to port 9034, and whatever you type in one telnet session gets echoed to the other telnet sessions (depending on the system, one has to type Enter before it gets echoed - on Windows it echoes every character typed).
Now the problem :
On Linux AMD64 or ARM, I can connect to it from localhost and from another system, be that Windoes or Linux. On Windows, it only works on localhost, and I fail to understand why. The fact that hints.ai_flags = AI_PASSIVE; is specified makes it listen on all interfaces, if I understand things correctly.
The MSDN doc states:
Setting the AI_PASSIVE flag indicates the caller intends to use the returned socket address structure in a call to the bind function.
When the AI_PASSIVE flag is set and pNodeName is a NULL pointer, the IP address portion of the socket address structure is set to INADDR_ANY for IPv4 addresses and IN6ADDR_ANY_INIT for IPv6 addresses.
The code reads :
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0)
How do I make this behave correctly on Windows?
It is compiled with :
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o "src\chatserver.o" "..\src\chatserver.cpp"
and linked with
g++ -mwindows -o chatserver.exe "src\chatserver.o" -lws2_32
What do I need to change in the code please?
This is the complete code:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#ifdef _WIN32
#include <ws2tcpip.h>
#endif
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); }
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
#ifdef _WIN32
WSADATA wsaData; // Initialize Winsock
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (NO_ERROR != nResult) {
printf ("Error occurred while executing WSAStartup().");
}
#endif
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) { continue; }
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
//setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
}
else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
std::cout << "selectserver: new connection on socket " << newfd;
/*
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,get_in_addr((struct sockaddr*)&remoteaddr),remoteIP, INET6_ADDRSTRLEN),newfd);
*/
}
}
else {
// handle data from a client
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
std::cout << "selectserver: socket " << i << " hung up";
}
else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
getaddrinfo() can return multiple IP addresses. You are correctly looping through all of the returned addresses, but you are breaking the loop after the first successful bind(), and then you are calling listen() on that one single socket, regardless of its socket family. Since you are using AF_UNSPEC when calling getaddrinfo(), it is possible that it is returning BOTH INADDR_ANY for IPv4 AND IN6ADDR_ANY_INIT for IPv6.
Change your code to listen on every IP address that getaddrinfo() returns, and to keep track of those sockets so you can use all of them in your select() loop. If you just wanted to listen on either INADDR_ANY or IN6ADDR_ANY_INIT, there would be no point in using getaddrinfo() at all, as you could just hard-code the socket()/bind() calls for those two addresses and get rid of the loop altogether. The purpose of using getaddrinfo() in this manner is to let it decide what you should be listening on, given the AI_PASSIVE hint you provided. Don't make assumptions about its output.
You also cannot use fdmax on Windows, so you need to re-write your select() loop. Sockets on Windows do not use file descriptors, so you can't simply loop from 0 <= fdmax when calling FD_ISSET(), and the first parameter of select() is ignored as well. I suggest not storing your active socket descriptors/handles in a master fd_set to begin with. Use a std::list or other suitable container instead, and then dynamically create a new fd_set whenever you need to call select(). This would be more portable across different platforms.
Try something more like this:
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SOCKET int
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
inline int closesocket(int s) { return close(s); }
inline int getLastSocketError() { return errno; }
#endif
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
inline int getLastSocketError() { return WSAGetLastError(); }
#endif
#include <iostream>
#include <list>
#include <algorithm>
#include <utility>
#define PORT "9034" // port we're listening on
#ifdef _WIN32
#define SELECT_MAXFD 0
#else
#define SELECT_MAXFD fdmax+1
#endif
enum eSocketType { stListener, stClient };
struct SocketInfo
{
SOCKET sckt;
eSocketType type;
};
SocketInfo makeSocketInfo(SOCKET sckt, eSocketType type) {
SocketInfo info;
info.sckt = sckt;
info.type = type;
return info;
}
// get sockaddr, IPv4 or IPv6:
void* get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
std::list<SocketInfo> master; // socket descriptors
std::list<SocketInfo>::iterator i, j;
SOCKET sckt, newsckt; // socket descriptors
fd_set read_fds; // temp file descriptor list for select()
#ifndef _WIN32
int fdmax; // maximum file descriptor number
#endif
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char ipAddr[INET6_ADDRSTRLEN];
int yes = 1; // for setsockopt() SO_REUSEADDR, below
int rv;
struct addrinfo hints, *ai, *p;
#ifdef _WIN32
WSADATA wsaData; // Initialize Winsock
rv = WSAStartup(MAKEWORD(2,2), &wsaData);
if (NO_ERROR != rv) {
std::cerr << "WSA startup failed, error: " << rv << std::endl;
return 1;
}
#endif
// get us the listening sockets and bind them
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rv = getaddrinfo(NULL, PORT, &hints, &ai);
if (rv != 0) {
std::cerr << "selectserver: getaddrinfo failed, error: " << gai_strerror(rv) << std::endl;
return 2;
}
for(p = ai; p != NULL; p = p->ai_next) {
sckt = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (INVALID_SOCKET == sckt) {
std::cerr << "selectserver: socket failed, error: " << getLastSocketError() << std::endl;
continue;
}
// lose the pesky "address already in use" error message
setsockopt(sckt, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
//setsockopt(sckt, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int));
if (bind(sckt, p->ai_addr, p->ai_addrlen) < 0) {
std::cerr << "selectserver: bind failed, error: " << getLastSocketError() << std::endl;
closesocket(sckt);
continue;
}
// listen
if (listen(sckt, 10) < 0) {
std::cerr << "selectserver: listen failed, error: " << getLastSocketError() << std::endl;
closesocket(sckt);
continue;
}
/*
std::cout << "selectserver: listening on IP " << inet_ntop(p->ai_family, get_in_addr(p->ai_addr), ipAddr, sizeof(ipAddr)) << ", socket " << sckt << std::endl,
*/
// add the listener to the master list
master.push_back(makeSocketInfo(sckt, stListener));
}
freeaddrinfo(ai); // all done with this
// if we got here, it means we didn't get bound
if (master.empty()) {
std::cerr << "selectserver: failed to bind" << std::endl;
return 3;
}
// main loop
while (1) {
#ifndef _WIN32
fdmax = 0;
#endif
FD_ZERO(&read_fds);
for (i = master.begin(); i != master.end(); ++i) {
sckt = i->sckt;
FD_SET(sckt, &read_fds);
#ifndef _WIN32
fdmax = std::max(fdmax, sckt);
#endif
}
if (select(SELECT_MAXFD, &read_fds, NULL, NULL, NULL) < 0) {
std::cerr << "select failed, error: " << getLastSocketError() << std::endl;
return 4;
}
// run through the existing connections looking for data to read
for(i = master.begin(); i != master.end(); ) {
sckt = i->sckt;
if (!FD_ISSET(sckt, &read_fds)) {
++i;
continue;
}
// we got one!!
if (stListener == i->type) {
// handle a new connection
addrlen = sizeof(remoteaddr);
newsckt = accept(sckt, (struct sockaddr *)&remoteaddr, &addrlen);
if (INVALID_SOCKET == newsckt) {
std::cerr << "accept failed on socket " << sckt << ", error: " << getLastSocketError() << std::endl;
}
else {
master.push_back(makeSocketInfo(newsckt, stClient)); // add to master list
std::cout << "selectserver: new connection, socket " << newsckt << std::endl;
/*
std::cout << "selectserver: new connection from " << inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), ipAddr, sizeof(ipAddr)) << ", socket " << newsckt << std::endl,
*/
}
}
else {
// handle data from a client
nbytes = recv(sckt, buf, sizeof(buf), 0);
if (nbytes <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
std::cout << "selectserver: socket " << sckt << " disconnected" << std::endl;
}
else {
std::cerr << "selectserver: recv failed on socket " << sckt << ", error: " << getLastSocketError() << std::endl;
}
closesocket(sckt); // bye!
i = master.erase(i); // remove from master list
continue;
}
// send to everyone!
// except a listener and ourselves
for(j = master.begin(); j != master.end(); ) {
if ((j->sckt != sckt) && (stClient == j->type)) {
if (send(j->sckt, buf, nbytes, 0) < 0) {
std::cerr << "selectserver: send failed on socket " << j->sckt << ", error: " << getLastSocketError() << std::endl;
closesocket(j->sckt); // bye!
j = master.erase(j); // remove from master list
continue;
}
}
++j;
}
}
++i;
}
}
for(i = master.begin(); i != master.end(); ++i) {
closesocket(i->sckt);
}
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
If you are running the code on a system that supports dual-stack sockets (like Windows), you can change AF_UNSPEC to AF_INET6 (or just hard-code socket()/bind() without using getaddrinfo()) to create only IPv6 listener(s) on IN6ADDR_ANY_INIT, and then disable the IPV6_V6ONLY socket option on them. This will allow IPv6 listen sockets to accept both IPv4 and IPv6 clients, reducing the number of listen sockets you need to create.

Sockets: Why is accept() hanging but connect() is succeeding?

I have been trying to understand why on my server the accept() call is still blocking when the client has a successful connect() call?
server.cpp
#include <errno.h>
#include <strings.h>
#include <iostream>
#include "globals.h"
using namespace std;
/* Declaring errno */
extern int errno;
/* Function for printing error */
void report_error(char *s)
{
printf("receiver: error in%s, errno = %d\n", s, errno);
exit(1);
}
int main(int argc, char *argv[])
{
int s,c;
int res;
struct sockaddr_in socket_address = {0}, client_sa = {0};
unsigned int client_sa_len = sizeof(client_sa);
/* Creating the socket and returns error if unsuccesfull */
if((s= socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)) == -1)
report_error("socket");
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr=INADDR_ANY;
socket_address.sin_port = htons(5318 + 2000);
/* Binding the socket and returns error if unsuccesfull */
if(bind(s, (struct sockaddr *)&socket_address, sizeof(socket_address))== -1)
report_error("bind");
listen(s, 10);
cout << "listening on port\n";
while(1)
{
/*The server just hangs here*/
c = accept(s, (struct sockaddr*)&client_sa, &client_sa_len);
if (c > 0)
{
cout << "LOG: Was the accept successful" << endl;
res = fork();
if (res < 0)
{
perror("Forking of child failed");
}
}
if(res == 0)
{
//close(s);
char msg[MSGL], reply[50], args[MSGL];
char command[MSGL];
cout << "LOG: Get message?" << endl;
GetRequest(msg, c, &client_sa);
if( (msg[0] == 'c') && (msg[1] == 'd') && (msg[2] == ' '))
{
strncpy(command, "cd", sizeof(command));
int arg_i = 0;
for(int i = 3; msg[i] != '\n'; ++i)
{
args[arg_i] = msg[i];
++arg_i;
}
}
else
{
for(int i = 0; msg[i] != '\n'; ++i)
{
command[i] = msg[i];
}
}
else
{
if(c > 0)
{
//close(c);
}
}
}
return 0;
}
When I run this server it prints that it is listening, then when I initialize the client it does not say that the client has connected
client.cpp
#include <errno.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include "globals.h"
using namespace std;
/* Declaring errno */
extern int errno;
int main(int argc, char *argv[])
{
int s;
char* server_address = argv[1];
char command[MSGL];
char reply[MSGL];
int connect_success;
struct sockaddr_in sa = {0} ,cli_sa = {0};
int length = sizeof(sa);
struct hostent *hp;
cli_sa.sin_family = AF_INET;
cli_sa.sin_addr.s_addr=INADDR_ANY;
cli_sa.sin_port = htons(5318 + 2001);
/* FILL SOCKET ADDRESS*/
if((hp = gethostbyname(server_address))==NULL)
report_error("gethostbyname");
bcopy((char*)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
sa.sin_family = hp->h_addrtype;
//memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
sa.sin_port = htons(5318 + 2000); /* define port number based on student ID*/
/* Creating the socket and returns error if unsuccessfull */
if((s=socket(AF_INET, SOCK_DGRAM, PF_UNSPEC))== -1)
report_error("socket");
/* Binding the socket and returns error if unsuccesfull */
if(bind(s, (struct sockaddr *)&cli_sa, sizeof(cli_sa))== -1)
report_error("bind");
connect_success = connect(s,(struct sockaddr*) &sa, length);
cout << connect_success << endl;
if (connect_success < 0)
{
report_error("connect");
cout << "LOG: is there an error?" << endl;
}
cout << "LOG: is the connection made?" << endl;
while(1)
{
cout << "myRPC>>";
fgets(command,MSGL,stdin);
if (DoOperation(command,reply,s,sa) == SEND_FAILURE)
{
cout << "Error: sending command\n";
}
}
return 0;
}
I'm fairly certain that your server process has undefined behavior.
accept() and connect() is for TCP sockets. You are creating UDP sockets. For UDP sockets, all that connect() does is set the default address for send(), and it always succeeds immediately. This is explained in the manual page for connect(2), which you should definitely read:
If the socket sockfd is of type SOCK_DGRAM, then addr is the address
to which datagrams are sent by default, and the only address from
which datagrams are received.
I expect accept() to fail for UDP (SOCK_DGRAM) sockets, most likely with EINVAL. If you review the logic in your server code, when accept() fails, res never gets initialized before its value is tested in the if() statement.
Undefined behavior.

OpenSSL socket: Select always returns 0

I made a small socket echo server with a blocking socket (see code below), but the select statement always returns 0 even when there is a message to be read. Everything else works. If you replace the select statement by simple assigning 1 to selectResult, the server works.
The server runs on Ubuntu in a VM, while the client is on the Host system (Windows 7 professional). My IDE for the server is Eclipse 3.8 and it uses OpenSSL 1.0.1j.
To get this code to work, you only need to include OpenSSL's root directory, add its library path to the linker and link to ssl, crypto and dl (in that order). Also you need a certificate and private key.
Thanks in advance!
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;
#define MAX_BUFFER 1024
int main()
{
// Initializing...
SSL_CTX*_ctx = NULL;
SSL* _ssl = NULL;
fd_set _fdSet;
int _serverSocket = 0;
int _port = 9090;
timeval t;
const char* certPath = "/home/alex/Certificate/cacert.pem";
const char* pKeyPath = "/home/alex/Certificate/privkey.pem";
// Init OpenSSL
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
_ctx = SSL_CTX_new(TLSv1_1_server_method());
if (_ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
// Set certificate and private key.
if (SSL_CTX_use_certificate_file(_ctx, certPath, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(_ctx, pKeyPath, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
if (!SSL_CTX_check_private_key(_ctx))
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
// Initialize server socket:
// 1. set address
struct sockaddr_in addr;
int optval = 1;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(_port);
addr.sin_addr.s_addr = INADDR_ANY;
// 2. init socket, set socket options, bind it to address
_serverSocket = socket(PF_INET, SOCK_STREAM, 0);
setsockopt(_serverSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if (bind(_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) != 0)
{
perror("can't bind port");
abort();
}
// 3. Prepare the socket to accept connections
if (listen(_serverSocket, 1) != 0)
{
perror("Can't configure listening port");
abort();
}
cout << "Server finished initializing." << endl;
bool bServerStayAlive = true;
while (bServerStayAlive)
{
cout << "Waiting for connection..." << endl;
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
int client = accept(_serverSocket, (struct sockaddr*) &addr, &len);
printf("Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
_ssl = SSL_new(_ctx);
SSL_set_fd(_ssl, client);
if (SSL_accept(_ssl) == -1) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
while (bServerStayAlive)
{
FD_ZERO(&_fdSet);
FD_SET(_serverSocket, &_fdSet);
t.tv_sec = 1;
t.tv_usec = 0;
int selectResult = select(_serverSocket + 1, &_fdSet, NULL, NULL, &t);
if (selectResult == 0)
{
cout << "timeout" << endl;
continue;
}
if (selectResult < 0)
{
cout << "Select error: " << selectResult << endl;
bServerStayAlive = false;
break;
}
cout << "Going to read something\n";
unsigned char buffer[MAX_BUFFER];
memset(buffer, 0, MAX_BUFFER);
int bytes = SSL_read(_ssl, buffer, MAX_BUFFER); /* get request */
if (bytes > 0)
{
cout << "Received message: " << endl;
for (int i = 0; i < bytes; i++)
cout << buffer[i];
cout << endl;
SSL_write(_ssl, buffer, bytes);
}
else
{
ERR_print_errors_fp(stderr);
break;
}
}
}
int sd = SSL_get_fd(_ssl); /* get socket connection */
SSL_free(_ssl); /* release SSL state */
close(sd); /* close connection */
cout << "Connection was closed.\n";
}
// Uninitializing
close(_serverSocket);
SSL_CTX_free(_ctx);
return 0;
}
I think you meant to select on the client socket that you just accepted, not the _serverSocket that you're accepting connections on.