Help with sockets in C

if i have a server which wants to connect to exactly 5 clients, does that mean i need 5 socket file descriptors and use
listen(socket_fd,1);
for each one

or just do

listen(socket_fd,5)

also whats the second parameter number mean? what happens if i put 0 there?

also if i am connected to five clients, and i have listen(socket_fd,5), does that mean while one of the 5 is being handled, it has support for the other 4 to continue waiting?, or I am not sure what its doing here, can I even put a 0 there, if I have 5 clients connected, will that work?

For your requirement to connect your server to exactly 5 clients,
listen(socket_fd,5) is the right option.

>>also whats the second parameter number mean? what happens if i put 0 there?

The second parameter is the limit on the maximum number of incoming connection requests to your server socket that can remian pending waiting to be accepted.

You could start different threads for each accepted connection and process each separately. In the main thread you could poll the listen socket for an incoming connection request and accept the same.

I dont know the answer to what happens if you put a 0 for teh second parameter, but my guess is if a second connection request arrives when you already have one in the listen queue and it has not been accepted yet by your application, then this would immediately be dropped.

when u say to have
listen(socket_fd,5)
do u mean like
listen(socket_fd1,5)
listen(socket_fd2,5)
listen(socket_fd3,5)
listen(socket_fd4,5)
listen(socket_fd5,5)

or just one?

You need just one. Your socket_fd is usually not the socket with which the client and server communicate. It is rather just the point where the client connects to first and then waits for the server to open another socket on which the real data exchange takes place.
So if you have

listen(socket_fd, 5);

This means that 5 connetions are allowed to be in the queue and wait to be served. If all of these slots are taken, they will be blocked.
You can then accept the connections in this queue with the accept() call and this function will return a new file descripter which marks the direct connetion to the client.

i have this other problem
when i run my server, it works fine, and i get the connection from the client, but right after if i test it again
i get
ERROR on binding: Address already in use
but if I change the port, and compile, then change the port to what it was before, and compile, then run it , then it works again, then the whole thing happens again

does anyone know why this could be happening?

---------- Post updated at 01:51 PM ---------- Previous update was at 12:36 PM ----------

nvm i fixed it

---------- Post updated at 01:52 PM ---------- Previous update was at 01:51 PM ----------

but to keep a server and client running for more than one iteration, i dont understand how to do that

where do you put the while(1 ) {} block
what goes in the block?

Anything you want to run repeatedly.

i mean like
in the while loop do i need to redefine all the sockets declarations?

---------- Post updated at 02:19 PM ---------- Previous update was at 02:18 PM ----------

say i want exactly 5 connections
does the
listen(soc,5);
go in the while loop?

No. If you put the declarations outside, you can keep and reuse them.

[quote]

No, you only have to do listen() once. The thing which actually gives you new connections, accept(), goes into the loop.

So this is my function (server)

int main(int argc, char **argv) {
    
    // Set Up
    int socket_fd, new_socket_fd, num; // file descriptors, and port number
    socklen_t client_length; // size of client address 
    struct sockaddr_in serv_addr, cli_addr;
    char buffer[MAXDATASIZE]; // input gets put here

    // Set Up Socket
    for(num=0;num<CONSIGNMENT;num++) client_names[num]=NULL;   // set all to Null 
    bzero(buffer,MAXDATASIZE); // Set all to zero
    socket_fd = socket(AF_INET, SOCK_STREAM, 0); // create a listening socket to the internet
    if (socket_fd < 0) error("ERROR opening socket"); // check the socket
    bzero((char *) &serv_addr, sizeof(serv_addr)); // sets all values in the serv_addr to 0
    serv_addr.sin_family = AF_INET; // set up the information for the server
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORT);
    if (bind(socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("ERROR on binding"); // check the bind
    listen(socket_fd,5); // sets it up for listening, max of 5 waiting connections while one is being handled             
    client_length = sizeof(cli_addr);
    
    while(1) {
        new_socket_fd = accept(socket_fd, (struct sockaddr *) &cli_addr, &client_length); // pause process until client connects
        if (new_socket_fd < 0) error("ERROR on accept"); // check new file descriptor

     // server and client talks....


        close(new_socket_fd); 
    }
    
    close(socket_fd); // close connections with client
    return(0); 
}

is the while loop supposed to be like this, the server should be able to connect with any number of clients, and any of the clients should be about to talk to the server at any time...

That will work but the server will only be able to talk to one client at a time. If you want to handle several simultaneous connections, you'll need to fork() like this:

int maxproc=5; // Max # of simultaneous connections

{
        int newfd=accept(sock);

        // Handle children without blocking
        while(waitpid(-1, NULL, WNOHANG) >= 0)
        {
                maxproc++;
        }

        // If you've exceeded the maximum, actually wait
        if(maxproc <= 0) waitpid(-1, NULL, 0);

        switch(fork())
        {
        case -1: // Error
                perror("Couldn't fork");
                close(newfd);
                break;
        default: // Parent
                maxproc--;
                close(newfd);
                break;
        case 0: // child
                do_stuff();
                exit(0);
                break;        
        }
}

nvm i fixed it.

---------- Post updated at 09:09 PM ---------- Previous update was at 07:46 PM ----------

how does the select function code work? will it achieve a better result than what you proposed?

also

when running my program, i notice that it works usually, but sometimes the output comes out incomplete even when i haven't changed my code...

i think this may have to do with my code for reading and writing from the sockets

this is what i have, are there any errors?

void send_message(int *client_socket_fd, char *message, int length) {
    if (write(*client_socket_fd, message, length) < 0) error("ERROR writing to socket");
}

void read_message(int *socket_fd, char *buffer, int display) {
    size_t bytes; 
    if((bytes = read(*socket_fd, buffer, MAXDATASIZE)) < 0) error("ERROR reading from socket");    
    if (display) write(1,buffer,bytes);    
}

display is either 0 or 1 depending on if i want it to be printed to stdout
so after reading from the socket, does the all the data form the socket get removed, or is it better if i manually remove it, maybe if i do that it will fix my problem?

It essentially keeps a list of file descriptors and lets you know when any of them are ready. Whether it's "better" depends on what you want to do.

How should I know? Post your complete program!

server

#include "header.h"
int main(int argc, char **argv) {
    char buffer[MAXDATASIZE]; 
    bzero(buffer, sizeof(buffer)); 
    client *client_list=NULL; item *item_list=NULL;
    item_list = add_item("bike", "8", "70", "5", "False", item_list);
    int socket_fd, client_socket_fd; 
    socklen_t client_length; 
    struct sockaddr_in serv_addr, cli_addr;
    client_length = sizeof(cli_addr);
    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd < 0) error("ERROR opening socket");
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORT);
    if (bind(socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("ERROR on binding"); 
    listen(socket_fd,5);          
    client_socket_fd = accept(socket_fd, (struct sockaddr *) &cli_addr, &client_length);
    if (client_socket_fd < 0) error("ERROR on accept"); 
    read_message(client_socket_fd, buffer, 0); 
    if (already_there(client_list, buffer)==0) {      
        client_list = add_client(buffer, client_list, client_socket_fd); 
        send_message(client_socket_fd, "Enter", 5); 
        write(1, buffer, str_len(buffer)); write(1, " connected!\n", 12); 
        read_message(client_socket_fd, buffer, 0); 
        send_items(item_list, client_socket_fd); 
    }
    else {                                       
        send_message(client_socket_fd, "Leave", 5); 
        write(1, "Rejected incoming client - ", 27); write(1, buffer, str_len(buffer)); write(1, "! (User with same name already connected)\n", 42); 
        read_message(client_socket_fd, buffer, 0); 
    }
    close(client_socket_fd); close(socket_fd); 
    return(0); 
}

In this function, this line:
write(1, buffer, str_len(buffer)); write(1, " connected!\n", 12);
is making the client not read everything from the socket
right now, that line is in the right spot to avoid this problem, but if i move that line one line up or 2 lines up, then the client only reads some of the output.

That can't possibly be your complete code. str_len, add_client, send_message, and read_message aren't defined anywhere.

If you don't know what's going wrong, you don't know why it's going wrong, and don't know what I need to see. Chopping bits out of your code just wastes your time and ours.

I won't ask again. Post your complete program!


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

#ifndef PORT
#define PORT 30758  
#endif

#define MAXDATASIZE 1000 
#define CONSIGNMENT 8

typedef struct client_list {
    char *client_name;
    int client_socket_fd;
    struct client_list *next;
} client;

typedef struct item_list {
    char *name;
    char *number;
    char *current_price;
    char *minimum_price;
    char *num_of_bids_left;
    char *is_closed;
    struct item_list *next;
} item;

void error(const char *error_message) {
    perror(error_message);
    exit(1);
}

int str_len(char *string) {
    int count = 0;
    while(*(string+count)!= '\n' && *(string+count)!= '\0') count++;
    return count;
}

void send_message(int client_socket_fd, char *message, int length) {
    if (write(client_socket_fd, message, length) < 0) error("ERROR writing to socket");
}

void read_message(int socket_fd, char *buffer, int display) {
    size_t bytes; bzero(buffer,MAXDATASIZE);
    if((bytes = read(socket_fd, buffer, MAXDATASIZE)) < 0) error("ERROR reading from socket");    
    if (display) write(1,buffer,bytes);
}

void send_items(item *item_list, int client_socket_fd) {
    send_message(client_socket_fd, "Items available are:\n", 21);
    while(item_list) {
        send_message(client_socket_fd, item_list->number, str_len(item_list->number));
        send_message(client_socket_fd, ".\nItem Name: ", 13);
        send_message(client_socket_fd, item_list->name, str_len(item_list->name));
        send_message(client_socket_fd, "\nCurrent Price: $", 17);
        send_message(client_socket_fd, item_list->current_price, str_len(item_list->current_price));
        send_message(client_socket_fd, "\nNumber Of Bids Left: ", 22);
        send_message(client_socket_fd, item_list->num_of_bids_left, str_len(item_list->num_of_bids_left));
        send_message(client_socket_fd, "\nIs Closed: ", 12);
        send_message(client_socket_fd, item_list->is_closed, str_len(item_list->is_closed));
        send_message(client_socket_fd, "\n", 1);
        item_list = item_list->next;
    }
}

int already_there(client *current, char *username) {
    while(current) {
        if (strcmp(current->client_name, username)==0) return(1);
        current = current->next;
    }
    return(0);
}

client *add_client(char *username, client *client_list, int client_socket_fd) {
    client *new_client = (client *)malloc(sizeof(client)); 
    new_client->client_name = username;
    new_client->client_socket_fd = client_socket_fd;
    new_client->next = client_list;
    return new_client;
}

item *add_item(char *name, char *number, char *current_price, char *num_of_bids_left, char *is_closed, item *item_list) {
    item *new_item = (item *)malloc(sizeof(item)); 
    new_item->name=name;
    new_item->number=number;
    new_item->current_price=current_price;
    new_item->num_of_bids_left=num_of_bids_left;
    new_item->is_closed=is_closed;
    new_item->next=item_list;
    return new_item;
}

---------- Post updated at 01:29 PM ---------- Previous update was at 01:25 PM ----------

client

int main(int argc, char *argv[]) {
    int socket_fd;
    struct sockaddr_in server_address;
    struct hostent *server;
    char *username, buffer[MAXDATASIZE];
    if (argc == 3) username = getenv("USER");
    else if (argc == 4) username = argv[3];
    else error("Error, invalid number of arguments, need a hostname, port, and a user name (optional).\n"); 
    bzero(buffer,MAXDATASIZE); 
    socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
    if (socket_fd < 0) error("Error opening socket");
    server = gethostbyname(argv[1]); 
    if (server == NULL) error("Error, no such host\n");
    bzero((char *) &server_address, sizeof(server_address)); 
    server_address.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&server_address.sin_addr.s_addr, server->h_length); 
    server_address.sin_port = htons(PORT);
    if (connect(socket_fd,(struct sockaddr *) &server_address,sizeof(server_address)) < 0) error("Error connecting"); 
    send_message(socket_fd, username, str_len(username)); 
    read_message(socket_fd, buffer, 0); 
    if (strcmp(buffer, "Enter")==0) {
        write(1,username,str_len(username)); write(1," successfully connected to ",27); 
        write(1,argv[1],str_len(argv[1])); write(1,"!\n",2);
        send_message(socket_fd, "ok", 2); 
        read_message(socket_fd, buffer, 1); 
    }
    else { 
        write(1,"User ",5); write(1,username,str_len(username)); 
        write(1," already connected to ",22); write(1,argv[1],str_len(argv[1])); write(1,"!\n",2);
        send_message(socket_fd, "ok", 2); 
    }
    close(socket_fd); 
    return 0;
}

---------- Post updated at 01:29 PM ---------- Previous update was at 01:29 PM ----------

so far i only added support for one client at a time

Now that I can see your sending and receiving functions I can rule out some problems.

I don't see why the position of that line would change what the client receives. Could it be that the server's actually only receiving a partial response from the client instead -- the client sends it all but the server doesn't get it?

You can't count on getting data in the same chunks someone sent it as. You might have to check if you got a complete line, and if you didn't, read more.

but how?
if you read it waits until more data comes in, so if you put a while loop, it will just go into an infinite loop?
so really if you read once, doesnt it read everything from the socket?, i tried before putting a while loop around it, but then it just froze, since i assume it had already finished reading everything...

Have the loop break when a line's complete. Read one character at a time. This means send_message will need to write a \n after every message so the receiver can tell where it ends.

int pos=0;
bzero(buffer,MAXDATASIZE);

// Stop once we have an entire single line
while((buffer[pos] != '\n') && (pos < MAXDATASIZE))
{
        // Put new data 'pos' bytes ahead, so we don't overwrite the old
        ssize_t b=read(socket_fd, buffer+pos, 1); 
        if(b > 0) pos+=b;
        else break; // b<=0 means socket closed or socket error, so give up.
}

The receiving end isn't told how much the sending end sent. The socket doesn't necessarily send all the data at once, either. Plus there's travel time too. It's easily possible to read less than the sender wrote.

I see you've posted the client code now, I'll give it a quick look over.

your code is only reading one line (as in stops at a \n), but my data that's being sent, can include more than 0 \n's, could that be causing the problem?
but then again, sometimes the problem happens...

maybe somewhere (not at the end) in the data i send, there is a \0...?

I don't see anything obviously wrong with the client code except the problems already mentioned.