0

I am currently trying to create a multiple client-server which enables server to perform both read and write functions in C language. I am able to read the data from the client using readfds and putting it as parameter in SELECT. When I added in writefds, the client fails to connect to the server. I am not sure what is the issue behind it, whether this is the correct method to transmit and receive data

This is the code for my server

#include <stdio.h>
#include <string.h>   //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>   //close
#include <arpa/inet.h>    //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros

#define TRUE   1
#define FALSE  0
#define PORT 8080

int main(int argc , char *argv[])
{
    int opt = TRUE;
    int master_socket , addrlen , new_socket , client_socket[30] ,
        max_clients = 2 , activity, i , valread , sd;
    int max_sd;
    struct sockaddr_in address;

    char buffer[1025];  //data buffer of 1K

    fd_set readfds;
    fd_set writefds;


    char *message = "Welcome to Server\r\n";

    //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);
    }

    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 = INADDR_ANY;
    address.sin_port = htons( PORT );

    //bind the socket to localhost port 8888
    if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    printf("Listener on port %d \n", PORT);

    if (listen(master_socket, 3) < 0)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }


    addrlen = sizeof(address);
    puts("Waiting for connections ...");

    for(;;)
    {

        //clear the socket set
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);

        //add master socket to set
        FD_SET(master_socket, &readfds);
        FD_SET(master_socket, &writefds);


        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 , &writefds, NULL , NULL);

        if ((activity < 0) && (errno!=EINTR))
        {
            printf("select error");
        }


        if (FD_ISSET(master_socket, &readfds))
        {
            if ((new_socket = accept(master_socket,
                (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
            {
                perror("accept");
                exit(EXIT_FAILURE);
            }

            else {

                printf("Connected to socket \n");

            }
            bzero(buffer, 1025);

            int n=0;

            read(new_socket, buffer,       sizeof(buffer));

            printf("From client: %s\t To client: ", buffer);

            bzero(buffer, 1025);

            //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("Adding to list of sockets as %d\n" , i+1);
                }
            }
        }

        else if(FD_ISSET(master_socket, &readfds)) {

            printf("From client: %s\t To client : ", buffer);
            bzero(buffer, 1025);

            // copy server message in the buffer
            while ((buffer[n++] = getchar()) != '\n')
                                        ;

            write(new_socket, buffer, sizeof(buffer));

        }


        else{
            //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 also read the
                    //incoming message
                    if ((valread = read( sd , buffer, 1024)) == 0)
                    {

                        close( sd );
                        client_socket[i] = 0;
                    }

                    //Echo back the message that came in
                    else
                    {


                    }
                }
            }
        }//else

    }

    return 0;
}

This is the code for the client

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

#define MAX 1000
#define PORT 8080
#define SA struct sockaddr

void func(int sockfd)
{
    char buff[MAX];
    int n;
    int firstConnect = 1;

    for (;;)
    {

        bzero(buff, sizeof(buff));
        n = 0;

        if(firstConnect==1) {
            printf("Enter the string : ");
            firstConnect = 0;

            while ((buff[n++] = getchar()) != '\n')
                ;
            write(sockfd, buff, sizeof(buff));

        }


        bzero(buff, sizeof(buff));

        read(sockfd, buff, sizeof(buff));

        printf("From Server: %s\t To Server : ", buff);


        while ((buff[n++] = getchar()) != '\n');
        write(sockfd, buff, sizeof(buff));

        if ((strncmp(buff, "exit", 4)) == 0)
        {
            printf("Client Exit...\n");
            break;
        }
    }
}


int main()
{

    printf("CLIENT");
    int sockfd, connfd;
    struct sockaddr_in servaddr, cli;

    // socket create and varification
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        printf("socket creation failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully created..\n");

    bzero(&servaddr, sizeof(servaddr));

    // assign IP, PORT
    servaddr.sin_family = AF_INET;

    // servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_addr.s_addr = inet_addr("127.8.1.0");

    servaddr.sin_port = htons(PORT);

    // connect the client socket to server socket
    if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
    {
        printf("connection with the server failed...\n");
        exit(0);
    }
    else
        printf("connected to the server..\n");

    // function for chat
    func(sockfd);

    // close the socket
    close(sockfd);
}
Bodo
  • 9,287
  • 1
  • 13
  • 29
Tragend
  • 17
  • 1
  • 3
  • You don't have to add `master_socket` to `writefds` because you only use it for `accept()`. You may want to add the client sockets to `writefds` because you probably will use it for `read` and `write`. I noticed that you use `if (FD_ISSET(master_socket, &readfds))` twice. This is probably not what you want. After `new_socket = accept(...)` you call `read(new_socket, ...)` If this blocks because the client might not have sent any data yet, it will prevent your server from accepting the next connection. – Bodo Jan 16 '19 at 08:32
  • Thanks. I added client socket to writefds and it works. Another issue I have is the server stops sending/receiving after the client sent data to the server or if it is connected to a second client. I want the server to be able to send and receive messages continuously, broadcast to all clients and also able to customize messages to individual client. What do I do to make this happen? – Tragend Jan 16 '19 at 10:13
  • Please update the code in your question to show your modification. Your server code can block in `getchar()`. You would have to use non-blocking input as well. See https://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input . You would need to add the input file desriptor to `readfds` together with the sockets and handle the input parallel to the socket communication until you decide that it's time to send it. You would have to handle partial `read`s and `write`s as well. Using `fork` to use a separate process per connection might be easier. – Bodo Jan 16 '19 at 12:31

1 Answers1

1

As side node, it appears that you expect read to zero-terminate received data. That is not the case. The length of received data is in the return value of read, it must not be ignored.

It also appears that you expect write to find the end of the string you are sending. That doesn't happen. You must specify the exact length of the data you are sending, rather than the length of the buffer that contains the data.

send/write and recv/read functions work on binary data rather than zero-terminated strings and can send/receive less than the specified size or may return an error. Learn how to use these functions correctly by not ignoring the return value and handling errors and partial reads and writes.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271