Dipset
Dipset

Reputation: 15

Deny a connection to a server C socket programming

I have a little problem : my server understand that he can't accept a new connection but the client still thinks he's connected. I don't know how to fix this.

My client code :

#include<stdio.h> //printf
#include<string.h>    //strlen
#include<sys/socket.h>    //socket
#include<arpa/inet.h> //inet_addr
#include <fcntl.h> // for open
#include <unistd.h> // for close

int main(int argc , char *argv[]){
    int sock;
    struct sockaddr_in server;
    char message[1000] , server_reply[2000];

    //Create socket
    sock = socket(AF_INET , SOCK_STREAM , 0);
    if (sock == -1){
        printf("Could not create socket");
    }
    puts("Socket created");

    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_family = AF_INET;
    server.sin_port = htons(8080);

    //Connect to remote server
    if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){
        perror("connect failed. Error");
        return 1;
    }

    puts("Connected\n");

    //keep communicating with server
    while(1){
        printf("Enter message : ");
        scanf("%s" , message);

        //Send some data
        if(send(sock , message , strlen(message) , 0) < 0){
            puts("Send failed");
            return 1;
        }

        //Receive a reply from the server
        if(recv(sock , server_reply , 2000 , 0) < 0){
            puts("recv failed");
            break;
        }

        puts("Server reply :");
        puts(server_reply);
    }

    close(sock);
    return 0;
}

And my server code :

#include<stdio.h>
#include<string.h>    //strlen
#include<stdlib.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h>    //write
#include<pthread.h> //for threading , link with lpthread

#define MAX_PLAYER 4

void *connection_handler(void *);

int nbrePlayer = 0;

int main(int argc , char *argv[]){
  int socket_desc , client_sock , c , *new_sock;

  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(8080);

  //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
  if(listen(socket_desc , 4)<0){
    perror("listen");
    return 1;
  }

  //Accept and incoming connection
  puts("Waiting for incoming connections...");
  c = sizeof(struct sockaddr_in);

  while((client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c))>0){
    if(nbrePlayer>=MAX_PLAYER){
      puts("connection refused");
      close(client_sock);
      continue;
    } else {
      puts("Connection accepted");
    }
    nbrePlayer++;
    pthread_t sniffer_thread;
    new_sock = malloc(1);
    *new_sock = client_sock;

    if( pthread_create( &sniffer_thread , NULL ,  connection_handler , (void*) new_sock) < 0){
      perror("could not create thread");
      return 1;
    }

    //Now join the thread , so that we dont terminate before the thread
    //pthread_join( sniffer_thread , NULL);
    puts("Handler assigned");
    printf("%d\n",nbrePlayer);
  }

  if (client_sock < 0){
    perror("accept failed");
    return 1;
  }

  return 0;
}

/*
* This will handle connection for each client
* */
void *connection_handler(void *socket_desc){
  //Get the socket descriptor
  int sock = *(int*)socket_desc;
  int read_size;
  char *message , client_message[2000];

  //Receive a message from client
  while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 ){
    //Send the message back to client
    write(sock , client_message , strlen(client_message));
  }

  if(read_size == 0){
    puts("Client disconnected");
    fflush(stdout);
  }
  else if(read_size == -1){
    perror("recv failed");
  }

  //Free the socket pointer
  free(socket_desc);
  `nbrePlayer`--;
  //Delete the player for the shared memory
  return 0;
}

Thx forward for your answer

Upvotes: 0

Views: 1754

Answers (3)

Luis Colorado
Luis Colorado

Reputation: 12668

Just drop the listen value to 0. listen(sock_fd, 0); will disallow any incoming connection while the server is not in the accept(2) syscall. The client will receive a ECONNREF ("Connection refused") from the connect(2) syscall.

NOTE

Just tried and it doesn't work... The linux kernel silently ignores the 0 value (or means use default, just have to check)

The other possibility is to close the accept socket descriptor, in which case you can lose the access to the same port in the next connection, but it is not being in use, so how can the kernel know you want to reserve. Attached is a simple echo server that implements the closing of the accept descriptor, and it works! (I have included code to get the kernel assigned port to the connection, in case you don't do bind(2) syscall, so it gets the same port next time)

/* $Id: srv.c,v 1.6 2012/01/21 18:14:31 luis Exp $
 * Author: Luis Colorado <[email protected]>
 * Date: Thu Feb 26 12:44:15 MET 1998
 */

#define PROGNAME    "srv"

#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>

extern char *optarg;
extern int optind, opterr, optopt;

int bind_port = 0;
int listen_val = 0;

int main (int argc, char **argv)
{
    int opt, sd, res;

    /* process the program options... */
    while ((opt = getopt(argc, argv, "b:l:")) != EOF) {
            switch (opt) {
        case 'b': bind_port = atol(optarg); break;
        case 'l': listen_val = atol(optarg); break;
            } /* switch */
    } /* while */

    /* Construct the sockaddr_in for the connect system call */
    for(;;) {
        sd = socket (AF_INET, SOCK_STREAM, 0);
        if (sd < 0) {
            perror (PROGNAME ": socket");
        } /* if */
        if (bind_port) {
            struct sockaddr_in srv_name;
            srv_name.sin_family = AF_INET;
            srv_name.sin_port = htons(bind_port);
            srv_name.sin_addr.s_addr = INADDR_ANY;
            int res = bind(sd,
                    (const struct sockaddr *)&srv_name,
                    sizeof srv_name);
            if (res < 0) {
                perror(PROGNAME ": bind");
                exit(1);
            }
            printf(PROGNAME ": bound to port %d\n", bind_port);
        }
        if (listen_val >= 0) {
            int res = listen(sd, listen_val);
            if (res < 0) {
                perror(PROGNAME ": listen");
                exit(1);
            }
            printf(PROGNAME ": listen set to %d\n", listen_val);
        }
        {
            struct sockaddr_in server;
            int server_sz = sizeof server;
            int res = getsockname(sd,
                    (struct sockaddr *) &server,
                    &server_sz);
            bind_port = ntohs(server.sin_port);
            printf("bound to port %d\n", bind_port);
        }

        struct sockaddr_in client;
        int client_sz = sizeof client;
        int cd = accept(sd,
                (struct sockaddr *)&client,
                &client_sz);
        if (cd == -1) {
            perror (PROGNAME ": accept");
            break;
        } /* if */

        /*************************************************
         * THIS IS THE TRICK... JUST CLOSE THE ACCEPTING
         * SOCKET AND THE SERVER WILL REFUSE INCOMING
         * CONNECTIONS FROM THEN ON.
         *************************************************/
        close(sd);

        printf("Accepted connection from %s:%d\n",
                inet_ntoa(client.sin_addr),
                ntohs(client.sin_port));
        char *greeting = PROGNAME": This is the echo "
                "server ready for input\r\n";
        write(cd, greeting, strlen(greeting));

        char buffer[1024];
        ssize_t n;

        while ((n = read(cd, buffer, sizeof buffer)) > 0) {
            printf("Recibido: [%.*s]\n", n, buffer);
            write(cd, buffer, n);
        } /* while */
        if(n < 0) {
            perror(PROGNAME ": read");
        } else { /* == 0 */
            printf("Recibido: EOF\n");
            char *greeting = "\r\n" PROGNAME ": Have a nice day :)\r\n";
            write(cd, greeting, strlen(greeting));
        }
        close(cd);
    } /* for (;;) */

} /* main */

Upvotes: 2

Pras
Pras

Reputation: 4044

In your application, server is first accepting connection, then its closing it, when it meets certain condition,client does socket send on connecting to server, a socket send does not always detect closed/broken connection but a recv always detects it. So you should think about designing your application so that the client first does recv(expecting a welcome message) after connection so that it detects the closed socket from server.

Another way could be if condition nbrePlayer==MAX_PLAYER-1 meets, you should not call accept, so that connect() at client gets error For this to work you need to pass 1 as backlog parameter to listen() so that no incoming sockets are put in queue by listen(), waiting to get accepted by accept() call But behaviour of backlog is subject to implementation on underlying platform as per this post listen() ignores the backlog argument?

Upvotes: 1

Ra&#39;Jiska
Ra&#39;Jiska

Reputation: 1049

This is because here you are checking if the connection was initially correctly initiated, not if the nb of players was exceeded.

In order to accomplish what you like, you may want to to create special message concerning data usable by your program.

E.g: Upon connection, if the number of players is exceeded, server sends a message to client telling him his request was refused, that before closing the socket server-side. Upon reception of this message, client would display a nice error to the user before closing its own socket.

This way you would have a proper connection refusal and socket closing on both ends.

Upvotes: 0

Related Questions