简体   繁体   中英

How to connect clients two by two as they connect

I'm facing a problem with sockets programming in C. I've coded a server where multiple clients can connect and send messages in a chatroom but i don't know how to connect two clients two by two.

example: Client A connect to the Server but must wait in a queue waiting for Client B to connect. Client C connect but must wait in a queue waiting for Client D to connect.

Every pair of clients must be in their specific chatroom, do I need to share the fd between the two sockets of the 2 clients or link between the two sockets.

I thought about using another way by using the function accept() twice before I fork() but I have to use child processes for that.

Here is my server code:

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

#define PORT "8888"   


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)
{
    fd_set master;    
    fd_set read_fds;  
    int fdmax;        

    int listener;     
    int newfd;        
    struct sockaddr_storage remoteaddr; 
    socklen_t addrlen;

    char buf[256];    
    int nbytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1;        
    int i, j, rv;

    struct addrinfo hints, *ai, *p;

    FD_ZERO(&master);    
    FD_ZERO(&read_fds);  


    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, "Server msg: %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;
        }

        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
            close(listener);
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "Server msg: bind failed\n");
        exit(2);
    }

    freeaddrinfo(ai); 

    puts("Bind success");

    if (listen(listener, 10) == -1)
    {
        perror("listen");
        exit(3);
    }
    puts("Server listening ...");


    FD_SET(listener, &master);


    fdmax = listener;


    for(;;)
    {
        read_fds = master;
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
        {
            perror("select");
            exit(4);
        }


        for(i = 0; i <= fdmax; i++) 
        {
            if (FD_ISSET(i, &read_fds))
            {   
                if (i == listener)
                {

                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener,
                        (struct sockaddr *)&remoteaddr,
                        &addrlen);

                    if (newfd == -1)
                    {
                        perror("accept");
                    }
                    else
                    {
                        FD_SET(newfd, &master); 
                        if (newfd > fdmax)
                        {   
                            fdmax = newfd;
                        }
                        printf("Server msg: new connection from %s on "
                               "socket %d\n", inet_ntop(remoteaddr.ss_family,
                               get_in_addr((struct sockaddr*)&remoteaddr),
                               remoteIP, INET6_ADDRSTRLEN), newfd);
                    }
                } 
                else
                {

                    if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
                    {  
                        if (nbytes == 0)
                        {   
                            printf("Server msg: socket %d lost\n", i);
                        }
                        else
                        {
                            perror("recv");
                        }
                        close(i); 
                        FD_CLR(i, &master); 
                    }
                    else
                    {

                        for(j = 0; j <= fdmax; j++)
                        {

                            if (FD_ISSET(j, &master)) 
                            {

                                if (j != listener && j != i)
                                {
                                    if (send(j, buf, nbytes, 0) == -1)
                                    {
                                        perror("send");
                                    }
                                }
                            }
                        }
                    }
                }
            } 
        } 
    } 

    return 0;
}

And here is my client code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <arpa/inet.h>

#define PORT "8888" 

#define MAXDATASIZE 100 
#define MAXNAMESIZE 25 

void *receive_handler(void *);

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(int argc, char *argv[])
{
    char message[MAXDATASIZE];
    char nickName[MAXNAMESIZE]; 
    int sockfd;                 
    char sBuf[MAXDATASIZE]; 
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"Usage: ./client address\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }


    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("Client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);   
            perror("Client: connect");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "Client: connection failed\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
            s, sizeof s);
    printf("Client: connection to %s\n", s);

    freeaddrinfo(servinfo); 

    puts("Give your username:");
    memset(&nickName, sizeof(nickName), 0);
    memset(&message, sizeof(message), 0); 
    fgets(nickName, MAXNAMESIZE, stdin);  
    pthread_t recv_thread;

    if( pthread_create(&recv_thread, NULL, receive_handler, (void*)(intptr_t) sockfd) < 0)
    {   
        perror("Failed on thread creation");
        return 1;
    }    
    puts("Connection established");

    puts("Welcome!\n");
    puts("[Type '/quit' to quit the chatroom]");

    for(;;)
    {
        char temp[6];
        memset(&temp, sizeof(temp), 0);

        memset(&sBuf, sizeof(sBuf), 0); 
        fgets(sBuf, 100, stdin); 

        if(sBuf[0] == '/' &&
           sBuf[1] == 'q' &&
           sBuf[2] == 'u' &&
           sBuf[3] == 'i' &&
           sBuf[4] == 't')
            return 1;


        int count = 0;
        while(count < strlen(nickName))
        {
            message[count] = nickName[count];
            count++;
        }
        count--;
        message[count] = ':';
        count++;

        for(int i = 0; i < strlen(sBuf); i++)
        {
            message[count] = sBuf[i];
            count++;
        }
        message[count] = '\0';
        if(send(sockfd, message, strlen(message), 0) < 0)
        {
            puts("Sent failed");
            return 1;
        }
        memset(&sBuf, sizeof(sBuf), 0);

    }

    pthread_join(recv_thread , NULL);
    close(sockfd);

    return 0;
}

void *receive_handler(void *sock_fd)
{
    int sFd = (intptr_t) sock_fd;
    char buffer[MAXDATASIZE];
    int nBytes;

    for(;;)
    {
        if ((nBytes = recv(sFd, buffer, MAXDATASIZE-1, 0)) == -1)
        {
            perror("recv");
            exit(1);
        }
        else
            buffer[nBytes] = '\0';
        printf("%s", buffer);
    }
}

Yes, in my opinion you can use two accept and then call a fork() or, even better, a new thread . You can create a new process/thread for each pair of connections.

What do you mean sharing the fd? You mean sending a fd of one client to another? In this case NO You can keep, in the thread function, a mapping between the two clients to know which clients are paired. When receiving a message from one client, thanks to the mapping, you know which client you are supposed to send the message to.

I think the rest depends on further details you should specify in your question. Let me know

If each client can both send and receive you might want to use multithreading on client-side too: one to send and one to receive.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM