So I'm writing a very basic TCP server which just echoes back the messages sent from the client back to the client. I have a setup where the server is running in a while loop and waiting for either a new client to connect or for a message from one of the existing clients using the select() method.
My question is:
How do i, from the server side, close the master socket and basically shutting down the server. Im not asking for exact code but more for standard practice.
For some context:
In the long run I am imagining multiple clients connected to my server and I need to shutdown the server gracefully from the server side.
Even more context: The server code.
#define TRUE 1
#define FALSE 0
#define PORT 55555
int main(int argc, char* argv[])
{
int opt = TRUE;
int masterSocket, addrlen, newSocket, maxClients = 10, clientSockets[maxClients],
activity, valread, sd, maxSd;
struct sockaddr_in address;
char buffer[1025];
fd_set readfds;
const char *message = "ECHO DAMON v1.0 \r\n";
/* Initialize array with 0 so it's not read */
for(int i = 0; i < maxClients ; i++)
{
clientSockets[i] = 0;
}
/* Create master socket */
if((masterSocket = socket(AF_INET, SOCK_STREAM,0)) == 0)
{
perror("Error when trying to create master socket.\n");
exit(EXIT_FAILURE);
}
/* Set master socket to allow multiple connections */
if( setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
perror("Could not set sockopt");
exit(EXIT_FAILURE);
}
/* Socket type */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if( bind(masterSocket, (struct sockaddr*)&address, sizeof(address)) < 0)
{
perror("Error, could not bind master socket. \n");
exit(EXIT_FAILURE);
}
printf("Listening on %d. \n", PORT);
if( listen(masterSocket, 3) < 0)
{
perror("Error, could not listen.\n");
exit(EXIT_FAILURE);
}
addrlen = sizeof(address);
puts("Waiting for connections...\n"); //just a printf variant
/* END INIT */
while(TRUE)
{
/* Clear socket set */
FD_ZERO(&readfds);
/* Add master socket to set */
FD_SET(masterSocket, &readfds);
/* Add child sockets to set, will be 0 first iteration */
for(int i = 0; i < maxClients ; i++)
{
sd = clientSockets[i]; // sd = socket descriptor
/* If valid socket descriptor */
if(sd > 0)
{
FD_SET(sd, &readfds);
}
/* Get highest fd number, needed for the select function (later) */
if(sd > maxSd)
{
maxSd = sd;
}
}//end for-loop
/* Wait for activity on any socket */
activity = select(maxSd +1, &readfds, NULL, NULL, NULL);
if((activity < 0) && (errno != EINTR))
{
printf("Error on select.\n"); //no need for exit.
}
/* If the bit for the file descriptor fd is set in the
file descriptor set pointed to by fdset */
/* If something happend in the master socket, its a new connection */
if(FD_ISSET(masterSocket, &readfds))
{
if((newSocket = accept(masterSocket, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0)
{
perror("Could not accept new socket.\n");
exit(EXIT_FAILURE);
}
/* Print info about connector */
printf("New connection, socket fd is %d, ip is: %s, port: %d\n", newSocket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
if( send(newSocket, message, strlen(message), 0) != strlen(message))
{
perror("Could not sent welcome message to new socket.\n");
}
puts("Welcome message sen successfully.\n");
/* Add new socket to array of clients */
for(int i = 0; i < maxClients; i++)
{
if(clientSockets[i] == 0)
{
clientSockets[i] = newSocket;
printf("Adding socket to list of client at index %d\n", i);
break;
}
}
}//end masterSocket if
/* Else something happend at client side */
for(int i = 0; i < maxClients; i++)
{
sd = clientSockets[i];
if(FD_ISSET(sd, &readfds))
{ /* Read socket, if it was closing, else read value */
if((valread = read( sd, buffer, 1024)) == 0)
{
getpeername( sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
printf("Host disconnected, ip %s, port %d.\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
close(sd);
clientSockets[i] = 0;
}
}
else
{
buffer[valread] = '\0';
send(sd, buffer, strlen(buffer), 0);
}
}
}
return 0;
}
If you are planning to exit your application gracefully by adding a breakstatement in your app by something similar to this:
if (exit_condition)
{
break;
}
You can place this loop at the end of your main function:
/* close connections gracefully */
closesocket(masterSocket);
masterSocket = 0; /* optional, see comment below */
for(int i = 0; i < maxClients; i++)
{
if (clientSockets[i] != 0)
{
shutdown(clientSockets[i]);
closesocket(clientSockets[i]);
clientSockets[i] = 0; /* optional, except if you also have a SIGINT handler */
}
}
If you want to do the same to handle an exit with Ctrl-C, you will find details on how to setup a SIGINT handler to handle Ctr-C here:
Catch Ctrl-C in C. Place the above loop in your handler, then. Your sockets-related variables will have to be declared at global scope, since your sockets are only visible from main() in your current code.
Related
i was working on a socket server on a friend's computer and everythng was working fine but then i executed the code on my computer and it throws a buffer overflow like this
*** buffer overflow detected ***: /home/erick/CLionProjects/AirWar++/cmake-build-debug/AirWar__ terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f906c5047e5]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7f906c5a556c]
/lib/x86_64-linux-gnu/libc.so.6(+0x116570)[0x7f906c5a3570]
/lib/x86_64-linux-gnu/libc.so.6(+0x1169da)[0x7f906c5a39da]
We are using rapidjson library to send the data, i dont know why this happens if the exact same code runs perfectly on his computer, the only difference is that i am running Ubuntu on a VM and he was running Ubuntu as the main OS.
I would really appreciate your help, the code fails on this if:
if ((valread = read( sd , buffer, 1024)) == 0)
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[3] ,
max_clients = 3 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
void server::init() {
using namespace rapidjson;
char buffer[400];
//set of socket descriptors
fd_set readfds;
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
//set master socket to allow multiple connections ,
//this is just a good habit, it will work without this
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.100.19");//Write your IP
address.sin_port = htons( PORT );
//bind the socket to localhost port 8080
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Escuchando en el puerto %d \n", PORT);
//try to specify maximum of 3 pending connections for the master socket
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//accept the incoming connection
addrlen = sizeof(address);
puts("Esperando conexiones ...");
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL ,
//so wait indefinitely
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
//If something happened on the master socket ,
//then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket,
(struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number
printf("Nueva conexion , socket fd %d , ip : %s , puerto : %d\n" , new_socket , inet_ntoa(address.sin_addr) , ntohs
(address.sin_port));
//send new connection message
//if( send(new_socket, "Conectado", strlen("Conectado"), 0) != strlen("Conectado") )
//{
// perror("send");
//}
//puts("Mensaje enviado con exito");//succesfully send
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("AƱadido a la lista de sockets como %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and read the message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , \
(socklen_t*)&addrlen);
printf("Cliente desconectado , ip %s , port %d \n" ,
inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
run=FALSE;
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//se eliminan dos caracteres nulos del buffer;
std::string m=buffer;
std::string s(m.substr(0,valread));
const char * json= s.c_str();
std::cout<<json;
Document d;
d.Parse(json);
Value& type=d["type"];
if(strcmp(type.GetString(),"shoot")==0){
stat= 5;//Dispara
}else if(strcmp(type.GetString(),"izq")==0){
//Mov. izquierda
stat= 4;
}else if(strcmp(type.GetString(),"der")==0){
//Mov. derecha
stat=6;
}else if(strcmp(type.GetString(),"arr")==0){
//Mov. arriba
stat=8;
}else if(strcmp(type.GetString(),"aba")==0){
//Mov. abajo
stat= 2;
}else{
stat=0;//Quieta
}
}
}
}
}
}
long server:: Send(const char * msg){
send(client_socket[0],msg,strlen(msg),0);
}
Btw we both use CLion.
Will be waiting for your help, thanks!
char buffer[400];
// ^^^
...
if ((valread = read( sd , buffer, 1024)) == 0)
// ^^^^
I hate to sound like a jerk, but didn't the error message prompt you to check the size of buffers that you were reading data into?
Also note that with TCP reads you might not get the entire message in a read() call so buffer might not be null terminated which can cause problems here:
std::string m=buffer;
And this line makes me think that the null terminator might not even be part of the message (we'd have to see the client code to be sure):
send(client_socket[0],msg,strlen(msg),0);
Buffer overflow is exactly what it is.
if ((valread = read( sd , buffer, 1024)) == 0)
Here you are lying to the operating system about the size of buffer, which is 400, not 1024. This line of code should be
if ((valread = read( sd , buffer, sizeof buffer)) == 0)
and you also need to check it for -1.
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've wrote a simple client code in python, and I'm trying to connect to a simple echo server written in C.
I know it shouldn't matter, but for some reason I did manage to connect to a server written in python, but I cannot connect to the C server.
Here's the code of the client:
import socket
import sys
import time
HOST = 'localhost'
PORT = 11000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
msg = raw_input()
s.send(msg)
data = s.recv(len(msg))
s.close()
print 'Received: ', data
And here's the C code of the echo server:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>
#ifndef AF_INET
#define AF_INET 2
#endif
#ifndef SOCK_DGRAM
#define SOCK_DGRAM 2
#endif
#ifndef INADDR_ANY
#define INADDR_ANY 0
#endif
#ifndef IP_DONTFRAG
#define IP_DONTFRAG 67
#endif
#define BUFFER_SIZE 1024
#define ECHO_PORT_UDP 10000
#define ECHO_PORT_TCP 11000
int main(int argc, char *argv[]) {
int echo_socket = 0;
int echo_socket_child = 0; // for TCP
struct sockaddr_in server;
struct sockaddr_in client;
struct hostent *hostp; // client host info
struct sockaddr_in clientaddr; // client addr
char *hostaddrp; // dotted decimal host addr string
char buffer[BUFFER_SIZE];
unsigned int clientlen = 0;
unsigned int serverlen = 0;
int received = 0;
int port = 0;
char *endptr;
int optval = 1;
int msg_byte_size = 0;
// Parameters check
if (argc == 2) {
port = strtol(argv[1], &endptr, 0);
if ((*endptr) || ((port != ECHO_PORT_UDP) && (port != ECHO_PORT_TCP))) {
printf("EchoServer: Invalid port number.\n Use port %d for UDP, port %d for TCP.\n", ECHO_PORT_UDP, ECHO_PORT_TCP);
return -1;
}
else {
if (port == ECHO_PORT_UDP) {
printf("EchoServer: Running UDP on port %d.\n", port);
}
if (port == ECHO_PORT_TCP) {
printf("EchoServer: Running TCP on port %d.\n", port);
}
}
}
else {
printf("EchoServer: Invalid arguments.\n");
return -1;
}
// Opening UDP socket
if (port == ECHO_PORT_UDP) {
if ((echo_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("EchoServer: Failed opening socket");
return -1;
}
}
if (port == ECHO_PORT_TCP) {
if ((echo_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("EchoServer: Failed opening socket");
return -1;
}
// setsockopt: Handy debugging trick that lets us rerun the server immediately after we kill it; otherwise we have to wait about 20 secs.
// Eliminates "ERROR on binding: Address already in use" error.
setsockopt(echo_socket, SOL_SOCKET, SO_REUSEADDR,(const void *)&optval , sizeof(int));
}
// Construct the server sockaddr_in structure
memset(&server, 0, sizeof(server)); /* Clear struct */
server.sin_family = AF_INET; /* Internet/IP */
server.sin_addr.s_addr = htonl(INADDR_ANY); /* Any IP address */
server.sin_port = htons(atol(argv[1])); /* server port */
// Bind the socket
serverlen = sizeof(server);
if (bind(echo_socket, (struct sockaddr *) &server, serverlen) < 0) {
printf("EchoServer: Failed binding socket");
return -1;
}
// Wait for a datagram until cancelled
if (port == ECHO_PORT_UDP) {
while (1) {
/* Receive a message from the client */
clientlen = sizeof(client);
if ((received = recvfrom(echo_socket, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client, &clientlen)) < 0) {
printf("EchoServer: Failed receiving datagram");
return -1;
}
printf("Client datagram received from: %s\n", inet_ntoa(client.sin_addr));
/* Send the message back to client */
if (sendto(echo_socket, buffer, received, 0, (struct sockaddr *) &client, sizeof(client)) != received) {
printf("Mismatch in number of echoed bytes");
return -1;
}
}
}
// Wait for a connection until cancelled
if (port == ECHO_PORT_TCP) {
while (1) {
echo_socket_child = accept(echo_socket, (struct sockaddr *) &client, &clientlen);
if (echo_socket_child < 0) {
printf("ERROR on accept");
break;
}
// gethostbyaddr: determine who sent the message
hostp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr, sizeof(clientaddr.sin_addr.s_addr), AF_INET);
if (hostp == NULL) {
printf("ERROR on gethostbyaddr");
break;
}
hostaddrp = inet_ntoa(clientaddr.sin_addr);
if (hostaddrp == NULL) {
printf("ERROR on inet_ntoa\n");
break;
}
printf("server established connection with %s \n", hostaddrp);
// read: read input string from the client
bzero(buffer, BUFFER_SIZE);
msg_byte_size = read(echo_socket_child, buffer, BUFFER_SIZE);
if (msg_byte_size < 0) {
printf("ERROR reading from socket");
break;
}
printf("server received %d bytes: %s", msg_byte_size, buffer);
// write: echo the input string back to the client
msg_byte_size = write(echo_socket_child, buffer, strlen(buffer));
if (msg_byte_size < 0) {
printf("ERROR writing to socket");
break;
}
} // endof while(1)
close(echo_socket_child);
return -1;
}
return 0;
}
Any ideas why I fail to connect to the server?
edit:
this is the error I receive:
Traceback (most recent call last):
File "s.py", line 8, in <module>
s.connect((HOST, PORT))
File "C:\Python27\lib\socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 10061]
(1) Add a listen call to the TCP section of the code.
(2) You have to tell accept what the length of the sockaddr you are passing it is and it will in return tell you the length of the address of the client it returned. You were passing it as 0 length so naturally it didn't pass back a client address which subsequently makes your gethostbyaddr fail with unknown address.
(3) If you don't close the client socket within the loop it just remains open (and leaks a file descriptor) for the duration of the server's life. Eventually you will run out of FDs. It doesn't effect your client which just closes after the receipt of one msg but any client who writes more than one message will never have it received by the server and will never receive an eof from the server.
if (port == ECHO_PORT_TCP)
{
if (listen(echo_socket, ECHO_PORT_TCP) == -1)
{
perror("listen");
exit(1);
}
while (1)
{
clientlen = sizeof(client);
echo_socket_child = accept(echo_socket, (struct sockaddr *) &client, &clientlen);
if (echo_socket_child < 0)
{
perror("accept");
break;
}
// gethostbyaddr: determine who sent the message
hostp = gethostbyaddr((const char *) &client.sin_addr.s_addr, sizeof(client.sin_addr.s_addr), AF_INET);
if (hostp == NULL)
{ herror("byaddr");
break;
}
hostaddrp = inet_ntoa(client.sin_addr);
if (hostaddrp == NULL)
{
printf("ERROR on inet_ntoa\n");
break;
}
printf("server established connection with %s (%s)\n", hostp->h_name, hostaddrp);
bzero(buffer, BUFFER_SIZE);
msg_byte_size = read(echo_socket_child, buffer, BUFFER_SIZE);
if (msg_byte_size < 0)
{
printf("ERROR reading from socket");
break;
}
printf("server received %d bytes: %s", msg_byte_size, buffer);
msg_byte_size = write(echo_socket_child, buffer, strlen(buffer));
if (msg_byte_size < 0)
{
printf("ERROR writing to socket");
break;
}
close(echo_socket_child);
} // endof while(1)
return -1;
}
First off I am still very new to c++. I am trying to write a GNSS internet radio caster program. Basically when a client logs in and is accepted my program stores what data stream source the client requests in a Client class. I was going to match an array of Client objects and an array of clientSockets using the index number of both arrays however trying to use Select() to check nonblocking sockets has made that complicated. I have found some examples of implementing Select() but I'm not certain how to track the original clientSocket from my array that I use to accept the connection from the listenSocket with.
class Client{
public:
std::string source;
std::string user; //stored in txt file as name:password
boolean connected;
int IndexNumber;
};
typedef struct _MYSOCKET_INFORMATION{
char Buffer[BUFFERSIZE];
WSABUF DataBuf;
SOCKET Socket;
DWORD SendBytes;
DWORD RecvBytes;
}SOCKET_INFORMATION, *LPSOCKET_INFORMATION;
// creates number of objects of this structure
LPSOCKET_INFORMATION SocketList[NUMBER_CLIENTS];
FD_SET Writer;
FD_SET Reader;
DWORD TotalSockets = 0;
DWORD Flags;
u_long NonBlock = 1;
timeval tv;
long tv_sec = 1;
long tv_usec = 0;
//intiate objects
Caster caster;
DataStream datastream[NUMBER_BASES]; //100 base stations max
Client client[NUMBER_CLIENTS];
SOCKET listenSocket;
SOCKET clientSocket[NUMBER_CLIENTS];
SOCKET serverSocket;
char recvArray[BUFFERSIZE];
using namespace std;
int main()
{
//initialize the Read and Write socket sets
FD_ZERO(&Reader);
FD_ZERO(&Writer);
//check for connection attempt
FD_SET(listenSocket, &Reader);
FD_SET(serverSocket, &Reader);
//set read and write state based on state of buffer
for( unsigned int i=0; i<TotalSockets; i++){
if( SocketList[i]->RecvBytes < SocketList[i]->SendBytes){
FD_SET(SocketList[i]->Socket,&Writer);
}
else{
FD_SET(SocketList[i]->Socket,&Reader);
}
}
//Select returns number of sockets ready to be read/writen
Total = select(0, &Reader, &Writer, NULL, &tv);
if(Total == SOCKET_ERROR){
printf("Error select function %d\n", WSAGetLastError());
return 1;
}
if( FD_ISSET(listenSocket, &Reader)){
Total--;
//set current client no
while(client[current].connected == true){
current++;
if(current >= NUMBER_CLIENTS){
current = 0;
count++;
if(count >= 2){//has cylcled twice all must be set to true, can add no more.
count =0;
printf("client limit reached.\n");
break;
}
}
}
clientSocket[current] = accept(listenSocket, NULL, NULL); //setup client connection for further use
if( clientSocket[current] != INVALID_SOCKET){
//set new client to non blocking
check = ioctlsocket(clientSocket[current], FIONBIO, &NonBlock);
if( check == SOCKET_ERROR){
printf("Error setting client to nonblocking mode. %d.\n", WSAGetLastError());
closesocket(clientSocket[current]);
continue;
}
else{
printf("Accepted client connection request \n");
//recieve data
check = recv(clientSocket[current], recvArray, BUFFERSIZE, 0);
if( check == SOCKET_ERROR){ //close connection if error
printf("error recieving login data. %d \n", WSAGetLastError());
closesocket(clientSocket[current]);
continue;
}
if( check == 0){//no data is recieved then client disconnected
printf("ERROR no data, client dsiconnected.\n");
closesocket(clientSocket[current]);
continue;
}
else{
for(int n=0; n<check +1 ; n++){ //must be request message for data per NTRIP 1.0
clientRequest << recvArray[n]; //place read char bytes into stringstream object for parsing.
}
//if request is good returns a client requested source, username, and true false for data send.
checkClientRequest(clientSocket[current], client[current].source, client[current].connected, client[current].user);
}
}
}
}//end of check client listen socket
for(unsigned int i=0; Total>0 && i<TotalSockets; i++){
LPSOCKET_INFORMATION SocketInfo = SocketList[i];
//check if client sends GGA string
if(FD_ISSET(SocketInfo->Socket, &Reader)){
Total--;
SocketInfo->DataBuf.buf = SocketInfo->Buffer;
SocketInfo->DataBuf.len = BUFFERSIZE;
Flags = 0;
SocketInfo->RecvBytes = sizeof(SocketInfo->DataBuf);
check = WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &(SocketInfo->RecvBytes), &Flags, NULL, NULL);
if( check == SOCKET_ERROR){
FreeSocketInformation(i); //shuts down client socket if socket error
continue;
}
else{
memcpy(recvArray, SocketInfo->DataBuf.buf, SocketInfo->RecvBytes +1);
//print data on screen change to sort lines
printf("%s \n", SocketInfo->DataBuf.buf);
}
}
There's more to it than just this but how do I track the structure SocketInfo with my clientSocket[]'s?